Sidebar.purs 4.9 KB

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