Completely overhaul frontend styling for clearer usage. Add support for entering either the full link or just link key for viewing secrets without opening links anew.

This commit is contained in:
James Eversole 2022-12-26 21:49:21 -06:00
parent 785a797b7c
commit 117d8793dd
10 changed files with 205 additions and 112 deletions

View File

@ -1,3 +1,6 @@
# Changelog for Purr # Changelog for Purr
0.3.0:
- Complete overhaul of frontend styling
- Properly support entering either the full link or link key only in the "get secret" input field
## Unreleased changes ## Unreleased changes

View File

@ -14,7 +14,7 @@ license: ISC
license-file: LICENSE license-file: LICENSE
build-type: Simple build-type: Simple
extra-source-files: extra-source-files:
README.md README
ChangeLog.md ChangeLog.md
library library
@ -60,6 +60,7 @@ library
, random >=1.2 , random >=1.2
, scotty ==0.12 , scotty ==0.12
, shakespeare >=2.0.20 , shakespeare >=2.0.20
, split >=0.2.3.4
, sqlite-simple >=0.4.18.0 , sqlite-simple >=0.4.18.0
, text >=1.2.5.0 , text >=1.2.5.0
, time >=1.9 , time >=1.9
@ -97,6 +98,7 @@ executable Purr-exe
, random >=1.2 , random >=1.2
, scotty ==0.12 , scotty ==0.12
, shakespeare >=2.0.20 , shakespeare >=2.0.20
, split >=0.2.3.4
, sqlite-simple >=0.4.18.0 , sqlite-simple >=0.4.18.0
, text >=1.2.5.0 , text >=1.2.5.0
, time >=1.9 , time >=1.9
@ -135,6 +137,7 @@ test-suite Purr-test
, random >=1.2 , random >=1.2
, scotty ==0.12 , scotty ==0.12
, shakespeare >=2.0.20 , shakespeare >=2.0.20
, split >=0.2.3.4
, sqlite-simple >=0.4.18.0 , sqlite-simple >=0.4.18.0
, text >=1.2.5.0 , text >=1.2.5.0
, time >=1.9 , time >=1.9

View File

@ -1,12 +1,12 @@
name: Purr name: Purr
version: 0.1.0.0 version: 0.3.0
license: ISC license: ISC
author: "James Eversole" author: "James Eversole"
maintainer: "james@eversole.co" maintainer: "james@eversole.co"
copyright: "2022 James Eversole" copyright: "2022 James Eversole"
extra-source-files: extra-source-files:
- README.md - README
- ChangeLog.md - ChangeLog.md
default-extensions: default-extensions:
@ -42,6 +42,7 @@ dependencies:
- scotty == 0.12 - scotty == 0.12
- shakespeare >= 2.0.20 - shakespeare >= 2.0.20
- sqlite-simple >= 0.4.18.0 - sqlite-simple >= 0.4.18.0
- split >= 0.2.3.4
- time >= 1.9 - time >= 1.9
- text >= 1.2.5.0 - text >= 1.2.5.0
- wai-extra >= 3.1.12.1 - wai-extra >= 3.1.12.1

View File

@ -9,13 +9,15 @@ import Feature.Sharing.SQLite (findByLink, insertNewSecret)
import Feature.Sharing.Templates (renderPw) import Feature.Sharing.Templates (renderPw)
import Feature.Sharing.Types import Feature.Sharing.Types
import Control.Monad.Reader (ask, lift, liftIO)
import Data.Maybe (listToMaybe)
import Data.List.Split (splitOn)
import Web.Scotty.Trans
import Prelude
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy as LT
import Control.Monad.Reader (ask, lift, liftIO)
import Data.Maybe (listToMaybe)
import Web.Scotty.Trans
import Prelude
routes :: PurrApp () routes :: PurrApp ()
routes = do routes = do
@ -28,7 +30,7 @@ routes = do
post "/pw" $ do post "/pw" $ do
reqId <- param "userLink" reqId <- param "userLink"
res <- findByLink reqId res <- findByLink reqId
html $ renderPw reqId res html $ renderPw (last $ splitOn "/" reqId) res
post "/new" $ do post "/new" $ do
reqSecret <- param "newSec" reqSecret <- param "newSec"

View File

