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 {
static get properties () {
return {
model: { type: Object, attribute: true }
model: { type: Object, attribute: true },
collapsed: { type: Boolean, attribute: false },
edit: { type: Boolean, attribute: false }
}
}
async initialize () {
this.collapsed = false
this.edit = false
if (!this.model) {
return
}
@ -22,6 +26,34 @@ export default class MUCTaskListView extends CustomElement {
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) {
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)

View File

@ -3,6 +3,8 @@ import { api } from '@converse/headless/core'
import tplMucTaskLists from './templates/muc-task-lists'
import { __ } from 'i18n'
import './styles/muc-task-lists.scss'
export default class MUCTaskListsView extends CustomElement {
static get properties () {
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 () {
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
const i18nDelete = __(LOC_task_list_delete)
return html`
<div class="">
Tasklist: ${tasklist.get('name')}
<a class="" title="${i18nDelete}"
@click=${el.deleteTaskList}
>
<converse-icon class="fa fa-trash-alt" size="1em"></converse-icon>
</a>
<div class="task-list-description">
${el.collapsed
? 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}
>
<converse-icon class="fa fa-trash-alt" size="1em"></converse-icon>
</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 class="">
${
repeat(tasks, (task) => task.get('id'), (task) => {
<div class="task-list-tasks">
${el.collapsed
? ''
: repeat(tasks, (task) => task.get('id'), (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)
return html`
<div class="">
${
repeat(tasklists, (tasklist) => tasklist.get('id'), (tasklist) => {
return html`<livechat-converse-muc-task-list .model=${tasklist}></livechat-converse-muc-task-list>`
})
}
</div>
${
repeat(tasklists, (tasklist) => tasklist.get('id'), (tasklist) => {
return html`<livechat-converse-muc-task-list .model=${tasklist}></livechat-converse-muc-task-list>`
})
}
<form class="converse-form" @submit=${el.submitCreateTaskList}>
<div class="form-group">
<label>

View File

@ -92,21 +92,31 @@ export class PubSubManager {
}
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]
async saveItem (item) {
const id = item.get('id')
if (!id) {
throw new Error('Can\'t delete an empty without ID')
}
const item = $build('item').c(type.itemTag, attributes)
for (const fieldName in type.fields ?? []) {
if (!(fieldName in data)) { continue }
item.c(fieldName).t(data[fieldName]).up()
const type = this._typeFromCollection(item.collection)
if (!type) {
throw new Error('Collection not found in manager')
}
await api.pubsub.publish(this.roomJID, this.node, item)
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}.`)
}
@ -135,6 +145,29 @@ export class PubSubManager {
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.
*/