Sidebar.purs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. module PureTabs.Sidebar where
  2. import Browser.Runtime as Runtime
  3. import Browser.Tabs (Tab(..), TabId(..), WindowId)
  4. import Browser.Tabs.OnUpdated (ChangeInfo(..))
  5. import Browser.Windows (getCurrent)
  6. import Control.Alternative (pure)
  7. import Control.Bind ((=<<), (>>=))
  8. import Data.Foldable (traverse_)
  9. import Data.Maybe (Maybe(..), maybe)
  10. import Data.Monoid ((<>))
  11. import Data.Number (fromString)
  12. import Data.Show (show)
  13. import Data.Unit (unit)
  14. import Debug.Trace (traceM)
  15. import Effect (Effect)
  16. import Effect.Aff (Aff, launchAff_)
  17. import Effect.Class (liftEffect)
  18. import Effect.Console (log)
  19. import Effect.Exception (throw)
  20. import JQuery as J
  21. import JQuery.Ext (after, prepend, getHtmlElem) as J
  22. import Prelude (Unit, bind, ($), discard)
  23. import PureTabs.Model (BackgroundEvent(..), SidebarEvent(..))
  24. import Sortable (create, Event) as S
  25. import Web.DOM.Element (id)
  26. import Web.HTML.HTMLElement (toElement)
  27. main :: Effect Unit
  28. main = do
  29. log "started sidebar"
  30. port <- Runtime.connect
  31. launchAff_ $ runSidebar port
  32. where
  33. runSidebar :: Runtime.Port -> Aff Unit
  34. runSidebar port = do
  35. currentWindow <- getCurrent
  36. liftEffect $ initSidebar port currentWindow.id
  37. sortableOnUpdate :: Runtime.Port -> S.Event -> Effect Unit
  38. sortableOnUpdate port { item: item, newIndex: Just newIndex } = do
  39. sTabId <- id $ toElement item
  40. case fromString sTabId of
  41. Nothing -> throw $ "couldn't convert to a tab id " <> sTabId
  42. Just tabId' -> Runtime.postMessageJson port $ SbTabMoved (TabId tabId') newIndex
  43. sortableOnUpdate port _ = pure unit
  44. initSidebar :: Runtime.Port -> WindowId -> Effect Unit
  45. initSidebar port winId = do
  46. log $ "windowId " <> (show winId)
  47. Runtime.postMessageJson port $ SbHasWindowId winId
  48. _ <- Runtime.onMessageJsonAddListener port onMsg
  49. allTabs <- J.getHtmlElem =<< J.select "#tabs"
  50. sortable <- S.create { onUpdate: sortableOnUpdate port } allTabs
  51. pure unit
  52. where
  53. onMsg :: BackgroundEvent -> Effect Unit
  54. onMsg event = case event of
  55. BgTabCreated tab -> createTab port tab
  56. BgTabDeleted tabId -> deleteTabElement tabId
  57. BgInitialTabList tabs -> traverse_ (createTab port) tabs
  58. BgTabUpdated tid cinfo tab -> updateTabInfo tid cinfo tab
  59. BgTabActived prev new -> activateTab prev new
  60. BgTabMoved tid prevPos newPos -> moveTab tid prevPos newPos
  61. _ -> log "received unsupported message type"
  62. createTab :: Runtime.Port -> Tab -> Effect Unit
  63. createTab port (Tab tab) = do
  64. tabsDiv <- J.select "#tabs"
  65. tabElem <- createTabElement port (Tab tab)
  66. insertTabAt tab.index tabElem
  67. createTabElement :: Runtime.Port -> Tab -> Effect J.JQuery
  68. createTabElement port (Tab tab) = do
  69. tabDiv <- J.create "<div>"
  70. J.setAttr "class" "tab" tabDiv
  71. J.setAttr "id" tab.id tabDiv
  72. J.on "click" onTabClick tabDiv
  73. if tab.active then (J.addClass "active" tabDiv) else (pure unit)
  74. if isDiscarded tab then (J.addClass "discarded" tabDiv) else (pure unit)
  75. -- favicon
  76. faviconDiv <- J.create "<div>"
  77. J.addClass "tab-favicon" faviconDiv
  78. setFaviconUrl tab.favIconUrl faviconDiv
  79. J.append faviconDiv tabDiv
  80. -- title
  81. tabTitle <- J.create "<div>"
  82. J.addClass "tab-title" tabTitle
  83. J.setText tab.title tabTitle
  84. J.append tabTitle tabDiv
  85. -- close button
  86. closeButton <- createCloseButton
  87. J.append closeButton tabDiv
  88. J.on "click" onCloseClick closeButton
  89. pure tabDiv
  90. where
  91. onCloseClick :: J.JQueryEvent -> J.JQuery -> Effect Unit
  92. onCloseClick event j = Runtime.postMessageJson port $ SbTabDeleted tab.id
  93. onTabClick :: J.JQueryEvent -> J.JQuery -> Effect Unit
  94. onTabClick event j = Runtime.postMessageJson port $ SbTabActived tab.id
  95. isDiscarded :: forall r. { discarded :: Maybe Boolean | r } -> Boolean
  96. isDiscarded { discarded: Just true } = true
  97. isDiscarded _ = false
  98. createCloseButton :: Effect J.JQuery
  99. createCloseButton = do
  100. parent <- J.create "<div>"
  101. J.addClass "close-button-parent" parent
  102. outer <- J.create "<div>"
  103. J.addClass "close-button-outer" outer
  104. J.append outer parent
  105. inner <- J.create "<div>"
  106. J.addClass "close-button-inner" inner
  107. J.append inner outer
  108. pure parent
  109. setFaviconUrl :: Maybe String -> J.JQuery -> Effect Unit
  110. setFaviconUrl Nothing div = pure unit
  111. setFaviconUrl (Just favData) div = J.css { "background-image": favUrl } div
  112. where
  113. favUrl = "url(" <> favData <> ")"
  114. deleteTabElement :: TabId -> Effect Unit
  115. deleteTabElement tabId = do
  116. div <- J.select ("#" <> show tabId)
  117. J.remove div
  118. updateTabInfo :: TabId -> ChangeInfo -> Tab -> Effect Unit
  119. updateTabInfo tid (ChangeInfo cinfo) (Tab tab) = do
  120. let
  121. tabIdSelec = "#" <> (show tid)
  122. tabDiv <- J.select tabIdSelec
  123. tabTitleDiv <- J.select (tabIdSelec <> " > .tab-title")
  124. let
  125. newTitle = case cinfo.status of
  126. Just "loading" -> Just "Loading ..."
  127. _ -> Just tab.title
  128. maybe (pure unit) (\t -> J.setText t tabTitleDiv) newTitle
  129. maybe (pure unit) (\discarded -> J.setClass "discarded" discarded tabDiv) tab.discarded
  130. tabFaviconDiv <- J.select ("#" <> (show tid) <> " > .tab-favicon")
  131. setFaviconUrl cinfo.favIconUrl tabFaviconDiv
  132. activateTab :: (Maybe TabId) -> TabId -> Effect Unit
  133. activateTab prev new = do
  134. maybe (pure unit) (\p -> (J.select ("#" <> (show p))) >>= J.setClass "active" false) prev
  135. newTab <- J.select ("#" <> (show new))
  136. J.setClass "active" true newTab
  137. moveTab :: TabId -> Int -> Int -> Effect Unit
  138. moveTab tabId prev new = do
  139. tabDiv <- J.select $ "#" <> show tabId
  140. J.remove tabDiv
  141. insertTabAt new tabDiv
  142. insertTabAt :: Int -> J.JQuery -> Effect Unit
  143. insertTabAt 0 tabDiv = do
  144. allTabs <- J.select "#tabs"
  145. J.prepend tabDiv allTabs
  146. insertTabAt pos tabDiv = do
  147. child <- J.select $ "#tabs > .tab:nth-child(" <> (show pos) <> ")"
  148. J.after tabDiv child