Merge branch 'dev' of ssh://git.karchnu.fr:2202/WeirdOS/todo-webclient into dev

This commit is contained in:
Luka Vandervelden 2019-12-12 01:25:25 +01:00
commit 6f53a71e02
8 changed files with 326 additions and 144 deletions

View File

@ -6,12 +6,23 @@ module.exports = {
create-socket: (socket-url) ->
self = {}
self.token = ""
request-types = {
"get-token": 0
"add-user": 1
"get-user": 2
"get-user-by-credentials": 3
"mod-user": 4
# TODO: code these messages
"register": 5
"get-extra": 6
"set-extra": 7
"update-password": 8
"list-users": 9
"set-permissions": 10
}
response-types = {
@ -20,6 +31,9 @@ module.exports = {
"user": 2
"user-added": 3
"user-edited": 4
"extra": 5
"extra-updated": 6
"users-list": 7
}
# TODO: naming convention
@ -92,6 +106,23 @@ module.exports = {
uid: uid
}
self.list-users = ->
self.send request-types[\list-users], JSON.stringify {
token: self.token
# FIXME: this will be removed once the authd program will accept
# any list-users requests from any user
# which is the only logical choice since any user can create projects and assign people to it
key: "nico-nico-nii"
}
self.set-permissions = (list-id, user-id, permission) ->
self.send request-types[\set-permissions], JSON.stringify {
token: self.token
list: list-id
uid: user-id
permission: permission
}
# TODO: authd overhaul
#self.add-user = (login, password) ->
# self.send request-types[\add-user], JSON.stringify {

View File

@ -1,41 +1,8 @@
# first:
# connection to authd
# display the authd widget
#
# second:
# connection to todod
# rewrite the whole body component
# page 1: (TODO) list of lists (or todos)
# display:
# navbar
#
# "element 1" button
# "element 2" button
# ...
# "element x" button
#
# "new list" button
# widgets:
# navbar: [return to list button] [logout]
# element button:
# [project list name]
# or
# [project list name] [RIGTHS] [DELETE]
#
# page 2:
# display:
# (TODO) list of tasks (this client has a kanban display of tasks, with columns)
# (TODO) task widget
# widgets:
# task widget:
# TODO
#
# page 3: (TODO) pages about
maquette = require "maquette"
nmd = require "nano-markdown"
authd = require "./authd.ls"
LoginForm = require "../lib/authd/client/login-form.ls"
todows = require "./todowebsocket.ls"
bulma = require "./bulma.ls"
Task = require "./task.ls"
@ -49,16 +16,10 @@ projector = create-projector!
model = {
# view: login, project-list, project, network-error (TODO: other views, such as rights)
# XXX FIXME TODO: TESTING THINGS
current-view: "login"
viewed-project: void
# current-view: "testing-modals"
# { uid => user data }
users: {}
# list of Project objects
project-list: []
@ -71,6 +32,9 @@ model = {
previous-error: undefined
error: undefined
# { uid => user data }
users: {}
}
model.authd-url =
@ -122,22 +86,30 @@ model.authd-ws.user-on-socket-close ++= [ authd-on-websocket-close ]
# authd message handlers
model.authd-ws.add-event-listener \token, (message) ->
model.current-view := "project-list"
model.todod-ws.token := message.token
model.todod-ws.list-lists!
projector.schedule-render!
# 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
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!
# TODO: user-added, user-edited
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
#
@ -167,7 +139,7 @@ model.todod-ws.add-event-listener \lists-list, (message) ->
model.project-list := message.lists.map (x) ->
old-project = model.project-list.find((.id == x.id))
new-project = Project x, model.todod-ws
new-project = Project x, model.todod-ws, model.users
if old-project && new-project.id == old-project.id
new-project.tasks = old-project.tasks
@ -179,13 +151,13 @@ model.todod-ws.add-event-listener \lists-list, (message) ->
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.project-list := model.project-list ++ [ (Project message.list, model.todod-ws, model.users) ]
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
new-project = Project message.list, model.todod-ws, model.users
model.project-list := model.project-list.map (project) ->
if project.id == message.list.id
@ -268,22 +240,24 @@ render-navbar = ->
h \div.navbar-start [
h \a.navbar-item.is-size-2 {
onclick: ->
model.todod-ws.unsubscribe model.viewed-project.id
if model.viewed-project
model.todod-ws.unsubscribe model.viewed-project.id
model.todod-ws.list-lists!
model.viewed-project := void
model.current-view := "project-list"
} [ "⌂" ]
]
if model.viewed-project
if model.current-view == "project" && model.viewed-project
model.viewed-project.inner-nav-render!
h \div.navbar-end [
if model.viewed-project
if model.current-view == "project" && model.viewed-project
model.viewed-project.right-nav-render!
h \a.navbar-item {
key: "logout"
onclick: ->
model.current-view := "login"
# TODO: remove anything related to the old session on the client
@ -336,11 +310,23 @@ render-project = (project) ->
bulma.title 3 "Error, we did not get the project id " + project.id
]
model.login-form = LoginForm {
authws-url: model.authd-url
on-login: (user, token) ->
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!
}
render-body = ->
h \div.section [
switch model.current-view
when "login"
authd.login-page model
# authd.login-page model
model.login-form.render!
when "project-list"
h \div#project-list [

View File

@ -24,18 +24,37 @@ col-to-lines = (column, self) ->
} [ "DELETE" ]
]
# <div class="field has-addons">
# <p class="control">
# <input class="input" type="text" placeholder="Your email">
# </p>
# <p class="control">
# <a class="button is-static">
# @gmail.com
# </a>
# </p>
# </div>
user-form-selection = (self, user) ->
h \option {
value: user.uid
} [ user.login ]
ProjectCreationModal = (project, todod-ws) ->
permission-groups =
"read"
"post"
"edit"
"admin"
permissions-add = (self, permission, user-id) ->
perm-list = self.permissions[permission]
if perm-list
is-already-there = perm-list.find (e) -> (""+ e) == ("" + user-id)
if is-already-there
console.log "user #{user-id} already in #{perm-list}"
else
perm-list ++= [ parseInt(user-id) ]
self.permissions[permission] := perm-list
# console.log "adding user #{user-id} to #{perm-list[0]}: #{perm-list}"
else
console.log "Cannot find #{permission} permissions, creating it"
self.permissions[permission] := user-id
permission-to-form-selection = (self, permission) ->
h \option {
value: permission
} [ permission ]
ProjectCreationModal = (project, todod-ws, users) ->
# work on a copy of the columns
# in case of cancelled modifications, only the copies are changed
@ -48,11 +67,15 @@ ProjectCreationModal = (project, todod-ws) ->
self = {
title: project.title || ""
permissions: project.permissions || [[]]
new-user: "New user"
new-column-input: {
title: "New column !"
}
permissions: project.permissions || {admin: [], edit: [], post: [], read: []}
tmp:
new-user-permission:
id: void
permission: permission-groups[0]
new-column-input:
title: "New column !"
users: users || []
extra_properties:
columns: columns-copy
@ -73,29 +96,30 @@ ProjectCreationModal = (project, todod-ws) ->
}
]
h \hr []
# h \hr []
bulma.field [
bulma.label "Adding a user"
bulma.input {
value: self.new-user
oninput: (e) ->
self.new-user := e.target.value
name: \new-user
id: \user-add
}
]
# bulma.field [
# bulma.label "Adding a user"
# bulma.input {
# value: self.new-user
# oninput: (e) ->
# self.new-user := e.target.value
# name: \new-user
# id: \user-add
# }
# ]
h \hr []
h \aside.menu [
h \p.menu-label [ "Permissions" ]
h \ul.menu-list self.permissions.map (permission) ->
h \li [
h \p permission.map (e, index) ->
h \ul.menu-list Object.keys(self.permissions).map (permission) ->
h \li {
key: "permission" + permission
} [
h \p self.permissions[permission].map (e, index) ->
if index == 0
"Permissions '" + e + "': "
"Permissions '" + permission + "': " + e + ", "
else
"" + e + ", "
]
@ -103,6 +127,43 @@ ProjectCreationModal = (project, todod-ws) ->
h \hr []
h \p [ "Adding new user" ]
h \div.field.has-addons {
key: "adding-user"
} [
h \div.select.control [
h \select {
onchange: (e) ->
self.tmp.new-user-permission.permission := e.target.value
} permission-groups.map (permission) -> permission-to-form-selection self, permission
]
h \div.select.control.is-expanded [
h \select {
onchange: (e) ->
self.tmp.new-user-permission.uid := e.target.value
} [
user-form-selection self, { login: "Choose a user", uid: "-" }
for user-id, user of self.tmp.users
user-form-selection self, user
]
]
h \div.control.button.is-success.is-outlined {
onclick: ->
if self.tmp.new-user-permission.uid == void || self.tmp.new-user-permission.uid == "-"
console.log "adding an user permission on the kanban: failed, no user selected"
else
# TODO:
# adding the permissions in self.permissions
# then editing the project in the db via todod-ws
permissions-add self, self.tmp.new-user-permission.permission, self.tmp.new-user-permission.uid
} [ "+" ]
]
h \hr []
h \p [ "Choose the columns" ]
for dom in (self.extra_properties.columns.map (column) -> col-to-lines column, self)
@ -115,9 +176,9 @@ ProjectCreationModal = (project, todod-ws) ->
} [
h \p.control.is-expanded [
h \input.input {
value: self.new-column-input.title
value: self.tmp.new-column-input.title
oninput: (e) ->
self.new-column-input.title := e.target.value
self.tmp.new-column-input.title := e.target.value
} [ ]
]
@ -125,20 +186,21 @@ ProjectCreationModal = (project, todod-ws) ->
onclick: ->
new-col = {
id: UUID!
title: self.new-column-input.title
title: self.tmp.new-column-input.title
}
self.extra_properties.columns ++= [ new-col ]
self.new-column-input.title := "New column !"
self.tmp.new-column-input.title := "New column !"
} [ "+" ]
]
]
on-validation: ->
tmp = delete self.tmp
if project.id
todod-ws.edit-list project.id, self
else
todod-ws.add-list self.title, self
self.tmp := tmp
}, self
self.render = ->

