WIP LiveScript login component.
This commit is contained in:
parent
16fa2271f6
commit
b1b502ff66
109
client/authws.ls
Normal file
109
client/authws.ls
Normal file
@ -0,0 +1,109 @@
|
||||
bulma = require "./bulma.ls"
|
||||
h = require 'maquette' .h
|
||||
|
||||
AuthWS = (socket-url) ->
|
||||
self = {}
|
||||
|
||||
request-types = {
|
||||
"get-token": 0
|
||||
"add-user": 1
|
||||
"get-user": 2
|
||||
"get-user-by-credentials": 3
|
||||
"mod-user": 4
|
||||
"register": 5
|
||||
}
|
||||
|
||||
response-types = {
|
||||
"error": 0
|
||||
"token": 1
|
||||
"user": 2
|
||||
"user-added": 3
|
||||
"user-edited": 4
|
||||
}
|
||||
|
||||
# TODO: naming convention
|
||||
# users can record functions to run on events
|
||||
self.user-on-socket-error = []
|
||||
self.user-on-socket-close = []
|
||||
|
||||
self.callbacks = {}
|
||||
for key, value of response-types
|
||||
self.callbacks[value] = []
|
||||
|
||||
self.add-event-listener = (type, callback) ->
|
||||
type = response-types[type]
|
||||
|
||||
self.callbacks[type] ++= [callback]
|
||||
|
||||
self.open-socket = ->
|
||||
self.socket := new WebSocket socket-url
|
||||
|
||||
self.socket.onerror = (event) ->
|
||||
for f in self.user-on-socket-error
|
||||
f event
|
||||
self.socket.close!
|
||||
|
||||
self.socket.onclose = (event) ->
|
||||
for f in self.user-on-socket-close
|
||||
f event
|
||||
|
||||
self.socket.onmessage = (event) ->
|
||||
message = JSON.parse(event.data)
|
||||
|
||||
for f in self.callbacks[message.mtype]
|
||||
f JSON.parse(message.payload)
|
||||
|
||||
self.reopen = ->
|
||||
self.socket.close!
|
||||
self.open-socket!
|
||||
|
||||
self.open-socket!
|
||||
|
||||
self.send = (type, opts) ->
|
||||
self.socket.send JSON.stringify { mtype: type, payload: opts }
|
||||
|
||||
self.get-token = (login, password) ->
|
||||
self.send request-types[\get-token], JSON.stringify {
|
||||
login: login
|
||||
password: password
|
||||
}
|
||||
|
||||
self.get-user-by-credentials = (login, password) ->
|
||||
self.send request-types[\get-user-by-credentials], JSON.stringify {
|
||||
login: login
|
||||
password: password
|
||||
}
|
||||
|
||||
self.login = (login, password) ->
|
||||
self.get-token login, password
|
||||
self.get-user-by-credentials login, password
|
||||
|
||||
|
||||
self.get-user = (uid) ->
|
||||
self.send request-types[\get-user], JSON.stringify {
|
||||
uid: uid
|
||||
}
|
||||
|
||||
self.register = (login, password) ->
|
||||
self.send request-types[\register], JSON.stringify {
|
||||
login: login
|
||||
password: password
|
||||
}
|
||||
|
||||
# TODO: authd overhaul required
|
||||
#self.add-user = (login, password) ->
|
||||
# self.send request-types[\add-user], JSON.stringify {
|
||||
# login: login
|
||||
# password: password
|
||||
# }
|
||||
|
||||
# TODO: authd overhaul required
|
||||
#self.mod-user = (uid) ->
|
||||
# self.send request-types[\mod-user], JSON.stringify {
|
||||
# uid: uid
|
||||
# }
|
||||
|
||||
self
|
||||
|
||||
module.exports = AuthWS
|
||||
|
39
client/bulma.ls
Normal file
39
client/bulma.ls
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
h = require 'maquette' .h
|
||||
|
||||
module.exports = {
|
||||
box: (args, children) ->
|
||||
h \div.box args, children
|
||||
title: (level, args, label) ->
|
||||
if not label
|
||||
label = args
|
||||
args = {}
|
||||
|
||||
h "div.title.is-#{level}", args, [label]
|
||||
label: (args, label) ->
|
||||
if not label
|
||||
label = args
|
||||
args = {}
|
||||
|
||||
h \label.label args, [label]
|
||||
input: (args, children) ->
|
||||
h \input.input args, children
|
||||
|
||||
# FIXME: Use only args and add args.label and args.input?
|
||||
# Or maybe args.name and args.type could be used directly?
|
||||
field: (args, children) ->
|
||||
h \div.field args, children
|
||||
|
||||
modal: (args, content) ->
|
||||
h \div.modal args, [
|
||||
h \div.modal-background args.background
|
||||
h \div.modal-content [args.content]
|
||||
]
|
||||
|
||||
form: (method, url, content) ->
|
||||
h \form.form {
|
||||
action: url
|
||||
method: method
|
||||
}, content
|
||||
}
|
||||
|
62
client/index.ls
Normal file
62
client/index.ls
Normal file
@ -0,0 +1,62 @@
|
||||
maquette = require "maquette"
|
||||
|
||||
{create-projector, h} = maquette
|
||||
|
||||
projector = create-projector!
|
||||
|
||||
bulma = require "./bulma.ls"
|
||||
|
||||
AuthWS = require "./authws.ls"
|
||||
|
||||
LoginForm = require "./login-form.ls"
|
||||
UserConfigurationPanel = require "./user-configuration-panel.ls"
|
||||
|
||||
model = {
|
||||
token: void
|
||||
}
|
||||
|
||||
document.add-event-listener \DOMContentLoaded ->
|
||||
user-config-panel = void
|
||||
|
||||
login-form = LoginForm {
|
||||
enable-registration: true
|
||||
authws-url: "ws://localhost:9999/auth.JSON"
|
||||
|
||||
on-login: (user, token) ->
|
||||
model.user := user
|
||||
model.token := token
|
||||
|
||||
user-config-panel := UserConfigurationPanel model.user, model.token
|
||||
|
||||
projector.schedule-render!
|
||||
on-error: (error) ->
|
||||
projector.schedule-render!
|
||||
}
|
||||
|
||||
projector.append document.body, ->
|
||||
h \div.body [
|
||||
if model.token == void
|
||||
h \div.section.hero.is-fullheight [
|
||||
h \div.hero-body [
|
||||
h \div.container [
|
||||
h \div.columns [
|
||||
h \div.column []
|
||||
h \div.column.is-3 [
|
||||
login-form.render!
|
||||
]
|
||||
h \div.column []
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
else if user-config-panel
|
||||
h \div.section [
|
||||
h \div.container [
|
||||
h \div.box [
|
||||
user-config-panel.render!
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
206
client/login-form.ls
Normal file
206
client/login-form.ls
Normal file
@ -0,0 +1,206 @@
|
||||
maquette = require "maquette"
|
||||
|
||||
{h} = maquette
|
||||
|
||||
bulma = require "./bulma.ls"
|
||||
|
||||
AuthWS = require "./authws.ls"
|
||||
|
||||
LoginForm = (args) ->
|
||||
args or= {}
|
||||
|
||||
self = {
|
||||
on-login: args.on-login || ->
|
||||
on-error: args.on-error || ->
|
||||
current-view: "login"
|
||||
|
||||
enable-registration: args.enable-registration || false
|
||||
registrating: false
|
||||
|
||||
input: {
|
||||
login: ""
|
||||
password: ""
|
||||
repeat-password: ""
|
||||
}
|
||||
locked-input: false
|
||||
|
||||
error: void
|
||||
|
||||
authws-url: args.authws-url ||
|
||||
((if location.protocol == 'https' then 'wss' else 'ws') +
|
||||
'://' + location.hostname + ":9999/auth.JSON")
|
||||
}
|
||||
|
||||
|
||||
auth-ws = AuthWS self.authws-url
|
||||
|
||||
auth-ws.add-event-listener \token, (message) ->
|
||||
self.error := void
|
||||
|
||||
self.token = message.token
|
||||
self.locked-input := false
|
||||
|
||||
if self.user
|
||||
self.on-login self.user, self.token
|
||||
|
||||
auth-ws.add-event-listener \user, (message) ->
|
||||
self.error := void
|
||||
|
||||
self.user = message.user
|
||||
|
||||
if self.token
|
||||
self.on-login self.user, self.token
|
||||
|
||||
auth-ws.add-event-listener \user-added, (message) ->
|
||||
{login, password} = {self.input.login, self.input.password}
|
||||
|
||||
console.log "user added, duh"
|
||||
|
||||
self.user := message.user
|
||||
|
||||
auth-ws.get-token login, password
|
||||
|
||||
auth-ws.add-event-listener \error, (message) ->
|
||||
# We’ll get another error that’s clearer. Dropping that one.
|
||||
if message.reason == "user not found"
|
||||
return
|
||||
|
||||
self.error := message.reason
|
||||
self.locked-input := false
|
||||
|
||||
self.on-error message.reason
|
||||
|
||||
self.render = ->
|
||||
h \form.form.login-form {
|
||||
key: self
|
||||
onsubmit: (e) ->
|
||||
{login, password} = {self.input.login, self.input.password}
|
||||
|
||||
self.locked-input := true
|
||||
|
||||
if self.registrating
|
||||
auth-ws.register login, password
|
||||
else
|
||||
auth-ws.get-token login, password
|
||||
auth-ws.get-user-by-credentials login, password
|
||||
|
||||
e.prevent-default!
|
||||
}, [
|
||||
h \div.field {key: \login} [
|
||||
bulma.label "Login"
|
||||
bulma.input {
|
||||
type: "text"
|
||||
id: "login"
|
||||
name: "login"
|
||||
classes: {
|
||||
"is-danger": self.error == "invalid credentials"
|
||||
}
|
||||
disabled: self.locked-input
|
||||
oninput: (e) ->
|
||||
self.input.login = e.target.value
|
||||
}
|
||||
]
|
||||
|
||||
h \div.field {key: \password} [
|
||||
bulma.label "Password"
|
||||
bulma.input {
|
||||
type: "password"
|
||||
id: "password"
|
||||
name: "password"
|
||||
classes: {
|
||||
"is-danger": self.error == "invalid credentials"
|
||||
}
|
||||
oninput: (e) ->
|
||||
self.input.password = e.target.value
|
||||
disabled: self.locked-input
|
||||
}
|
||||
]
|
||||
|
||||
if self.registrating
|
||||
h \div.field {key: \password-repeat} [
|
||||
bulma.label "Password (reapeat)"
|
||||
bulma.input {
|
||||
type: \password
|
||||
id: \password-repeat
|
||||
name: \password-repeat
|
||||
classes: {
|
||||
"is-danger": self.input.password != self.input.repeat-password
|
||||
}
|
||||
disabled: self.locked-input
|
||||
oninput: (e) ->
|
||||
self.input.repeat-password = e.target.value
|
||||
}
|
||||
]
|
||||
|
||||
if self.error
|
||||
h \div.field {key: \error-notification} [
|
||||
h \div.notification.is-danger [
|
||||
self.error
|
||||
]
|
||||
]
|
||||
|
||||
if self.registrating
|
||||
h \div.field.is-grouped {key: \login-button} [
|
||||
if self.input.login == ""
|
||||
h \button.button.is-static.is-fullwidth {
|
||||
type: \submit
|
||||
} [
|
||||
"(empty login)"
|
||||
]
|
||||
else if self.input.password != self.input.repeat-password
|
||||
h \button.button.is-static.is-fullwidth {
|
||||
type: \submit
|
||||
} [
|
||||
"(passwords don’t match)"
|
||||
]
|
||||
else if self.input.password == ""
|
||||
h \button.button.is-static.is-fullwidth {
|
||||
type: \submit
|
||||
} [
|
||||
"(empty password)"
|
||||
]
|
||||
else
|
||||
h \button.button.is-success.is-fullwidth {
|
||||
type: \submit
|
||||
} [
|
||||
"Register!"
|
||||
]
|
||||
]
|
||||
else
|
||||
h \div.field.is-grouped {key: \login-button} [
|
||||
h \button.button.is-fullwidth.is-success {
|
||||
type: \submit
|
||||
} [
|
||||
"Log in!"
|
||||
]
|
||||
]
|
||||
|
||||
h \div.field.level {key: \extra-buttons} [
|
||||
#h \div.level-left [
|
||||
# h \a.link [ "(lala, remember me?)" ]
|
||||
#]
|
||||
|
||||
if self.enable-registration
|
||||
h \div.level-right [
|
||||
if self.registrating
|
||||
h \a.link {
|
||||
onclick: (e) ->
|
||||
self.registrating := false
|
||||
} [
|
||||
"Log in"
|
||||
]
|
||||
else
|
||||
h \a.link {
|
||||
onclick: (e) ->
|
||||
self.registrating := true
|
||||
} [
|
||||
"Create account!"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
self
|
||||
|
||||
module.exports = LoginForm
|
||||
|
43
client/user-configuration-panel.ls
Normal file
43
client/user-configuration-panel.ls
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
{h} = require "maquette"
|
||||
|
||||
UserConfigurationPanel = (user, token) ->
|
||||
self = {}
|
||||
|
||||
console.log user
|
||||
|
||||
self.render = ->
|
||||
full-name = user.full_name
|
||||
if full-name == ""
|
||||
full-name = user.login
|
||||
|
||||
h \div.columns {
|
||||
key: self
|
||||
} [
|
||||
h \div.column.is-one-quarter [
|
||||
h \figure.image.is-128 [
|
||||
h \img {
|
||||
# FIXME
|
||||
url: "https://bulma.io/images/placeholders/128x128.png"
|
||||
alt: "Avatar of #{full-name}"
|
||||
}
|
||||
]
|
||||
]
|
||||
h \div.column [
|
||||
h \div.title.is-2 [ full-name ]
|
||||
|
||||
if full-name != user.login
|
||||
h \div.title.is-3.subtitle [
|
||||
user.login
|
||||
]
|
||||
|
||||
h \div.title.is-4 [ "Permissions" ]
|
||||
h \div.tags user.groups.map (group) ->
|
||||
h \div.tag [ group ]
|
||||
]
|
||||
]
|
||||
|
||||
self
|
||||
|
||||
module.exports = UserConfigurationPanel
|
||||
|
Loading…
Reference in New Issue
Block a user