Add a basic HTML interface
This commit is contained in:
parent
257712bfae
commit
831b361eda
|
@ -0,0 +1,128 @@
|
||||||
|
<!Doctype HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #201b27;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
#thumbnails {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 30px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.thumbnail-card {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
#focused-panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0, 0, 0, 60%);
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#focused-panel > img {
|
||||||
|
max-height: 80vh;
|
||||||
|
max-width: 60vw;
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
#spinner {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loader-text {
|
||||||
|
position: absolute;
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto auto;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 31px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Yoinked from https://codepen.io/AdamDipinto/pen/eYOaGvY
|
||||||
|
with modifications */
|
||||||
|
.loader {
|
||||||
|
position: relative;
|
||||||
|
width: 350px;
|
||||||
|
height: 350px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(#ff52bf, #f1f1f1, #1ed3ec);
|
||||||
|
animation: animate 1.2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animate {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(#ff52bf, #f1f1f1, #1ed3ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span:nth-child(1) {
|
||||||
|
filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span:nth-child(2) {
|
||||||
|
filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span:nth-child(3) {
|
||||||
|
filter: blur(25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span:nth-child(4) {
|
||||||
|
filter: blur(50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
background: #201b27;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="/index.js"></script>
|
||||||
|
<script src="https://unpkg.com/rescript-blurhash@0.4.0/dist/production.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="spinner">
|
||||||
|
<div class="loader">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div id="loader-text">Downloading<br/>Gallery...</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,6 +1,7 @@
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
import Aviary.FFI (removeSpinner)
|
||||||
import Aviary.UI (component)
|
import Aviary.UI (component)
|
||||||
import Aviary.Logic (fetch_and_decrypt_gallery, get_parameters)
|
import Aviary.Logic (fetch_and_decrypt_gallery, get_parameters)
|
||||||
import Aviary.Model (Model(..))
|
import Aviary.Model (Model(..))
|
||||||
|
@ -8,6 +9,7 @@ import Aviary.Model (Model(..))
|
||||||
import Data.Either (Either(..))
|
import Data.Either (Either(..))
|
||||||
import Effect (Effect)
|
import Effect (Effect)
|
||||||
import Effect.Aff (Aff, launchAff)
|
import Effect.Aff (Aff, launchAff)
|
||||||
|
import Effect.Class (liftEffect)
|
||||||
import Halogen.Aff (awaitBody)
|
import Halogen.Aff (awaitBody)
|
||||||
import Halogen.VDom.Driver (runUI)
|
import Halogen.VDom.Driver (runUI)
|
||||||
|
|
||||||
|
@ -18,6 +20,7 @@ main_aff = do
|
||||||
gallery <- case parameters of
|
gallery <- case parameters of
|
||||||
Left e -> pure $ GError e
|
Left e -> pure $ GError e
|
||||||
Right parameters' -> fetch_and_decrypt_gallery parameters'
|
Right parameters' -> fetch_and_decrypt_gallery parameters'
|
||||||
|
_ <- liftEffect $ removeSpinner
|
||||||
_ <- runUI (component gallery) unit body
|
_ <- runUI (component gallery) unit body
|
||||||
pure unit
|
pure unit
|
||||||
|
|
||||||
|
|
14
src/UI.purs
14
src/UI.purs
|
@ -25,6 +25,7 @@ data Event = LoadThumbs
|
||||||
| ThumbLoaded Int ImageData
|
| ThumbLoaded Int ImageData
|
||||||
| FullLoaded Int ImageData
|
| FullLoaded Int ImageData
|
||||||
| Focus Int
|
| Focus Int
|
||||||
|
| Unfocus
|
||||||
|
|
||||||
component :: forall query input. Model -> H.Component query input Event Aff
|
component :: forall query input. Model -> H.Component query input Event Aff
|
||||||
component initialState = H.mkComponent
|
component initialState = H.mkComponent
|
||||||
|
@ -52,8 +53,8 @@ renderThumbnail pos image =
|
||||||
renderFocused :: forall m. Image -> H.ComponentHTML Event () m
|
renderFocused :: forall m. Image -> H.ComponentHTML Event () m
|
||||||
renderFocused image =
|
renderFocused image =
|
||||||
HH.div
|
HH.div
|
||||||
[ HP.class_ $ ClassName "focused-panel"
|
[ HP.id $ "focused-panel"
|
||||||
, HP.class_ $ ClassName "visible"
|
, HE.onClick \_ -> Unfocus
|
||||||
]
|
]
|
||||||
( [ HH.img $
|
( [ HH.img $
|
||||||
maybe [] (HP.src >>> pure) (fullOrBlurhash image)
|
maybe [] (HP.src >>> pure) (fullOrBlurhash image)
|
||||||
|
@ -107,11 +108,18 @@ update (Focus newFocus) = do
|
||||||
Just focusedImage' -> fetchFullAction newFocus focusedImage'
|
Just focusedImage' -> fetchFullAction newFocus focusedImage'
|
||||||
Nothing ->
|
Nothing ->
|
||||||
H.put $ GError $ UnexpectedError "Focus event raised with an out of bounds index!"
|
H.put $ GError $ UnexpectedError "Focus event raised with an out of bounds index!"
|
||||||
|
update Unfocus = H.modify_ \model -> case model of
|
||||||
|
GError e -> GError e
|
||||||
|
GLoaded gal -> GLoaded gal { focus = Nothing }
|
||||||
|
|
||||||
render :: forall m. Model -> H.ComponentHTML Event () m
|
render :: forall m. Model -> H.ComponentHTML Event () m
|
||||||
render (GError e) = HH.p_ [ HH.text $ show e ]
|
render (GError e) = HH.p_ [ HH.text $ show e ]
|
||||||
render (GLoaded {title, desc, images, focus}) = HH.div_
|
render (GLoaded {title, desc, images, focus}) = HH.div_
|
||||||
((maybe [] (HH.text >>> pure >>> HH.h1_ >>> pure) title) <>
|
((maybe [] (HH.text >>> pure >>> HH.h1_ >>> pure) title) <>
|
||||||
(maybe [] (HH.text >>> pure >>> HH.p_ >>> pure) desc) <>
|
(maybe [] (HH.text >>> pure >>> HH.p_ >>> pure) desc) <>
|
||||||
(mapWithIndex renderThumbnail images) <>
|
[ HH.div
|
||||||
|
[ HP.id "thumbnails"
|
||||||
|
]
|
||||||
|
(mapWithIndex renderThumbnail images)
|
||||||
|
] <>
|
||||||
(maybe [] (renderFocused >>> pure) (index images =<< focus)))
|
(maybe [] (renderFocused >>> pure) (index images =<< focus)))
|
||||||
|
|
|
@ -21,3 +21,7 @@ export function decodeBlurhashImpl(just) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeSpinner() {
|
||||||
|
document.getElementById("spinner").remove()
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
module Aviary.FFI where
|
module Aviary.FFI where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
import Data.ArrayBuffer.Types (ArrayBuffer)
|
import Data.ArrayBuffer.Types (ArrayBuffer)
|
||||||
import Data.Maybe (Maybe(..))
|
import Data.Maybe (Maybe(..))
|
||||||
|
import Effect (Effect)
|
||||||
import Web.File.Blob (Blob)
|
import Web.File.Blob (Blob)
|
||||||
|
|
||||||
-- mimeType :: String
|
-- mimeType :: String
|
||||||
|
@ -15,3 +18,5 @@ decodeBlurhash :: Int -> Int -> String -> Maybe String
|
||||||
decodeBlurhash = decodeBlurhashImpl Just Nothing
|
decodeBlurhash = decodeBlurhashImpl Just Nothing
|
||||||
decodeBlurhash32 :: String -> Maybe String
|
decodeBlurhash32 :: String -> Maybe String
|
||||||
decodeBlurhash32 = decodeBlurhash 32 32
|
decodeBlurhash32 = decodeBlurhash 32 32
|
||||||
|
|
||||||
|
foreign import removeSpinner :: Effect Unit
|
||||||
|
|
Loading…
Reference in New Issue