Compare commits

..

No commits in common. "67d88bd31b2860bd995b461b1974e6939a0da591" and "de60936cd2ae62b2d7869f59fffff7b6958d2df0" have entirely different histories.

9 changed files with 12 additions and 73 deletions

View file

@ -9,8 +9,7 @@ The goal is to provide a minimalistic and fast todo list that is self hostable.
- [ ] write a minimal step by step guide to install with nix, - [ ] write a minimal step by step guide to install with nix,
- [ ] add some css to make it look nicer - [ ] add some css to make it look nicer
- [ ] add htmx to make more agreable without making js manadatory - [ ] add htmx to make more agreable without making js manadatory
- [x] make api to allow usage with native app (a way to get every list that has been modified since date $date belonging from the user in a json or similar format) - [ ] make api to allow usage with native app (a way to get every list that has been modified since date $date belonging from the user in a json or similar format)
- [ ] document api to help create clients
## Version 0.0.3 ## Version 0.0.3
Simple todo list webapp. Simple todo list webapp.
Features : Features :

View file

@ -1 +0,0 @@
:M^¦ÄO2«I†èš³4,pe<70>¿Ôì8Ä·,CI†(q¸¦7/ ÷gGˆuâÍ×¥L'¿(Éœ¡wq1I#ÔpµÌYW»)2L{2—;våÇ_ižËÀ[ÛÈʳ<C38A>ûÕY

View file

@ -8,15 +8,12 @@ TodolistItem
Todolist Todolist
groupId GroupId OnDeleteCascade groupId GroupId OnDeleteCascade
title Text title Text
lastModified UTCTime
UniqueListPair groupId title UniqueListPair groupId title
deriving Show
User User
name Text name Text
UniqueName name UniqueName name
Group Group
group Text group Text
lastModified UTCTime
GroupUser GroupUser
user UserId user UserId
groupId GroupId OnDeleteCascade groupId GroupId OnDeleteCascade

View file

@ -26,4 +26,3 @@
/delete DeleteGroupR POST /delete DeleteGroupR POST
/delete/group/#GroupId DeleteTodolistR POST /delete/group/#GroupId DeleteTodolistR POST
/api/#Int ApiR GET

View file

@ -47,7 +47,6 @@ import Handler.Common
import Handler.Group import Handler.Group
import Handler.Todolist import Handler.Todolist
import Handler.TodolistItem import Handler.TodolistItem
import Handler.Api
-- This line actually creates our YesodDispatch instance. It is the second half -- This line actually creates our YesodDispatch instance. It is the second half
-- of the call to mkYesodData which occurs in Foundation.hs. Please see the -- of the call to mkYesodData which occurs in Foundation.hs. Please see the

View file

@ -12,7 +12,7 @@ module Foundation where
import Import.NoFoundation import Import.NoFoundation
import Data.Kind (Type) import Data.Kind (Type)
import Database.Persist.Sql (ConnectionPool, runSqlPool, rawSql) import Database.Persist.Sql (ConnectionPool, runSqlPool)
import Text.Hamlet (hamletFile) import Text.Hamlet (hamletFile)
import Text.Jasmine (minifym) import Text.Jasmine (minifym)
import Control.Monad.Logger (LogSource) import Control.Monad.Logger (LogSource)
@ -209,8 +209,3 @@ dbIfAuth groupId action = do
result <- runDB $ selectFirst [GroupUserUser ==. user, GroupUserGroupId ==. groupId] [] result <- runDB $ selectFirst [GroupUserUser ==. user, GroupUserGroupId ==. groupId] []
if isNothing result then permissionDenied "you are not logged in or you dont have access to this group" if isNothing result then permissionDenied "you are not logged in or you dont have access to this group"
else runDB action else runDB action
getGroups :: Key User -> Handler [Entity Group]
getGroups userId =
let sql = "SELECT ?? FROM \"group\" JOIN group_user gu ON \"group\".id = gu.group_id WHERE gu.user = ?" in
runDB $ rawSql sql [toPersistValue userId]

View file

@ -1,52 +0,0 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Handler.Api where
import Import
import Database.Persist.Sql (rawSql)
import Data.Time.Clock.POSIX (posixSecondsToUTCTime, utcTimeToPOSIXSeconds)
import qualified Data.Text as Text
getApiR :: Int -> Handler TypedContent
getApiR time = do
-- TODO: use only one runDB
userId <- getUserId
-- We get all groups no matter what, since else we can't know which groups have been deleted
groups <- getGroups userId
let utcTime = posixSecondsToUTCTime (fromIntegral time)
let sqlLists = "select ?? from todolist join group_user on todolist.group_id = group_user.group_id and group_user.user = ? join \"group\" on todolist.group_id = \"group\".id and \"group\".last_modified > ?;"
lists <- runDB $ rawSql sqlLists [toPersistValue userId, toPersistValue utcTime]
let a = lists :: [Entity Todolist]
let sqlItems = "select ?? from todolist join group_user on todolist.group_id = group_user.group_id and group_user.user = ? join \"group\" on todolist.group_id = \"group\".id and \"group\".last_modified > ? join todolist_item on todolist_item.todolist_id = todolist.id;"
items <- runDB $ rawSql sqlItems [toPersistValue userId, toPersistValue utcTime]
let t = unlines $ map groupToCSV groups <> map todolistToCSV lists <> map todolistItemToCSV items
return $ TypedContent typePlain $ toContent t
todolistItemToCSV :: Entity TodolistItem -> Text
todolistItemToCSV item = "i," <> fieldToText item
todolistToCSV :: Entity Todolist -> Text
todolistToCSV list = "l," <> fieldToText list
groupToCSV :: Entity Group -> Text
groupToCSV group = "g," <> fieldToText group
-- TODO: error management ? (maybe use Either Text Text and then propagate left to handler and send error ?)
fieldToText :: PersistEntity record => Entity record -> Text
fieldToText field = Text.intercalate "," (map persistValueToText $ entityValues field)
persistValueToText :: PersistValue -> Text
persistValueToText (PersistText s) = s
persistValueToText (PersistInt64 i) = Text.pack $ show i
persistValueToText (PersistUTCTime d) = Text.pack $ show $ floor (utcTimeToPOSIXSeconds d)
persistValueToText (PersistBool b) = if b then "T" else "F"
persistValueToText _ = error "Wrong input type"
getText :: Text
getText = do
-- GET EVERY GROUP THAT HAS BEEN MODIFIED SINCE TIMESTAMP FROM USER
-- GET EVERY TODOLIST THAT HAS BEEN MODIFIED SINCE TIMESTAMP
-- GET EVERY ITEM FROM THESE TODOLISTS
-- ENCODE ALL OF THEM IN THE TEXTFILE
-- SEND IT !
-- DONE :)
error "not done yet"

