todo-webclient/client/index.ls

338 lines
8.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

maquette = require "maquette"
nmd = require "nano-markdown"
LoginForm = require "../lib/authd/client/login-form.ls"
todows = require "./todowebsocket.ls"
AuthWS = require "./authws.ls"
bulma = require "./bulma.ls"
Task = require "./task.ls"
Project = require "./project.ls"
Modal = require "./modal.ls"
Navbar = require "./navbar.ls"
UsersCache = require "./users-cache.ls"
{create-projector, h} = maquette
projector = create-projector!
model = {
# view: login, project-list, project, network-error (TODO: other views, such as rights)
# FIXME: Replace model.current-view by an object that has a .render
# method.
current-view: "login"
viewed-project: void
# list of Project objects
project-list: []
port: 9999
authd-url: undefined
todod-url: undefined
authd-ws: undefined
todod-ws: undefined
previous-error: undefined
error: undefined
# { uid => user data }
users: {}
# uninitialized UserCache
users-cache: void
}
model.authd-url =
(if location.protocol == 'https' then 'wss' else 'ws') +
'://' + location.hostname + \: +
model.port +
"/auth.JSON"
model.todod-url =
(if location.protocol == 'https' then 'wss' else 'ws') +
'://' + location.hostname + \: +
model.port +
"/todo.JSON"
console.log "authd url: " + model.authd-url
console.log "todod url: " + model.todod-url
#
# network configuration
#
model.authd-ws = AuthWS model.authd-url
model.todod-ws = todows.create-socket model.todod-url
model.users-cache = UsersCache model.authd-ws, ->
projector.schedule-render!
#
# authd messages management
#
# authd socket errors
authd-on-websocket-error = (event) ->
console.log "WebSocket error.", event
model.current-view := "network-error"
model.last-network-error := event
projector.schedule-render!
authd-on-websocket-close = (event) ->
if model.current-view == "network-error"
return
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 ]
model.authd-ws.user-on-socket-close ++= [ authd-on-websocket-close ]
# authd message handlers
# 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!
model.authd-ws.add-event-listener \error, (message) ->
console.log "authd error", message
projector.schedule-render!
model.authd-ws.add-event-listener \user, (message) ->
model.users[message.user.uid] := message.user
projector.schedule-render!
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!
# TODO: user-added, user-edited
#
# todod messages management
#
# todod socket errors
on-websocket-error = (event) ->
console.log "WebSocket error.", event
model.current-view := "network-error"
model.last-network-error := event
projector.schedule-render!
on-websocket-close = (event) ->
if model.current-view == "network-error"
return
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.todod-ws.user-on-socket-error ++= [ on-websocket-error ]
model.todod-ws.user-on-socket-close ++= [ on-websocket-close ]
model.todod-ws.add-event-listener \lists-list, (message) ->
console.log "Projects list received", message
model.project-list := message.lists.map (x) ->
old-project = model.project-list.find((.id == x.id))
new-project = Project x, model.todod-ws, model.users-cache
if old-project && new-project.id == old-project.id
new-project.tasks = old-project.tasks
new-project
projector.schedule-render!
model.todod-ws.add-event-listener \new-list, (message) ->
console.log "New project", message
model.project-list := model.project-list ++ [ (Project message.list, model.todod-ws, model.users-cache) ]
projector.schedule-render!
model.todod-ws.add-event-listener \list-updated, (message) ->
console.log "Project updated", message
new-project = Project message.list, model.todod-ws, model.users-cache
model.project-list := model.project-list.map (project) ->
if project.id == message.list.id
new-project
else
project
if model.viewed-project && model.viewed-project.id == message.list.id
# create tasks in order to have the updated project properties
new-project.tasks = model.viewed-project.tasks.map (task) -> Task task, new-project, model.todod-ws, model.users-cache
model.viewed-project = new-project
projector.schedule-render!
model.todod-ws.add-event-listener \list-removed, (message) ->
console.log "A list has been removed", message
if model.current-view == "project" && model.viewed-project.id == message.list
model.current-view := "project-list"
model.viewed-project := void
projector.schedule-render!
model.project-list := model.project-list.filter((.id != message.list))
# tasks
model.todod-ws.add-event-listener \tasks, (message) ->
console.log "Tasks received", message
project = model.project-list.find((.id == message.list))
project.tasks := message.tasks.map (e) -> Task e, project, model.todod-ws, model.users-cache
if model.viewed-project && model.viewed-project.id == project.id
model.viewed-project := project
projector.schedule-render!
model.todod-ws.add-event-listener \task-created, (message) ->
console.log "A task has been created", message
task = message.task
list = model.project-list.find((.id == task.list))
if model.viewed-project && list.id == model.viewed-project.id
model.viewed-project := list
if list
list.tasks ++= [ Task task, list, model.todod-ws, model.users-cache ]
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
Task task, list, model.todod-ws, model.users-cache
else
e
projector.schedule-render!
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!
render-project-list = ->
h \div.cards-list model.project-list.map (project) ->
h \div.card.is-grey {
key: project.id
onclick: ->
model.current-view := "project"
model.viewed-project := project
model.todod-ws.subscribe project.id
model.todod-ws.get-list project.id
model.todod-ws.get-tasks project.id
} [
h \div.card-content [
bulma.title 3 project.title
]
]
render-project = (project) ->
if project
project.render!
else
h \div.notification.is-error [
bulma.title 3 "Error, we did not get the project id " + project.id
]
model.login-form = LoginForm {
authws-url: model.authd-url
schedule-render: -> projector.schedule-render!
on-login: (user, token) ->
# FIXME: May double-login if the user clicks the “login” button too much.
if model.current-view != "login"
return
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!
}
navbar = Navbar!
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
]
when "project-list"
render-project-list!
when "project"
render-project model.viewed-project
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 ]
]
]
]
else
h \div.notification.is-error [
"Wait, what? Internal error!"
]
]
if model.modal
model.modal.render!
]
document.add-event-listener 'DOMContentLoaded' ->
projector.append document.body, render-body