View File

@ -4,7 +4,7 @@ bulma = require "./bulma.ls"
Task = require "./task.ls"
Modal = require './modal.ls'
TaskCreationModal = require './task-creation-modal.ls'
ProjectCreationModal = require "./project-creation-modal.ls"
ProjectCreationModal = require './project-creation-modal.ls'
is-right-column = (task, column-id) ->
task.extra_properties && task.extra_properties.column && task.extra_properties.column == column-id
@ -20,9 +20,10 @@ orphan-tasks = (tasks, columns) ->
tasks.filter (task) -> (! has-column) || inexistant-column task, columns
Project = (self, todod-ws) ->
Project = (self, todod-ws, users) ->
self.todod-ws = todod-ws
self.tasks = []
self.users = users || []
modal = void
@ -33,6 +34,10 @@ Project = (self, todod-ws) ->
if first
tasks-to-display ++= orphan-tasks self.tasks, self.extra_properties.columns
#
# COLUMNS
#
h \div.column {
key: column.id
} [
@ -52,21 +57,27 @@ Project = (self, todod-ws) ->
self.right-nav-render = ->
[
h \div.navbar-item [
h \div.navbar-item {
key: "navbar-new-task"
} [
h \div.button.is-success.is-outlined {
onclick: ->
modal := TaskCreationModal self, self.todod-ws
modal := TaskCreationModal self, self.todod-ws, void, self.users
} [ "New task" ]
]
h \div.navbar-item [
h \div.navbar-item {
key: "navbar-edit-project"
} [
h \div.button.is-outlined {
onclick: ->
modal := ProjectCreationModal self, self.todod-ws
modal := ProjectCreationModal self, self.todod-ws, self.users
} [ "Edit this project" ]
]
h \div.navbar-item [
h \div.navbar-item {
key: "navbar-delete-project"
} [
h \div.button.is-danger.is-outlined {
onclick: ->
modal := Modal {

View File

@ -1,14 +1,14 @@
@charset "utf-8"
@import "../node_modules/bulmaswatch/superhero/_variables.scss"
// @import "../node_modules/bulmaswatch/superhero/_variables.scss"
// Import Bulma core
@import "../node_modules/bulma/bulma"
@import "../node_modules/bulmaswatch/superhero/_overrides.scss"
// @import "../node_modules/bulmaswatch/superhero/_overrides.scss"
.project>.columns
overflow-x: scroll
overflow-x: auto
.avatar
border-radius: 4px

View File

@ -1,7 +1,30 @@
# TODO: on modification, the description isn't updated on the client
h = require 'maquette' .h
Modal = require './modal.ls'
colors = [
"white"
"black"
"light"
"dark"
"primary"
"info"
"link"
"success"
"warning"
"danger"
"black-bis"
"black-ter"
"grey-darker"
"grey-dark"
"grey"
"grey-light"
"grey-lighter"
"white-ter"
"white-bis"
]
column-form-selection = (self, column) ->
h \option {
@ -9,32 +32,57 @@ column-form-selection = (self, column) ->
selected: self.extra_properties && self.extra_properties.column == column.id
} [ column.title ]
color-to-form-selection = (self, color, current-color) ->
h \option {
value: color
selected: current-color && current-color == color
} [ color ]
TaskCreationModal = (project, todod-ws, task) ->
user-form-selection = (self, user) ->
h \option {
value: user.uid
} [ user.login ]
TaskCreationModal = (project, todod-ws, task, users) ->
task ||= {}
# copy not to override anything on cancel
self = {
title: task.title || ""
description: task.description || ""
extra_properties: {
extra_properties:
column: ""
}
background-color: ""
assignee-id: task.assignee-id || void
tmp:
users: users || []
}
if task.extra_properties && task.extra_properties.column
self.extra_properties.column = task.extra_properties.column
# copy extra properties
# currently: column + background-color + assignee + expected duration time
for k,v of task.extra_properties
self.extra_properties[k] = v
modal = Modal {
+visible
content-render: (self) ->
h \div.form [
#
# TITLE
#
h \input.input {
value: self.title
oninput: (e) ->
self.title := e.target.value
}
#
# DESCRIPTION
#
h \textarea {
value: self.description
oninput: (e) ->
@ -51,13 +99,52 @@ TaskCreationModal = (project, todod-ws, task) ->
} project.extra_properties.columns.map (column) -> column-form-selection self, column
]
#
# USER MANAGEMENT
#
h \hr []
h \p [ "Assign someone to the task" ]
h \div.field.has-addons {
key: "assign-someone-to-task"
} [
h \div.select.control [
h \select {
onchange: (e) ->
self.extra_properties.assignee-id := e.target.value
} [
user-form-selection self, { login: "Choose a user", uid: "-" }
for user-id, user of self.tmp.users
user-form-selection self, user
]
]
]
#
# BACKGROUND COLOR
#
h \p [ "Choose the background color" ]
h \div.select [
h \select {
onchange: (e) ->
self.extra_properties.background-color := e.target.value
} colors.map (color) -> color-to-form-selection self, color, self.extra_properties.background-color
]
]
on-validation: ->
tmp = delete self.tmp
if task.id
todod-ws.edit-task task.id, self
else
todod-ws.add-task project.id, self.title, self
self.tmp = tmp
}, self
self.render = ->

View File

@ -1,62 +1,53 @@
#
# Tasks, previous version of todos
#
h = require 'maquette' .h
bulma = require "./bulma.ls"
nmd = require "nano-markdown"
TaskCreationModal = require './task-creation-modal.ls'
TaskRemovalModal = require './task-removal-modal.ls'
#
# generic functions
#
get-previous = (collection, element) ->
var previous
for item in collection
if item == element
return previous
previous = item
get-next = (collection, element) ->
var found-element
for item in collection
if found-element
return item
if item == element
found-element := true
display-login = (task, users) ->
if task.extra_properties && task.extra_properties.assignee-id && users && users[task.extra_properties.assignee-id] && users[task.extra_properties.assignee-id].login
h \p [ '@' + users[task.extra_properties.assignee-id].login ]
else
h \p [ '-' ]
Task = (self, project, todod-ws) ->
modal = void
self.render = ->
h \div.card { key: self.id } [
background-color = "grey"
if self.extra_properties && self.extra_properties.background-color
background-color = self.extra_properties.background-color
h "div.card.has-background-#{background-color}" {
key: self.id
} [
h \div.card-content [
h \div.media [
h \div.media-left [
# FIXME: assignee card image
"LEFT"
display-login self, project.users
]
h \div.media-content [
self.title
]
h \div.media-content [ self.title ]
h \div.button {
onclick: ->
modal := TaskCreationModal project, todod-ws, self
modal := TaskCreationModal project, todod-ws, self, project.users
} [ "Edit" ]
h \div.button {
h \div.button.is-danger {
onclick: ->
modal := TaskRemovalModal project.id, todod-ws, self
} [ "X" ]
]
h \div.content [
self.description
]
h \div.content {
key: "task-description-#{self.id}"
after-create: (dom) ->
dom.innerHTML = nmd self.description
} [ ]
]
if modal
modal.render!

View File

@ -68,10 +68,11 @@ module.exports = {
self.socket.onmessage = (event) ->
message = JSON.parse(event.data)
console.log "todod message received: ", message
parsed-message = JSON.parse(message.payload)
console.log "todod message #{message.mtype} received: ", parsed-message
for f in self.callbacks[message.mtype]
f JSON.parse(message.payload)
f parsed-message
self.reopen = ->
self.socket.close!
@ -80,37 +81,43 @@ module.exports = {
self.open-socket!
self.send = (type, opts) ->
console.log JSON.stringify { mtype: type, payload: opts }
console.log "sending message #{type} to todod: ", opts
# console.log JSON.stringify { mtype: type, payload: opts }
self.socket.send JSON.stringify { mtype: type, payload: opts }
self.list-lists = ->
self.send request-types[\list-lists], JSON.stringify {
token: self.token
id: "list-lists"
}
self.get-list = (list-id) ->
self.send request-types[\get-list], JSON.stringify {
token: self.token
list: list-id
id: "get-list"
}
self.get-tasks = (list-id) ->
self.send request-types[\get-tasks], JSON.stringify {
token: self.token
list: list-id
id: "get-tasks"
}
self.get-task = (task-id) ->
self.send request-types[\get-task], JSON.stringify {
token: self.token
task: task-id
id: "get-task"
}
self.remove-list = (list-id) ->
self.send request-types[\remove-list], JSON.stringify {
token: self.token
list: list-id
id: "remove-list"
}
# TODO: extra properties
@ -118,6 +125,7 @@ module.exports = {
payload = {
token: self.token
title: title
id: "add-list"
}
for key, value of opts
@ -129,6 +137,7 @@ module.exports = {
payload = {
token: self.token
list: list-id
id: "edit-list"
}
for key, value of opts
@ -140,6 +149,7 @@ module.exports = {
self.send request-types[\remove-task], JSON.stringify {
token: self.token
task: task-id
id: "remove-task"
}
# TODO: extra properties
@ -148,6 +158,7 @@ module.exports = {
token: self.token
list: list-id
title: title
id: "add-task"
}
for key, value of opts
@ -159,6 +170,7 @@ module.exports = {
payload = {
token: self.token
task: task-id
id: "edit-task"
}
for key, value of opts
@ -170,12 +182,14 @@ module.exports = {
self.send request-types[\subscribe], JSON.stringify {
token: self.token
list: list-id
id: "subscribe"
}
self.unsubscribe = (list-id) ->
self.send request-types[\unsubscribe], JSON.stringify {
token: self.token
list: list-id
id: "unsubscribe"
}
self