View file

@ -11,7 +11,7 @@ module Handler.Group where
import Import import Import
import Text.Read import Text.Read
import Database.Persist.Sql (fromSqlKey, toSqlKey) import Database.Persist.Sql (fromSqlKey, toSqlKey, rawSql)
getGroupR :: Handler Html getGroupR :: Handler Html
getGroupR = do getGroupR = do
userId <- getUserId userId <- getUserId
@ -25,8 +25,7 @@ postAddGroupR = do
-- TODO: in a newer version, put insertUnique_ -- TODO: in a newer version, put insertUnique_
user <- getUserId user <- getUserId
_ <- runDB $ do _ <- runDB $ do
currentTime <- liftIO getCurrentTime gId <- insert $ Group g
gId <- insert $ Group g currentTime
success <- insertUnique $ GroupUser user gId success <- insertUnique $ GroupUser user gId
when (isNothing success) $ delete gId when (isNothing success) $ delete gId
redirect GroupR redirect GroupR
@ -56,3 +55,8 @@ postDeleteGroupR = do
-- TODO: make sure the user has access to it aswell (this only works now for single user), and handle group owned by many -- TODO: make sure the user has access to it aswell (this only works now for single user), and handle group owned by many
runDB $ deleteWhere [GroupId <-. ids] runDB $ deleteWhere [GroupId <-. ids]
redirect EditGroupR redirect EditGroupR
getGroups :: Key User -> Handler [Entity Group]
getGroups userId =
let sql = "SELECT ?? FROM \"group\" JOIN group_user gu ON \"group\".id = gu.group_id WHERE gu.user = ?" in
runDB $ rawSql sql [toPersistValue userId]

View file

@ -16,8 +16,7 @@ postAddTodolistR :: GroupId -> Handler Html
postAddTodolistR groupId = do postAddTodolistR groupId = do
list <- runInputPost $ ireq textField "list" list <- runInputPost $ ireq textField "list"
-- TODO: in a newer version, put insertUnique_ -- TODO: in a newer version, put insertUnique_
currentTime <- liftIO getCurrentTime _ <- dbIfAuth groupId (insertUnique $ Todolist groupId list)
_ <- dbIfAuth groupId (insertUnique $ Todolist groupId list currentTime)
redirect $ TodolistR groupId redirect $ TodolistR groupId
-- TODO: move this to a new handler file -- TODO: move this to a new handler file