maquette = require "maquette" nmd = require "nano-markdown" {create-projector, h} = maquette projector = create-projector! bulma = require "./bulma.ls" 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 Task = (self, project) -> self.render = -> author = model.users[self.author] if typeof(author) != "object" and author != "request sent" model.users[self.author] = "request sent" # FIXME: This should go directly to authd. socket.send JSON.stringify { type: "get-user" uid: self.author } assigned_to = model.users[self.assigned_to] if self.assigned_to and typeof(assigned_to) != "object" and assigned_to != "request sent" model.users[self.assigned_to] = "request sent" # FIXME: This should go directly to authd. socket.send JSON.stringify { type: "get-user" uid: self.assigned_to } is-selected = model.selected == self.id h (\div.card.is- + (self.color || "dark")), { key: self.id classes: { "is-selected": is-selected } onclick: -> model.selected := self.id } [ h \div.card-content [ h \div.media [ h \div.media-left [ h \img.image.is-48x48.avatar { alt: "user image" src: if typeof(assigned_to) == "object" assigned_to.avatar else "https://bulma.io/images/placeholders/96x96.png" } ] h \div.media-content [ if model.editing == self.id + ".title" h \input.input { value: self.title onchange: (e) -> model.editing := undefined socket.send JSON.stringify { type: "edit-task" project: project.id task: self.id title: e.target.value } } [ self.title ] else h \a [ bulma.title 4 self.title ] if typeof(model.users[self.assigned_to]) == "object" user = model.users[self.assigned_to] h \div.subtitle.is-6 [ "@" + (user.full_name || user.login) ] ] if ! is-selected && self.description != "" h \div.media-right {key: "description-icon"} [ h \span.icon.is-size-1 [ "🗎" ] ] if is-selected h \div.media-right {key: "edit"} [ h \a.small { onclick: -> if model.editing == self.id + ".title" model.editing := undefined else model.editing := self.id + ".title" } [ "Edit" ] ] if is-selected h \div.media-right {key: "delete"} [ h \a.small { onclick: -> model.editing := self.id + ".delete" } [ "Delete" ] ] ] if is-selected h \div.content { key: self.description after-create: (dom) -> dom.innerHTML = nmd self.description } [ if model.editing == self.id + ".description" h \form.form [ h \textarea.textarea { value: model.editing-data oninput: (e) -> model.editing-data := e.target.value } h \div.button.is-fullwidth { onclick: -> socket.send JSON.stringify { type: "edit-task" project: project.id task: self.id description: model.editing-data } model.editing-data := undefined model.editing := undefined } [ "Update" ] ] ] if is-selected h \span.button.is-small { onclick: -> model.editing-data := self.description model.editing := self.id + ".description" } [ "edit" ] ] if is-selected h \div.card-footer {key: "assign"} [ if model.editing == self.id + ".assigned_to" h \div.card-footer-item { key: "assign.clicked" } [ h \input.input { onchange: (e) -> model.editing := undefined socket.send JSON.stringify { type: "edit-task" project: project.id task: self.id assigned_to: Number e.target.value } } ] else h \a.card-footer-item { key: "assign" onclick: -> model.editing := self.id + ".assigned_to" } [ "Assign" ] ] if is-selected h \div.card-footer {key: "color"} [ if model.editing == self.id + ".color" h \div.card-footer-item { key: "color.clicked" } [ h \input.input { onchange: (e) -> model.editing := undefined socket.send JSON.stringify { type: "edit-task" project: project.id task: self.id color: e.target.value } } ] else h \a.card-footer-item { key: "assign" onclick: -> model.editing := self.id + ".color" } [ "Change Color" ] ] if is-selected h \div.card-footer {key: "move"} [ h \a.card-footer-item { key: "⇐" onclick: -> socket.send JSON.stringify { type: "edit-task" project: project.id task: self.id column: get-previous project.columns.map((.id)), self.column } } [ "⇐" ] if model.editing == self.id + ".delete" h \a.card-footer-item { key: "delete" } [ h \div.button.is-danger { onclick: -> socket.send JSON.stringify { type: "delete-task" project: project.id task: self.id } } [ "Delete! For real!" ] ] h \a.card-footer-item { key: "⇒" onclick: -> socket.send JSON.stringify { type: "edit-task" project: project.id task: self.id column: get-next project.columns.map((.id)), self.column } } [ "⇒" ] ] ] self Project = (self) -> self.tasks = self.tasks.map (e) -> Task e, self self.render-column = (column) -> h \div.column.is-3 { key: column.id } [ h \div.card.is-column-header { key: column.id } [ h \div.card-header [ if model.editing == column.id + ".title" h \input.input { type: "text", value: column.name onchange: (e) -> console.log "onchange??" model.editing := undefined socket.send JSON.stringify { type: "edit-column" project: self.id column: column.id name: e.target.value } } else h \div.card-header-title [ bulma.title 3 column.name ] h \a.card-header-icon { key: "edit" onclick: -> if model.editing == column.id + ".title" model.editing := undefined else model.editing := column.id + ".title" } [ "Edit" ] if self.tasks.filter((.column == column.id)).length == 0 h \a.card-header-icon { key: "delete" onclick: -> model.editing := column.id + ".delete" } [ "Delete" ] ] if model.editing == column.id + ".delete" h \div.card-content [ h \div.button.is-fullwidth.is-danger { onclick: -> socket.send JSON.stringify { type: "delete-column" project: self.id column: column.id } } [ "Delete me!"] ] ] for task in self.tasks continue if task.column != column.id task.render! h \div.button.is-fullwidth { onclick: -> socket.send JSON.stringify { type: "new-task" project: self.id column: column.id title: "General Kenobi…" description: "" } } [ "New task" ] ] self.render = -> h \div.project { key: self.id } [ h \div.hero.is-dark { key: "title" } [ h \div.hero-body [ # FIXME: Consider using a .level for this. h \div.is-pulled-right { onclick: -> model.editing := self.id + ".name" } [ "Edit" ] if model.editing == self.id + ".name" h \input.input { onchange: (e) -> model.editing := undefined socket.send JSON.stringify { type: "edit-project" project: self.id name: e.target.value } value: self.name } else h \div.title [ self.name ] ] ] h \div.columns [ for dom in self.columns.map((column) -> self.render-column(column)) dom h \div.column.is-2 { key: "new-column" } [ h \div.button.is-fullwidth { onclick: -> socket.send JSON.stringify { type: "new-column" project: self.id name: "Hello, there!" } } [ "New Column" ] ] ] ] self model = { current-view: "login" editing: undefined selected: undefined users: {} projects: {} projects-list: [] } protocol = if location.protocol == 'https' then 'wss' else 'ws' port = if location.port then \: + location.port else '' socket-url = protocol + '://' + location.hostname + port + "/socket" console.log socket-url socket = new WebSocket socket-url socket.onopen = (event) -> # Nothing to do here ATM. socket.onerror = (event) -> model.state = "websocket-error" projector.schedule-render! socket.onclose = (event) -> model.state = "websocket-error" model.websocket-error = event.reason projector.schedule-render! socket.onmessage = (event) -> console.log event.data message = JSON.parse event.data switch message.type when "login" model.current-view := "projects-list" when "list-projects" for project in message.projects model.projects[project.id] = Project project model.projects-list := message.projects when "project" model.projects[message.project.id] = Project message.project when "user" model.users[message.user.uid] := message.user else console.log "RECEIVED UNKNOWN MESSAGE TYPE: #{message.type}" projector.schedule-render! console.log message renderer = -> h \div.section [ switch model.current-view when "login" h \div.container [ h \div.box [ h \form [ bulma.field [ bulma.label "Login" bulma.input { oninput: (e) -> model.login = e.target.value name: \login id: \login-input } ] bulma.field [ bulma.label "Password" bulma.input { oninput: (e) -> model.password = e.target.value name: \password type: \password id: \password-input } ] h \button.button.is-fullwidth.is-primary { onclick: (e) -> e.prevent-default! socket.send JSON.stringify { type: "login", login: model.login password: model.password } } [ "Letsu go!" ] ] ] ] when "project" h \div [ h \div.navbar [ h \div.navbar-end [ h \a.navbar-item { onclick: -> model.viewed-project := undefined model.current-view := "projects-list" } [ "Go back" ] ] ] if model.projects[model.viewed-project] model.projects[model.viewed-project].render! ] when "projects-list" h \div#projects-list [ h \div (model.projects-list || []).map (project) -> h \a.box { key: project.id onclick: -> model.current-view := "project" model.viewed-project := project.id socket.send JSON.stringify { type: "project", project: project.id } } [ bulma.title 3 project.name ] h \div.button.is-primary.is-large.is-fullwidth { onclick: -> socket.send JSON.stringify { type: "new-project" name: "Hello, there!" } } [ "New project! (random atm)" ] ] else h \div.notification.is-error [ "Wait, what? Internal error!" ] ] document.add-event-listener 'DOMContentLoaded' -> projector.append document.body, renderer