Task lists WIP:

* task list template + form
This commit is contained in:
John Livingston 2024-05-06 12:29:25 +02:00
parent d16bdd9a87
commit 9d5d59e9bc
No known key found for this signature in database
GPG Key ID: B17B5640CE66CDBC
7 changed files with 185 additions and 28 deletions

View File

@ -6,11 +6,15 @@ import { __ } from 'i18n'
export default class MUCTaskListView extends CustomElement { export default class MUCTaskListView extends CustomElement {
static get properties () { static get properties () {
return { return {
model: { type: Object, attribute: true } model: { type: Object, attribute: true },
collapsed: { type: Boolean, attribute: false },
edit: { type: Boolean, attribute: false }
} }
} }
async initialize () { async initialize () {
this.collapsed = false
this.edit = false
if (!this.model) { if (!this.model) {
return return
} }
@ -22,6 +26,34 @@ export default class MUCTaskListView extends CustomElement {
return tplMucTaskList(this, this.model) return tplMucTaskList(this, this.model)
} }
async saveTaskList (ev) {
ev?.preventDefault?.()
const name = ev.target.name.value.trim()
if ((name ?? '') === '') { return }
try {
this.querySelectorAll('input[type=submit]').forEach(el => {
el.setAttribute('disabled', true)
el.classList.add('disabled')
})
const tasklist = this.model
tasklist.set('name', name)
await tasklist.saveItem()
this.edit = false
} catch (err) {
console.error(err)
} finally {
this.querySelectorAll('input[type=submit]').forEach(el => {
el.removeAttribute('disabled')
el.classList.remove('disabled')
})
}
}
async deleteTaskList (ev) { async deleteTaskList (ev) {
ev?.preventDefault?.() ev?.preventDefault?.()
@ -41,6 +73,24 @@ export default class MUCTaskListView extends CustomElement {
) )
} }
} }
toggleTasks () {
this.collapsed = !this.collapsed
}
async toggleEdit () {
this.edit = !this.edit
if (this.edit) {
await this.updateComplete
const input = this.querySelector('input[name="name"]')
if (input) {
input.focus()
// Placing cursor at the end:
input.selectionStart = input.value.length
input.selectionEnd = input.selectionStart
}
}
}
} }
api.elements.define('livechat-converse-muc-task-list', MUCTaskListView) api.elements.define('livechat-converse-muc-task-list', MUCTaskListView)

View File

