Real-time sync for projects, cards & cardstags

This commit is contained in:
Brieuc Dubois 2024-01-21 00:55:42 +01:00
parent 1991f26322
commit 2fd7134b68
6 changed files with 155 additions and 72 deletions

View File

@ -78,8 +78,9 @@ func CreateCardTag(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "cardtag", "object": "cardTag",
"action": "create", "action": "create",
"card_id": cardID,
"data": cardtag, "data": cardtag,
"X-Request-Source": source, "X-Request-Source": source,
}); });
@ -172,9 +173,10 @@ func DeleteCardTag(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "cardtag", "object": "cardTag",
"action": "delete", "action": "delete",
"id": cardID, "card_id": cardID,
"tag_id": tagID,
"X-Request-Source": source, "X-Request-Source": source,
}); });
@ -272,7 +274,7 @@ func UpdateCardTag(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "cardtag", "object": "cardTag",
"action": "update", "action": "update",
"card_id": cardID, "card_id": cardID,
"tag_id": tagID, "tag_id": tagID,

View File

@ -48,7 +48,7 @@ func CreateTag(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "tag", "object": "projectTag",
"action": "create", "action": "create",
"data": tag, "data": tag,
"X-Request-Source": source, "X-Request-Source": source,
@ -106,7 +106,7 @@ func DeleteTag(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "tag", "object": "projectTag",
"action": "delete", "action": "delete",
"id": id, "id": id,
"X-Request-Source": source, "X-Request-Source": source,
@ -149,7 +149,7 @@ func UpdateTag(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "tag", "object": "projectTag",
"action": "update", "action": "update",
"id": id, "id": id,
"changes": tag, "changes": tag,
@ -202,7 +202,7 @@ func CreateTagOption(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "tagoption", "object": "tagOption",
"action": "create", "action": "create",
"data": option, "data": option,
"X-Request-Source": source, "X-Request-Source": source,
@ -284,7 +284,7 @@ func DeleteTagOption(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "tagoption", "object": "tagOption",
"action": "delete", "action": "delete",
"tag_id": tagID, "tag_id": tagID,
"option_id": optionID, "option_id": optionID,
@ -344,7 +344,7 @@ func UpdateTagOption(c *fiber.Ctx) error {
} }
publish(fiber.Map{ publish(fiber.Map{
"object": "tagoption", "object": "tagOption",
"action": "update", "action": "update",
"tag_id": tagID, "tag_id": tagID,
"option_id": optionID, "option_id": optionID,

View File

@ -152,7 +152,7 @@ export default class Card {
return true; return true;
} }
updateFromDict(dict: any) { parseUpdate(dict: any) {
if (dict.project_id && dict.project_id !== this._project.id) { if (dict.project_id && dict.project_id !== this._project.id) {
this._project = Project.fromId(dict.project_id) as Project; this._project = Project.fromId(dict.project_id) as Project;
} }
@ -160,6 +160,31 @@ export default class Card {
if (dict.content) this._content = dict.content; if (dict.content) this._content = dict.content;
} }
static parseDelete(id: any) {
cards.update((cards) => cards.filter((c) => c.id !== id));
}
parseTag(dict: any) {
const cardTag = CardTag.parse(dict, this);
if (!cardTag) return;
this._cardTags = [...this._cardTags, cardTag];
}
parseTagUpdate(dict: any) {
const cardTag = this._cardTags.find((ct) => ct.projectTag.id === dict.tag_id);
if (!cardTag) {
toastAlert('Failed to parse card tag update: card tag not found');
return;
}
cardTag.parseUpdate(dict);
}
parseTagDelete(id: any) {
this._cardTags = this._cardTags.filter((ct) => ct.projectTag.id !== id);
}
static parse(json: any): Card | null; static parse(json: any): Card | null;
static parse(json: any, project: Project | null | undefined): Card | null; static parse(json: any, project: Project | null | undefined): Card | null;

View File

@ -71,6 +71,35 @@ export default class CardTag {
return true; return true;
} }
parseUpdate(dict: any) {
if (dict.card_id && dict.card_id !== this._card.id) {
toastAlert('Failed to parse card tag update: card id mismatch');
return;
}
if (dict.tag_id && dict.tag_id !== this._projectTag.id) {
toastAlert('Failed to parse card tag update: projectTag id mismatch');
return;
}
if (dict.option_id) {
const option = this._projectTag.options.find((option) => option.id === dict.option_id);
if (!option) {
toastAlert('Failed to parse card tag update: option not found');
return;
}
this._option = option;
} else if (this._option) {
this._option = null;
}
if (dict.value) {
this._value = dict.value;
} else if (this._value) {
this._value = null;
}
}
static parse(json: any): CardTag | null; static parse(json: any): CardTag | null;
static parse(json: any, card: Card | null | undefined): CardTag | null; static parse(json: any, card: Card | null | undefined): CardTag | null;

View File

@ -65,10 +65,14 @@ export default class Project {
return true; return true;
} }
updateFromDict(dict: any) { parseUpdate(dict: any) {
if (dict.title) this._title = dict.title; if (dict.title) this._title = dict.title;
} }
static parseDelete(id: any) {
projects.update((projects) => projects.filter((project) => project.id !== id));
}
static parse(json: any): Project | null { static parse(json: any): Project | null {
if (!json) return null; if (!json) return null;

View File

@ -1,4 +1,5 @@
import Card, { cards } from '$lib/types/Card'; import Card, { cards } from '$lib/types/Card';
import CardTag from '$lib/types/CardTag';
import Project, { projects } from '$lib/types/Project'; import Project, { projects } from '$lib/types/Project';
import ProjectTag, { projectTags } from '$lib/types/ProjectTag'; import ProjectTag, { projectTags } from '$lib/types/ProjectTag';
import View, { views } from '$lib/types/View'; import View, { views } from '$lib/types/View';
@ -78,77 +79,99 @@ export default class WebSocketManager {
} }
function applyMessage(data: any) { function applyMessage(data: any) {
switch (data.object) { console.debug('WebSocket message:', data);
case 'project': if (!data.object) {
applyProject(data); toastWarning('Failed to parse WebSocket message: missing object');
break; return;
case 'card':
applyCard(data);
break;
case 'view':
applyView(data);
break;
case 'projectTag':
applyProjectTag(data);
break;
default:
console.log('Unknown object:', data);
} }
if (!data.action) {
toastWarning('Failed to parse WebSocket message: missing action');
return;
}
if (data.object === 'project') applyProject(data);
else if (data.object === 'cardTag') applyCardTag(data);
else if (data.object === 'card') applyCard(data);
else if (data.object === 'view') applyView(data);
else if (data.object === 'projectTag') applyProjectTag(data);
else if (data.object === 'filter') applyFilter(data);
else toastWarning('Failed to parse WebSocket message: unknown object');
} }
function applyProject(data: any) { function applyProject(data: any) {
switch (data.action) { if (data.action === 'create') {
case 'create':
Project.parse(data.data); Project.parse(data.data);
case 'update': return;
get(projects)
.find((p) => p.id === data.id)
?.updateFromDict(data.changes);
projects.reload();
break;
case 'delete':
projects.set(get(projects).filter((p) => p.id !== data.id));
break;
} }
if (data.action === 'delete') {
Project.parseDelete(data.id);
return;
}
const project = Project.fromId(data.id);
if (!project) {
toastWarning('Failed to parse project update: project not found');
return;
}
if (data.action !== 'update') {
toastWarning('Failed to parse project update: unknown action');
return;
}
project.parseUpdate(data.changes);
projects.reload();
} }
function applyCard(data: any) { function applyCard(data: any) {
switch (data.action) { if (data.action === 'create') {
case 'create':
Card.parse(data.data); Card.parse(data.data);
break; return;
case 'update': }
get(cards) if (data.action === 'delete') {
.find((c) => c.id === data.id) Card.parseDelete(data.id);
?.updateFromDict(data.changes); return;
}
const card = Card.fromId(data.id);
if (!card) {
toastWarning('Failed to parse card update: card not found');
return;
}
if (data.action !== 'update') {
toastWarning('Failed to parse card update: unknown action');
return;
}
card.parseUpdate(data.changes);
cards.reload(); cards.reload();
break;
case 'delete':
cards.set(get(cards).filter((c) => c.id !== data.id));
break;
}
} }
function applyView(data: any) { function applyCardTag(data: any) {
switch (data.action) { const card = Card.fromId(data.card_id);
case 'create': if (!card) {
case 'update': toastWarning('Failed to parse card tag update: card not found');
View.parse(data.value); return;
break;
case 'delete':
views.set(get(views).filter((v) => v.id !== data.id));
break;
}
} }
function applyProjectTag(data: any) { if (data.action === 'create') {
switch (data.action) { card.parseTag(data.data);
case 'create': } else if (data.action === 'update') {
case 'update': card.parseTagUpdate(data.changes);
ProjectTag.parse(data.value); } else if (data.action === 'delete') {
break; card.parseTagDelete(data.tag_id);
case 'delete': } else {
projectTags.set(get(projectTags).filter((t) => t.id !== data.id)); toastWarning('Failed to parse card tag update: unknown action');
break; return;
} }
cards.reload();
} }
function applyView(data: any) {}
function applyProjectTag(data: any) {}
function applyFilter(data: any) {}