From 979c19acd4f10fd8b991ff38288e81c3dffa8db1 Mon Sep 17 00:00:00 2001
From: Jens Nolte <jens@nightmarestudio.de>
Date: Wed, 27 Nov 2019 17:21:48 +0100
Subject: [PATCH] Create new types for different block update modes

---
 src/QBar/Blocks.hs | 17 ++++++++++++-----
 src/QBar/Core.hs   | 43 +++++++++++++++++++++++++++++++++++++++++++
 src/QBar/Server.hs |  2 +-
 3 files changed, 56 insertions(+), 6 deletions(-)

diff --git a/src/QBar/Blocks.hs b/src/QBar/Blocks.hs
index 9737683..dc9a627 100644
--- a/src/QBar/Blocks.hs
+++ b/src/QBar/Blocks.hs
@@ -11,18 +11,25 @@ import Data.Time.LocalTime
 import Pipes
 import Pipes.Concurrent
 
-dateBlock :: IO BlockOutput
-dateBlock = do
+dateBlock :: Block
+dateBlock = pushBlock producer
+  where
+    producer = do
+      yield =<< lift dateBlockOutput
+      lift $ sleepUntil =<< nextMinute
+      producer
+
+dateBlockOutput :: IO BlockOutput
+dateBlockOutput = do
   zonedTime <- getZonedTime
   let date = T.pack (formatTime defaultTimeLocale "%a %F" zonedTime)
   let time = T.pack (formatTime defaultTimeLocale "%R" zonedTime)
-  --let text = (T.pack "📅 ") <> T.pack (formatTime defaultTimeLocale "%a %F <span color='#ffffff'>%R</span>" zonedTime)
   let text = (T.pack "📅 ") <> date <> " " <> (coloredText activeColor time)
   return $ setBlockName "date" $ pangoMarkup $ createBlock text
 
 dateBlockProducer :: BarUpdateChannel -> BlockProducer
 dateBlockProducer barUpdateChannel = do
-  initialDateBlock <- lift dateBlock
+  initialDateBlock <- lift dateBlockOutput
   (output, input) <- lift $ spawn $ latest initialDateBlock
   lift $ void $ forkIO $ update output
   fromInput input
@@ -30,7 +37,7 @@ dateBlockProducer barUpdateChannel = do
     update :: Output BlockOutput -> IO ()
     update output = do
       sleepUntil =<< nextMinute
-      block <- dateBlock
+      block <- dateBlockOutput
       void $ atomically $ send output block
       updateBar barUpdateChannel
       update output
\ No newline at end of file
diff --git a/src/QBar/Core.hs b/src/QBar/Core.hs
index 713bb03..691ae37 100644
--- a/src/QBar/Core.hs
+++ b/src/QBar/Core.hs
@@ -44,9 +44,31 @@ $(deriveJSON defaultOptions ''Click)
 
 type BlockProducer = Producer BlockOutput IO ()
 
+-- |Block that 'yield's an update whenever the block should be changed
+newtype PushBlockProducer = PushBlockProducer BlockProducer
+-- |Block that generates an update on 'yield'. Should only be pulled when an update is required.
+newtype PullBlockProducer = PullBlockProducer BlockProducer
+-- |Cached block. Always 'yield's the latest update, so it should only be pulled when the bar is rendered.
+newtype CachedBlockProducer = CachedBlockProducer BlockProducer
+
+-- |Generic block type that can be a block in pull-, push- or cached mode.
+data Block = PushBlock PushBlockProducer
+  | PullBlock PullBlockProducer
+  | CachedBlock CachedBlockProducer
+
 data BarUpdateChannel = BarUpdateChannel (IO ())
 type BarUpdateEvent = Event.Event
 
+pushBlock :: BlockProducer -> Block
+pushBlock = PushBlock . PushBlockProducer
+
+pullBlock :: BlockProducer -> Block
+pullBlock = PullBlock . PullBlockProducer
+
+cachedBlock :: BlockProducer -> Block
+cachedBlock = CachedBlock . CachedBlockProducer
+
+
 defaultColor :: T.Text
 defaultColor = "#969896"
 
@@ -263,3 +285,24 @@ pangoColor (RGB r g b) =
 
 updateBar :: BarUpdateChannel -> IO ()
 updateBar (BarUpdateChannel updateAction) = updateAction
+
+cachePushBlock :: BarUpdateChannel -> PushBlockProducer -> CachedBlockProducer
+cachePushBlock barUpdateChannel (PushBlockProducer blockProducer) = CachedBlockProducer $
+  lift (next blockProducer) >>= either (lift . return) withInitialBlock
+  where
+    withInitialBlock :: (BlockOutput, BlockProducer) -> BlockProducer
+    withInitialBlock (initialBlockOutput, blockProducer') = do
+      (output, input, seal) <- lift $ spawn' $ latest initialBlockOutput
+      -- The async could be used to stop the block later, but for now we are just linking it to catch exceptions
+      lift $ link =<< async (sendProducerToMailbox output seal blockProducer')
+      fromInput input
+    sendProducerToMailbox :: Output BlockOutput -> STM () -> BlockProducer -> IO ()
+    sendProducerToMailbox output seal blockProducer' = do
+      runEffect $ for blockProducer' (sendOutputToMailbox output)
+      atomically seal
+    sendOutputToMailbox :: Output BlockOutput -> BlockOutput -> Effect IO ()
+    sendOutputToMailbox output blockOutput = lift $ do
+      -- The void discarding the boolean result that indicates if the mailbox is sealed
+      -- This is ok because right now once started a cached block never stops generating output and the mailbox is never sealed
+      atomically $ void $ send output blockOutput
+      updateBar barUpdateChannel
diff --git a/src/QBar/Server.hs b/src/QBar/Server.hs
index 8828796..290f14a 100644
--- a/src/QBar/Server.hs
+++ b/src/QBar/Server.hs
@@ -168,7 +168,7 @@ runBarConfiguration generateBarConfig options = do
   putStrLn "{\"version\":1,\"click_events\":true}"
   putStrLn "["
 
-  date <- dateBlock
+  date <- dateBlockOutput
   let initialBlocks = [date]
 
   -- Attach spinner indicator when verbose flag is set
-- 
GitLab