@ -9,6 +9,7 @@ import Control.Monad.Reader (ask, lift, liftIO)
import Crypto.Simple.CBC (encrypt, decrypt) import Crypto.Simple.CBC (encrypt, decrypt)
import Data.Maybe (listToMaybe) import Data.Maybe (listToMaybe)
import Data.Time.Clock.POSIX (getPOSIXTime) import Data.Time.Clock.POSIX (getPOSIXTime)
import Data.List.Split (splitOn)
import Database.SQLite.Simple import Database.SQLite.Simple
import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Base64 as B64
@ -22,7 +23,7 @@ findByLink link = do
db <- dbPath db <- dbPath
key <- encKey key <- encKey
conn <- liftIO $ open db conn <- liftIO $ open db
res <- liftIO $ query conn "SELECT * from pws WHERE link = ?" (Only link) res <- liftIO $ query conn "SELECT * from pws WHERE link = ?" (Only (last $ splitOn "/" link))
liftIO $ close conn liftIO $ close conn
readEncryptedSecret key res readEncryptedSecret key res

View File

@ -38,8 +38,7 @@ packages:
extra-deps: extra-deps:
- crypto-simple-0.1.0.0@sha256:5c0e1e04a814d903743d7543245951a91a46817230fdf478fadca57116805fc1,1502 - crypto-simple-0.1.0.0@sha256:5c0e1e04a814d903743d7543245951a91a46817230fdf478fadca57116805fc1,1502
ghc-options: #ghc-options:
'$everything': -haddock
# Override default flag values for local packages and extra-deps # Override default flag values for local packages and extra-deps
# flags: {} # flags: {}

View File

