todo-webclient/client/index.ls

338 lines
8.7 KiB
Plaintext
Raw Normal View History

2019-11-20 19:22:51 +01:00
2019-12-05 01:45:21 +01:00
maquette = require "maquette"
nmd = require "nano-markdown"
2019-12-12 01:25:36 +01:00
LoginForm = require "../lib/authd/client/login-form.ls"
2019-12-05 01:45:21 +01:00
todows = require "./todowebsocket.ls"
2019-12-20 01:34:41 +01:00
AuthWS = require "./authws.ls"
2019-12-05 01:45:21 +01:00
bulma = require "./bulma.ls"
Task = require "./task.ls"
Project = require "./project.ls"
Modal = require "./modal.ls"
2019-12-12 21:49:15 +01:00
Navbar = require "./navbar.ls"
2019-12-22 19:48:21 +01:00
UsersCache = require "./users-cache.ls"
2019-11-20 19:22:51 +01:00
{create-projector, h} = maquette
projector = create-projector!
model = {
2019-12-05 02:28:17 +01:00
# view: login, project-list, project, network-error (TODO: other views, such as rights)
2019-12-22 19:48:21 +01:00
# FIXME: Replace model.current-view by an object that has a .render
# method.
2019-12-05 01:45:21 +01:00
current-view: "login"
2019-12-03 03:47:27 +01:00
2019-12-05 01:45:21 +01:00
viewed-project: void
2019-12-03 03:47:27 +01:00
# list of Project objects
2019-12-05 02:28:17 +01:00
project-list: []
2019-11-22 18:42:46 +01:00
port: 9999
2019-12-03 03:47:27 +01:00
authd-url: undefined
2019-11-22 18:42:46 +01:00
todod-url: undefined
authd-ws: undefined
todod-ws: undefined
previous-error: undefined
error: undefined
2019-12-10 05:59:52 +01:00
# { uid => user data }
users: {}
2019-12-22 19:48:21 +01:00
# uninitialized UserCache
users-cache: void
2019-11-20 19:22:51 +01:00
}
2019-12-03 03:47:27 +01:00
model.authd-url =
(if location.protocol == 'https' then 'wss' else 'ws') +
'://' + location.hostname + \: +
model.port +
"/auth.JSON"
2019-11-22 18:42:46 +01:00
model.todod-url =
2019-12-03 03:47:27 +01:00
(if location.protocol == 'https' then 'wss' else 'ws') +
'://' + location.hostname + \: +
2019-11-22 18:42:46 +01:00
model.port +
2019-12-03 05:29:09 +01:00
"/todo.JSON"
2019-11-20 19:22:51 +01:00
2019-12-03 03:47:27 +01:00
console.log "authd url: " + model.authd-url
console.log "todod url: " + model.todod-url
2019-11-20 19:22:51 +01:00
2019-11-22 16:52:50 +01:00
#
# network configuration
#
2019-12-20 01:34:41 +01:00
model.authd-ws = AuthWS model.authd-url
2019-11-22 18:42:46 +01:00
model.todod-ws = todows.create-socket model.todod-url
2019-11-22 16:52:50 +01:00
2019-12-03 03:47:27 +01:00
2019-12-22 19:48:21 +01:00
model.users-cache = UsersCache model.authd-ws, ->
projector.schedule-render!
2019-12-03 03:47:27 +01:00
#
# authd messages management
#
# authd socket errors
authd-on-websocket-error = (event) ->
console.log "WebSocket error.", event
model.current-view := "network-error"
2019-12-20 01:34:41 +01:00
model.last-network-error := event
2019-12-03 03:47:27 +01:00
projector.schedule-render!
authd-on-websocket-close = (event) ->
2019-12-20 01:34:41 +01:00
if model.current-view == "network-error"
return
2019-12-03 03:47:27 +01:00
model.current-view := "login"
console.log "WebSocket has been closed.", event
# model.todod-ws.reopen!
projector.schedule-render!
# record changes that need to happen on a network event
model.authd-ws.user-on-socket-error ++= [ authd-on-websocket-error ]
2019-12-03 05:29:09 +01:00
model.authd-ws.user-on-socket-close ++= [ authd-on-websocket-close ]
2019-12-03 03:47:27 +01:00
# authd message handlers
2019-12-12 01:25:36 +01:00
# HANDLED with the "on-login" callback in the LoginForm component
# model.authd-ws.add-event-listener \token, (message) ->
# model.current-view := "project-list"
# model.authd-ws.token := message.token
# model.todod-ws.token := message.token
# model.todod-ws.list-lists!
# model.authd-ws.list-users!
# projector.schedule-render!
2019-12-03 03:47:27 +01:00
2019-12-03 05:29:09 +01:00
model.authd-ws.add-event-listener \error, (message) ->
2019-12-10 05:59:52 +01:00
console.log "authd error", message
2019-12-03 03:47:27 +01:00
projector.schedule-render!
2019-12-03 05:29:09 +01:00
model.authd-ws.add-event-listener \user, (message) ->
2019-12-03 03:47:27 +01:00
model.users[message.user.uid] := message.user
projector.schedule-render!
2019-12-10 05:59:52 +01:00
model.authd-ws.add-event-listener \users-list, (message) ->
console.log "Received users: ", message
message.users.map (user) ->
model.users[user.uid] := user
projector.schedule-render!
2019-12-03 03:47:27 +01:00
2019-12-10 05:59:52 +01:00
# TODO: user-added, user-edited
2019-12-03 03:47:27 +01:00
2019-12-03 03:47:27 +01:00
#
# todod messages management
#
# todod socket errors
2019-11-22 16:52:50 +01:00
on-websocket-error = (event) ->
console.log "WebSocket error.", event
2019-11-22 18:42:46 +01:00
model.current-view := "network-error"
2019-12-20 01:34:41 +01:00
model.last-network-error := event
2019-11-22 16:52:50 +01:00
projector.schedule-render!
2019-11-20 19:22:51 +01:00
2019-11-22 18:42:46 +01:00
on-websocket-close = (event) ->
2019-12-20 01:34:41 +01:00
if model.current-view == "network-error"
return
2019-11-20 19:22:51 +01:00
model.current-view := "login"
2019-11-22 18:42:46 +01:00
console.log "WebSocket has been closed.", event
# model.todod-ws.reopen!
2019-11-20 19:22:51 +01:00
2019-11-22 16:52:50 +01:00
projector.schedule-render!
# record changes that need to happen on a network event
2019-12-03 03:47:27 +01:00
model.todod-ws.user-on-socket-error ++= [ on-websocket-error ]
2019-12-03 05:29:09 +01:00
model.todod-ws.user-on-socket-close ++= [ on-websocket-close ]
model.todod-ws.add-event-listener \lists-list, (message) ->
2019-12-20 01:34:41 +01:00
console.log "Projects list received", message
2019-12-07 00:44:02 +01:00
2019-12-05 02:28:17 +01:00
model.project-list := message.lists.map (x) ->
2019-12-07 00:44:02 +01:00
old-project = model.project-list.find((.id == x.id))
2019-12-22 19:48:21 +01:00
new-project = Project x, model.todod-ws, model.users-cache
2019-12-07 00:44:02 +01:00
if old-project && new-project.id == old-project.id
new-project.tasks = old-project.tasks
new-project
2019-12-05 01:45:21 +01:00
projector.schedule-render!
2019-11-22 18:42:46 +01:00
model.todod-ws.add-event-listener \new-list, (message) ->
console.log "New project", message
2019-12-07 00:44:02 +01:00
2019-12-22 19:48:21 +01:00
model.project-list := model.project-list ++ [ (Project message.list, model.todod-ws, model.users-cache) ]
projector.schedule-render!
2019-12-06 21:07:39 +01:00
model.todod-ws.add-event-listener \list-updated, (message) ->
console.log "Project updated", message
2019-12-06 21:07:39 +01:00
2019-12-22 19:48:21 +01:00
new-project = Project message.list, model.todod-ws, model.users-cache
2019-12-06 21:07:39 +01:00
2019-12-08 04:08:21 +01:00
model.project-list := model.project-list.map (project) ->
2019-12-06 21:07:39 +01:00
if project.id == message.list.id
new-project
else
project
if model.viewed-project && model.viewed-project.id == message.list.id
2019-12-08 18:37:14 +01:00
# create tasks in order to have the updated project properties
2019-12-22 19:48:21 +01:00
new-project.tasks = model.viewed-project.tasks.map (task) -> Task task, new-project, model.todod-ws, model.users-cache
2019-12-06 21:07:39 +01:00
model.viewed-project = new-project
projector.schedule-render!
2019-12-05 01:45:21 +01:00
model.todod-ws.add-event-listener \list-removed, (message) ->
console.log "A list has been removed", message
2019-12-07 00:44:02 +01:00
if model.current-view == "project" && model.viewed-project.id == message.list
2019-12-05 02:28:17 +01:00
model.current-view := "project-list"
2019-12-05 01:45:21 +01:00
model.viewed-project := void
projector.schedule-render!
2019-12-05 02:28:17 +01:00
model.project-list := model.project-list.filter((.id != message.list))
2019-12-05 01:45:21 +01:00
2019-12-07 00:44:02 +01:00
# tasks
model.todod-ws.add-event-listener \tasks, (message) ->
console.log "Tasks received", message
project = model.project-list.find((.id == message.list))
2019-12-22 19:48:21 +01:00
project.tasks := message.tasks.map (e) -> Task e, project, model.todod-ws, model.users-cache
2019-12-07 00:44:02 +01:00
2019-12-20 01:34:41 +01:00
if model.viewed-project && model.viewed-project.id == project.id
2019-12-07 00:44:02 +01:00
model.viewed-project := project
projector.schedule-render!
2019-12-05 04:47:29 +01:00
model.todod-ws.add-event-listener \task-created, (message) ->
console.log "A task has been created", message
2019-12-05 04:47:29 +01:00
task = message.task
list = model.project-list.find((.id == task.list))
2019-12-20 01:34:41 +01:00
if model.viewed-project && list.id == model.viewed-project.id
2019-12-07 00:44:02 +01:00
model.viewed-project := list
2019-12-05 04:47:29 +01:00
if list
2019-12-22 19:48:21 +01:00
list.tasks ++= [ Task task, list, model.todod-ws, model.users-cache ]
2019-12-06 01:56:32 +01:00
projector.schedule-render!
model.todod-ws.add-event-listener \task-updated, (message) ->
console.log "A task has been updated", message
task = message.task
list = model.project-list.find((.id == task.list))
if list
list.tasks = list.tasks.map (e) ->
if e.id == task.id
2019-12-22 19:48:21 +01:00
Task task, list, model.todod-ws, model.users-cache
2019-12-06 01:56:32 +01:00
else
e
2019-12-05 04:47:29 +01:00
projector.schedule-render!
2019-12-06 04:07:19 +01:00
model.todod-ws.add-event-listener \task-removed, (message) ->
console.log "A task has been removed", message
task = message.task
for project in model.project-list
if project.tasks.find((.id == task))
project.tasks := project.tasks.filter((.id != task))
projector.schedule-render!
2019-12-05 02:28:17 +01:00
render-project-list = ->
2019-12-12 21:49:15 +01:00
h \div.cards-list model.project-list.map (project) ->
h \div.card.is-grey {
2019-12-05 04:47:29 +01:00
key: project.id
onclick: ->
model.current-view := "project"
model.viewed-project := project
2019-12-06 03:16:48 +01:00
model.todod-ws.subscribe project.id
2019-12-05 04:47:29 +01:00
model.todod-ws.get-list project.id
model.todod-ws.get-tasks project.id
} [
2019-12-12 21:49:15 +01:00
h \div.card-content [
bulma.title 3 project.title
]
2019-12-05 04:47:29 +01:00
]
render-project = (project) ->
2019-12-05 02:28:17 +01:00
if project
project.render!
2019-12-03 03:47:27 +01:00
else
h \div.notification.is-error [
bulma.title 3 "Error, we did not get the project id " + project.id
2019-12-03 03:47:27 +01:00
]
2019-11-22 16:52:50 +01:00
2019-12-12 01:25:36 +01:00
model.login-form = LoginForm {
authws-url: model.authd-url
schedule-render: -> projector.schedule-render!
2019-12-12 01:25:36 +01:00
on-login: (user, token) ->
2019-12-20 01:34:41 +01:00
# FIXME: May double-login if the user clicks the “login” button too much.
if model.current-view != "login"
return
2019-12-12 01:25:36 +01:00
model.current-view := "project-list"
model.authd-ws.token := token
model.todod-ws.token := token
model.todod-ws.list-lists!
model.authd-ws.list-users!
projector.schedule-render!
}
2019-12-12 21:49:15 +01:00
navbar = Navbar!
2019-11-20 19:22:51 +01:00
2019-12-12 21:49:15 +01:00
render-body = ->
h \div#body [
navbar.render model
h \div#main-section [
switch model.current-view
when "login"
h \div.columns [
h \div.column
h \div.column [
model.login-form.render!
]
h \div.column
]
2019-11-20 19:22:51 +01:00
2019-12-12 21:49:15 +01:00
when "project-list"
2019-12-05 02:28:17 +01:00
render-project-list!
2019-12-12 21:49:15 +01:00
when "project"
2019-12-05 02:28:17 +01:00
render-project model.viewed-project
2019-12-20 01:34:41 +01:00
when "network-error"
h \div.container [
h \div.notification.is-danger [
h \div.title.is-2 [ "Socket error!" ]
h \div.content [
h \p [ "Were very sorry, but something got very wrong between here and the backend." ]
h \p [ "Please check your network connection and refresh the page." ]
h \p.small [ "The error happened on " + model.last-network-error.explicit-original-target.url ]
]
]
]
2019-11-20 19:22:51 +01:00
2019-12-12 21:49:15 +01:00
else
h \div.notification.is-error [
"Wait, what? Internal error!"
]
]
2019-12-05 01:45:21 +01:00
if model.modal
model.modal.render!
2019-11-20 19:22:51 +01:00
]
document.add-event-listener 'DOMContentLoaded' ->
2019-12-12 21:49:15 +01:00
projector.append document.body, render-body
2019-11-20 19:22:51 +01:00