@ -3,6 +3,8 @@ import { api } from '@converse/headless/core'
import tplMucTaskLists from './templates/muc-task-lists' import tplMucTaskLists from './templates/muc-task-lists'
import { __ } from 'i18n' import { __ } from 'i18n'
import './styles/muc-task-lists.scss'
export default class MUCTaskListsView extends CustomElement { export default class MUCTaskListsView extends CustomElement {
static get properties () { static get properties () {
return { return {

View File

@ -0,0 +1,33 @@
.conversejs {
livechat-converse-muc-task-lists {
padding: 1em;
width: 100%;
}
livechat-converse-muc-task-list {
width: 100%;
.task-list-description {
border: 1px solid var(--chatroom-head-bg-color);
display: flex;
flex-flow: row nowrap;
justify-content: space-around;
padding: 0.25em;
column-gap: 0.25em;
width: 100%;
button {
border: 0;
}
.task-list-name {
flex-grow: 2;
}
}
.task-list-tasks {
padding-left: 2em;
width: 100%;
}
}
}

View File

@ -16,6 +16,12 @@ class ChatRoomTaskList extends Model {
}) ?? [] }) ?? []
} }
async saveItem () {
console.log('Saving task list ' + this.get('id') + '...')
await this.collection.chatroom.taskManager.saveItem(this, { name })
console.log('Task list ' + this.get('id') + ' created.')
}
async deleteItem () { async deleteItem () {
return this.collection.chatroom.taskManager.deleteItem(this) return this.collection.chatroom.taskManager.deleteItem(this)
} }

View File

@ -7,17 +7,52 @@ export default function tplMucTaskList (el, tasklist) {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
const i18nDelete = __(LOC_task_list_delete) const i18nDelete = __(LOC_task_list_delete)
return html` return html`
<div class=""> <div class="task-list-description">
Tasklist: ${tasklist.get('name')} ${el.collapsed
<a class="" title="${i18nDelete}" ? html`
<button @click=${el.toggleTasks}>
<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa fa-angle-double-up"
size="1em"></converse-icon>
</button>`
: html`
<button @click=${el.toggleTasks}>
<converse-icon
color="var(--muc-toolbar-btn-color)"
class="fa fa-angle-double-down"
size="1em"></converse-icon>
</button>`
}
${!el.edit
? html`
<div class="task-list-name">
${tasklist.get('name')}
</div>
<a title="${__('Edit')}"
@click=${el.toggleEdit}
>
<converse-icon class="fa fa-edit" size="1em"></converse-icon>
</a>
<a title="${i18nDelete}"
@click=${el.deleteTaskList} @click=${el.deleteTaskList}
> >
<converse-icon class="fa fa-trash-alt" size="1em"></converse-icon> <converse-icon class="fa fa-trash-alt" size="1em"></converse-icon>
</a> </a>`
: html`
<div class="task-list-name">
<form @submit=${el.saveTaskList}>
<input type="text" name="name" autofocus value=${tasklist.get('name')} />
<input type="submit" class="btn btn-primary" value="${__('Ok')}" />
<input type="reset" class="btn btn-secondary" value="${__('Cancel')}" @click=${el.toggleEdit} />
</form>
</div>`
}
</div> </div>
<div class=""> <div class="task-list-tasks">
${ ${el.collapsed
repeat(tasks, (task) => task.get('id'), (task) => { ? ''
: repeat(tasks, (task) => task.get('id'), (task) => {
return html`<livechat-converse-muc-task .model=${task}></livechat-converse-muc-task>` return html`<livechat-converse-muc-task .model=${task}></livechat-converse-muc-task>`
}) })
} }

View File

@ -14,13 +14,11 @@ export default function tplMucTaskLists (el, tasklists) {
const i18nTaskListName = __(LOC_task_list_name) const i18nTaskListName = __(LOC_task_list_name)
return html` return html`
<div class="">
${ ${
repeat(tasklists, (tasklist) => tasklist.get('id'), (tasklist) => { repeat(tasklists, (tasklist) => tasklist.get('id'), (tasklist) => {
return html`<livechat-converse-muc-task-list .model=${tasklist}></livechat-converse-muc-task-list>` return html`<livechat-converse-muc-task-list .model=${tasklist}></livechat-converse-muc-task-list>`
}) })
} }
</div>
<form class="converse-form" @submit=${el.submitCreateTaskList}> <form class="converse-form" @submit=${el.submitCreateTaskList}>
<div class="form-group"> <div class="form-group">
<label> <label>

View File

@ -92,21 +92,31 @@ export class PubSubManager {
} }
console.log('Creating item...') console.log('Creating item...')
const attributes = { xmlns: type.xmlns } await this._save(type, data)
console.log(`Node ${this.node} created on ${this.roomJID}.`)
for (const attrName in type.attributes ?? []) {
if (!(attrName in data)) { continue }
attributes[attrName] = data[attrName]
} }
const item = $build('item').c(type.itemTag, attributes) async saveItem (item) {
const id = item.get('id')
for (const fieldName in type.fields ?? []) { if (!id) {
if (!(fieldName in data)) { continue } throw new Error('Can\'t delete an empty without ID')
item.c(fieldName).t(data[fieldName]).up()
} }
await api.pubsub.publish(this.roomJID, this.node, item) const type = this._typeFromCollection(item.collection)
if (!type) {
throw new Error('Collection not found in manager')
}
const data = {}
for (const attr in (type.attributes ?? [])) {
data[attr] = item.get(attr)
}
for (const field in (type.fields ?? [])) {
data[field] = item.get(field)
}
console.log('Saving item...')
await this._save(type, data, id)
console.log(`Node ${this.node} created on ${this.roomJID}.`) console.log(`Node ${this.node} created on ${this.roomJID}.`)
} }
@ -135,6 +145,29 @@ export class PubSubManager {
console.log('Item deleted.') console.log('Item deleted.')
} }
async _save (type, data, id) {
const itemAttributes = {}
if (id) {
itemAttributes.id = id
}
const attributes = { xmlns: type.xmlns }
for (const attrName in type.attributes ?? []) {
if (!(attrName in data)) { continue }
attributes[attrName] = data[attrName]
}
const item = $build('item', itemAttributes).c(type.itemTag, attributes)
for (const fieldName in type.fields ?? []) {
if (!(fieldName in data)) { continue }
item.c(fieldName).t(data[fieldName]).up()
}
await api.pubsub.publish(this.roomJID, this.node, item)
}
/** /**
* Subscribed to the pubsub node. * Subscribed to the pubsub node.
*/ */