@ -4,18 +4,23 @@
@colorFour: #435F5D @colorFour: #435F5D
html html
font-family: Courier font-family: Monospace
background-color: #{colorTwo} background-color: #{colorTwo}
color: #{colorOne} color: #{colorOne}
body body
margin: 3% 10% 0 10% margin: 1% auto
font-size: 20px font-size: 20px
text-align: left font-weight: 150
text-align: left
header header
margin: 0 8% 0 0
text-align: right text-align: right
a
text-decoration: none
h2 h2
font-family: monaco, Consolas, monospace font-family: monaco, Consolas, monospace
text-transform: uppercase text-transform: uppercase
@ -29,14 +34,20 @@ p
a a
color: #{colorThree} color: #{colorThree}
.main
margin: 1em auto
max-width: 50%
.title .title
margin: 0 auto 0 auto text-align: center
font-size: 2em font-size: 2em
color: #{colorThree} margin: 0 auto 0 auto
color: #{colorThree}
.titleLink .titleLink
all: unset font-size: unset
cursor: pointer cursor: pointer
font-weight: 100
.title h1 .title h1
margin: 0.1em 0 0.1em 0 margin: 0.1em 0 0.1em 0
@ -49,19 +60,22 @@ a
opacity: 1 opacity: 1
.generators .generators
margin: 0 0 0.5em 0 padding: 1em 0.5em 0.5em 1.5em
background-color: #{colorTwo}
box-shadow: 8px 8px 12px #ccc
.generators .numberInput
background-color: #{colorTwo}
.generatorRadio
margin: 0
.logo .logo
margin: 4% 3% 0 0 margin: 4% 3% 0 0
font-size: 1.2vw font-size: 1.2vw
color: #{colorFour} color: #{colorFour}
text-align: center text-align: center
.content
display: flex
.column
flex: 50%
.emptyReq .emptyReq
height: 1% height: 1%
@ -103,7 +117,7 @@ a
transition: all 0.1s ease-in-out transition: all 0.1s ease-in-out
.mainInput:focus .mainInput:focus
border-bottom: 0.2em solid #{colorThree}; border-bottom: 0.2em solid #{colorThree}
.mainInput:focus::placeholder .mainInput:focus::placeholder
color: #{colorThree} color: #{colorThree}
@ -130,7 +144,7 @@ a
transition: all 0.1s ease-in-out transition: all 0.1s ease-in-out
.numberInput:focus .numberInput:focus
border-bottom: 0.2em solid #{colorThree}; border-bottom: 0.2em solid #{colorThree}
.numberInput:focus::placeholder .numberInput:focus::placeholder
color: #{colorThree} color: #{colorThree}
@ -142,33 +156,108 @@ a
.genResult .genResult
color: #{colorFour} color: #{colorFour}
.requestedPw
text-align: center
background-color: #333
color: #f0f6f0
margin: 1.5em 0 2em 0
padding: 0.5em 0.5em 0.5em 1.5em
box-shadow: 8px 8px 12px #ccc
.pwResult .pwResult
margin: 0 0 1em 0
font-size: 1.5em font-size: 1.5em
color: #{colorFour} color: #f0f6f0
.shareNew .shareNew
margin-bottom: 2em padding: 0.5em 0.5em 0.5em 1.5em
background-color: #{colorTwo}
box-shadow: 8px 8px 12px #ccc
margin: 1em 0 2em 0
.shareNew .mainInput
background-color: #{colorTwo}
.shareNew .numberInput
background-color: #{colorTwo}
.requestNew .requestNew
margin-top: 1em padding: 0.5em 0.5em 0.5em 1.5em
background-color: #{colorTwo}
box-shadow: 8px 8px 12px #ccc
margin-bottom: 2em
margin-top: 1em
.requestedPw .requestNew .mainInput
max-width: 85% background-color: #{colorTwo}
.resLink .resLink
font-size: 0.75em font-size: 0.8em
.validForm .validForm
margin: 0.5em 0 0 0 margin: 0
.radioLabel
display: flex
align-items: center
border-radius: 100px
margin: 0.3em 0
padding: 0.3em 0.5em
cursor: pointer
transition: .3s
.radioLabel:hover, .radioLabel:focus-within
background: hsla(0, 0%, 80%, .14)
.radioInput
position: fixed
left: 0
top: 0
width: 1px
height: 1px
opacity: 0
z-index: -1
.radioDesign
width: 22px
height: 22px
border-radius: 100px
background: linear-gradient(to right bottom, #fff, #{colorThree})
position: relative
.radioDesign::before
content: ''
display: inline-block
width: inherit
height: inherit
border-radius: inherit
background: hsl(0, 0%, 90%)
transform: scale(1.1)
transition: .3s
.radioInput:checked+.radioDesign::before
transform: scale(0)
.radioText
margin-left: 14px
letter-spacing: 3px
transition: .3s
.radioInput:checked~.radioText
color: hsl(0, 0%, 40%)
.hidden
display: none
@media only screen and (max-width : 768px) @media only screen and (max-width : 768px)
body body
text-align: center text-align: center
font-size: 16px font-size: 16px
.content .header
display: block margin: 0 2% 0 0
.main
max-width: 90%
.mainButton .mainButton
width: 80% width: 80%
@ -176,9 +265,6 @@ a
.genButton .genButton
width: 80% width: 80%
.generators
text-align: left
.mainInput .mainInput
width: 95% width: 95%
max-width: 100% max-width: 100%
@ -188,14 +274,17 @@ a
width: 25% width: 25%
.title .title
margin: 4% auto 2% auto margin: 4% auto 2% auto
font-size: 3em
.title h1 .title h1
font-size: 2.5em
margin-bottom: 0 margin-bottom: 0
.requestedPw .requestedPw
max-width: 100% max-width: 100%
.pwResult
font-size: 1.75em
.shareUtils .shareUtils
width: 100% width: 100%

View File

@ -1,28 +1,20 @@
<div #generators .column> <div #generators .generators>
<h2>Generators
<h3>Random, Classic, XKCD
<form id="genForm"> <form id="genForm">
<div .generators"> <div .generatorRadio">
<input type="radio" id="gibberish" name="newSec" value="#{show genGibberish}"> <label .radioLabel for="gibberish" .genResult>
<label for="gibberish" .genResult>#{show genGibberish} <input .radioInput type="radio" id="gibberish" name="newSec" value="#{show genGibberish}">
<br /> <div .radioDesign>
<input type="radio" id="oldschool" name="newSec" value="#{show genOldschool}"> <div .radioText>#{show genGibberish}
<label for="oldschool" .genResult>#{show genOldschool} <label .radioLabel for="oldschool" .genResult>
<br /> <input .radioInput type="radio" id="oldschool" name="newSec" value="#{show genOldschool}">
<input type="radio" id="xkcd" name="newSec" value="#{show genXkcd}"> <div .radioDesign>
<label for="xkcd" .genResult>#{show genXkcd} <div .radioText>#{show genOldschool}
<button .genButton <label .radioLabel for="xkcd" .genResult>
hx-get="/gen" <input .radioInput type="radio" id="xkcd" name="newSec" value="#{show genXkcd}">
hx-target="#generators" <div .radioDesign>
hx-swap="outerHTML" <div .radioText>#{show genXkcd}
/>
Generate New
<br />
<div .validForm> <div .validForm>
<h3>Share Generated Password
Valid for:
<br />
<input .numberInput <input .numberInput
name="newSecDuration" name="newSecDuration"
type="number" type="number"
@ -39,12 +31,19 @@
value="20" value="20"
onkeyup="if (value < 1 || value > 60) { value = 0 }" onkeyup="if (value < 1 || value > 60) { value = 0 }"
/> views /> views
<button .genButton <button .mainButton
hx-post="/new" hx-post="/new"
hx-target="#requestedPw" hx-target="#requestedPw"
hx-swap="outerHTML" hx-swap="outerHTML"
hx-include="[name='newSec']" hx-include="[name='newSec']"
/> />
Share Password share selected secret
<img class="htmx-indicator" src="/loading.svg" /> <img class="htmx-indicator" src="/loading.svg" />
<button .mainButton
hx-get="/gen"
hx-target="#generators"
hx-swap="outerHTML"
/>
regenerate
<br />

View File

@ -11,19 +11,20 @@ $doctype 5
<header> <header>
<a href="https://git.eversole.co/purr"> <a href="https://git.eversole.co/purr">
Made with &#9829; made with &#9829;
| <a href="mailto:#{email}">Contact |
<a href="mailto:#{email}">contact
<div #title .title> <div #main .main>
<h1>
<a #titleLink .titleLink href="/">purr
<div #content .content> <div #title .title>
<h1>
<a #titleLink .titleLink href="/">purr
<div #shareUtils .column>
<h2>Sharing Tools <div #shareUtils>
$if (link == "/") $if (link == "/")
<div #requestedPw .requestedPw> <div #requestedPw .hidden>
<p .emptyReq> <p .emptyReq>
$else $else
<div #requestedPw .requestedPw <div #requestedPw .requestedPw
@ -35,16 +36,31 @@ $doctype 5
> >
Loading... <img class="htmx-indicator" src="/loading.svg" /> Loading... <img class="htmx-indicator" src="/loading.svg" />
<div #requestNew .requestNew>
<p>
<input .mainInput
name="userLink"
type="text"
placeholder="enter a link to view secret"
/>
<button .mainButton
hx-post="/pw"
hx-target="#requestedPw"
hx-swap="outerHTML"
hx-include="[name='userLink']"
/>
get secret
<img class="htmx-indicator" src="/loading.svg" />
<div #shareNew .shareNew> <div #shareNew .shareNew>
<h3>Share New Secret
<p> <p>
<input .mainInput <input .mainInput
name="newSec" name="newSec"
type="text" type="text"
placeholder="Enter a Secret to Share" placeholder="enter a secret to share"
/> />
<div .validForm> <div .validForm>
Valid for: valid for:
<br /> <br />
<input .numberInput <input .numberInput
name="newSecDuration" name="newSecDuration"
@ -68,31 +84,12 @@ $doctype 5
hx-swap="outerHTML" hx-swap="outerHTML"
hx-include="[id='shareNew']" hx-include="[id='shareNew']"
/> />
Share Secret share secret
<img class="htmx-indicator" src="/loading.svg" /> <img class="htmx-indicator" src="/loading.svg" />
<div #requestNew .requestNew> <div #generators
<h3>Lookup hx-trigger="load"
<p> hx-get="/gen"
<input .mainInput hx-target="#generators"
name="userLink"
type="text"
placeholder="Enter a Link to View Secret"
/>
<button .mainButton
hx-post="/pw"
hx-target="#requestedPw"
hx-swap="outerHTML" hx-swap="outerHTML"
hx-include="[name='userLink']" >
/>
Get Secret
<img class="htmx-indicator" src="/loading.svg" />
<div #generators .column>
<h2>Generators
<button .mainButton
hx-get="/gen"
hx-target="#generators"
hx-swap="outerHTML"
/>
Load Generators

View File

@ -1,7 +1,6 @@
<div #requestedPw .requestedPw> <div #requestedPw .requestedPw>
$maybe pw <- password $maybe pw <- password
<p .resLink>Here's the secret found at <a href="/pw/#{link}">/pw/#{link}</a>: <p .resLink>secret found at <a href="/pw/#{link}">/pw/#{link}</a>:
<h3 .pwResult>#{pw} <h3 .pwResult>#{pw}
<hr />
$nothing $nothing
<h3 .pwResult>No secret found at <a href="/pw/#{link}">/pw/#{link}</a> <h3 .pwResult>no secret found at <a href="/pw/#{link}">/pw/#{link}</a>