A music player that connects to your cloud/distributed storage.
1module Main where
2
3import Data.Time.Clock.POSIX (getPOSIXTime)
4import Flow
5import Protolude hiding (list)
6import Renderers
7import Shikensu hiding (list)
8import Shikensu.Contrib
9import Shikensu.Contrib.IO as Shikensu
10import Shikensu.Utilities
11
12import qualified Data.Aeson as Aeson
13import qualified Data.Aeson.KeyMap as KeyMap (fromList)
14import qualified Data.ByteString.Lazy as BSL (toStrict)
15import qualified Data.Char as Char
16import qualified Data.List as List
17import qualified Data.Text as Text
18import qualified Data.Text.Encoding as Text
19import qualified Data.Text.IO as Text
20
21
22-- | (• ◡•)| (❍ᴥ❍ʋ)
23
24
25main :: IO ()
26main =
27 do
28 de <- dependencies
29 se <- sequences
30
31 -- Execute flows
32 -- & reduce to a single dictionary
33 let dictionary = List.concatMap (flow de) se
34
35 -- Write everything to disk
36 write "../build" dictionary
37
38 -- Make a file tree
39 build <- list "../build/**/*.*"
40
41 build
42 |> makeTree
43 |> write "../build"
44
45 -- Inject build timestamp
46 --
47 -- NOTE: Done by esbuild at the moment (see Justfile)
48 -- But we leave it here in case we need it anywhere else.
49 --
50 -- insertBuildTimestamp (de !~> "timestamp") build
51
52 -- Fin
53 return ()
54
55
56list :: [Char] -> IO Dictionary
57list pattern =
58 Shikensu.listRelativeF "./src" [pattern] >>= Shikensu.read
59
60
61
62-- SEQUENCES
63
64
65data Sequence
66 = AboutPages
67 | Favicons
68 | Fonts
69 | Hosting
70 | Html
71 | Images
72 | Manifests
73
74
75sequences :: IO [( Sequence, Dictionary )]
76sequences = lsequence
77 [ ( AboutPages, list "Static/About/**/*.md" )
78 , ( Favicons, list "Static/Favicons/**/*.*" )
79 , ( Fonts, list "Static/Fonts/**/*.*" )
80 , ( Hosting, list "Static/Hosting/**/*" )
81 , ( Html, list "Static/Html/**/*.html" )
82 , ( Images, list "Static/Images/**/*.*" )
83 , ( Manifests, list "Static/Manifests/**/*.*" )
84 ]
85
86
87
88-- FLOWS
89
90
91flow :: Dependencies -> (Sequence, Dictionary) -> Dictionary
92flow _ (Html, dict) =
93 rename "Application.html" "index.html" dict
94
95
96flow _ (Favicons, dict) = dict
97flow _ (Fonts, dict) = prefixDirname "fonts/" dict
98flow _ (Hosting, dict) = dict
99flow _ (Images, dict) = prefixDirname "images/" dict
100
101
102{-| Manifests -}
103flow _ (Manifests, dict) =
104 dict
105 |> clone "manifest.json" "site.webmanifest"
106 |> rename "Nextcloud/appinfo.xml" "appinfo/info.xml"
107
108
109{-| About Pages -}
110flow x (AboutPages, dict) =
111 dict
112 |> map lowerCasePath
113 |> renameExt ".md" ".html"
114 |> permalink "index"
115 |> prefixDirname "about/"
116 |> renderContent markdownRenderer
117 |> renderContent (layoutRenderer $ x !~> "aboutLayout")
118
119
120
121-- ADDITIONAL IO
122-- FLOW DEPENDENCIES
123
124
125type Dependencies = Aeson.Object
126
127
128dependencies :: IO Dependencies
129dependencies = do
130 aboutLayout <- Text.readFile "src/Static/About/Layout.html"
131 timestamp <- fmap show unixTime :: IO Text
132
133 return $ KeyMap.fromList
134 [ ( "aboutLayout", Aeson.toJSON aboutLayout )
135 , ( "timestamp", Aeson.toJSON timestamp )
136 ]
137
138
139
140-- INSERT
141
142
143makeTree :: Dictionary -> Dictionary
144makeTree dict =
145 let
146 treeContent =
147 dict
148 |> List.map localPath
149 |> List.filter (\p -> p /= "tree.json")
150 |> Aeson.encode
151 |> BSL.toStrict
152
153 defs =
154 case headMay dict of
155 Just def ->
156 def
157 |> forkDefinition "tree.json"
158 |> wrap
159 |> setContent treeContent
160
161 Nothing ->
162 []
163 in
164 defs
165
166
167insertBuildTimestamp :: Text -> Dictionary -> IO ()
168insertBuildTimestamp version dict =
169 dict
170 |> List.filter
171 (\def ->
172 localPath def == "service-worker.js"
173 )
174 |> List.map
175 (\def ->
176 def
177 |> content
178 |> fmap Text.decodeUtf8
179 |> fmap (Text.replace "{{BUILD_TIMESTAMP}}" version)
180 |> fmap Text.encodeUtf8
181 |> (\c -> def { content = c })
182 )
183 |> write "../build"
184 |> fmap (\_ -> ())
185
186
187
188-- COMMON
189
190
191lowerCasePath :: Definition -> Definition
192lowerCasePath def =
193 Shikensu.forkDefinition
194 ( def
195 |> localPath
196 |> List.map Char.toLower
197 )
198 def
199
200
201unixTime :: IO Int
202unixTime =
203 fmap floor getPOSIXTime
204
205
206wrap :: a -> [a]
207wrap a =
208 [a]