From f2313e3f045b5f8a5c8cccff85995bf062d8ef01 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 20 Oct 2023 12:18:46 -0700 Subject: [PATCH 1/9] Add messaging topics route --- .../messaging/topics/+page.svelte | 160 ++++++++++++++++++ .../messaging/topics/+page.ts | 62 +++++++ .../messaging/topics/create.svelte | 68 ++++++++ .../messaging/topics/store.ts | 11 ++ 4 files changed, 301 insertions(+) create mode 100644 src/routes/console/project-[project]/messaging/topics/+page.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/+page.ts create mode 100644 src/routes/console/project-[project]/messaging/topics/create.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/store.ts diff --git a/src/routes/console/project-[project]/messaging/topics/+page.svelte b/src/routes/console/project-[project]/messaging/topics/+page.svelte new file mode 100644 index 0000000000..1a990e8b88 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/+page.svelte @@ -0,0 +1,160 @@ + + + +
+
+ Topics +
+ +
+
+ + +
+ + + +
+
+
+
+ + +
+
+ + +
+
+
+ {#if data.topics.total} + + + d.$id)} /> + {#each $columns as column} + {#if column.show} + {column.title} + {/if} + {/each} + + + {#each data.topics.topics as topic (topic.$id)} + + + + {#each $columns as column (column.id)} + {#if column.show} + {#if column.id === '$id'} + {#key $columns} + + {topic.$id} + + {/key} + {:else if column.type === 'datetime'} + + {#if !topic[column.id]} + - + {:else} + {toLocaleDateTime(topic[column.id])} + {/if} + + {:else} + + {topic[column.id]} + + {/if} + {/if} + {/each} + + {/each} + + + + + + {:else if data.search && data.search != 'empty'} + +
+ Sorry, we couldn't find '{data.search}' +

There are no topics that match your search.

+
+ +
+ {:else} + + ($showCreate = true)} + href="https://appwrite.io/docs/references/cloud/client-web/teams" + target="topic" /> + {/if} +
+ + + diff --git a/src/routes/console/project-[project]/messaging/topics/+page.ts b/src/routes/console/project-[project]/messaging/topics/+page.ts new file mode 100644 index 0000000000..ead3791dfa --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/+page.ts @@ -0,0 +1,62 @@ +import { Query } from '@appwrite.io/console'; +import { sdk } from '$lib/stores/sdk'; +import { getLimit, getPage, getQuery, getSearch, pageToOffset } from '$lib/helpers/load'; +import { PAGE_LIMIT } from '$lib/constants'; +import { queryParamToMap, queries } from '$lib/components/filters/store'; + +// TODO: remove when sdk has the model +export type Topic = { + $id: string; + $createdAt: string; + $updatedAt: string; + providerId: string; + name: string; + total: number; + description: string; +}; + +export const load = async ({ url, route }) => { + const page = getPage(url); + const search = getSearch(url); + const limit = getLimit(url, route, PAGE_LIMIT); + const offset = pageToOffset(page, limit); + const query = getQuery(url); + + const parsedQueries = queryParamToMap(query || '[]'); + queries.set(parsedQueries); + + const payload = { + queries: [ + Query.limit(limit), + Query.offset(offset), + Query.orderDesc(''), + ...parsedQueries.values() + ] + }; + + if (search) { + payload['search'] = search; + } + + // TODO: remove when the API is ready with data + // This allows us to mock w/ data and when search returns 0 results + const topics: { topics: Topic[]; total: number } = await sdk.forProject.client.call( + 'GET', + new URL(sdk.forProject.client.config.endpoint + '/messaging/topics'), + { + 'X-Appwrite-Project': sdk.forProject.client.config.project, + 'content-type': 'application/json', + 'X-Appwrite-Mode': 'admin' + }, + payload + ); + + return { + offset, + limit, + search, + query, + page, + topics + }; +}; diff --git a/src/routes/console/project-[project]/messaging/topics/create.svelte b/src/routes/console/project-[project]/messaging/topics/create.svelte new file mode 100644 index 0000000000..65528f2c00 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/create.svelte @@ -0,0 +1,68 @@ + + + + + + {#if !showCustomId} +
+ (showCustomId = !showCustomId)} + > +
+ {:else} + + {/if} +
+ + + + +
diff --git a/src/routes/console/project-[project]/messaging/topics/store.ts b/src/routes/console/project-[project]/messaging/topics/store.ts new file mode 100644 index 0000000000..f8bd1df06a --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/store.ts @@ -0,0 +1,11 @@ +import type { Column } from '$lib/helpers/types'; +import { writable } from 'svelte/store'; + +export let showCreate = writable(false); + +export const columns = writable([ + { id: '$id', title: 'Topic ID', type: 'string', show: true, width: 140 }, + { id: 'name', title: 'Name', type: 'string', show: true, width: 140 }, + { id: 'total', title: 'Subscribers', type: 'integer', show: true, width: 140 }, + { id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 140 } +]); From ca674731d06c5849b7dd17970b545f7927301790 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 30 Oct 2023 15:32:39 -0700 Subject: [PATCH 2/9] Implmement create topic modal --- src/lib/actions/analytics.ts | 5 +++- .../messaging/topics/create.svelte | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index e9c127f9c5..5d9a037c9b 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -292,5 +292,8 @@ export enum Submit { MessagingProviderDelete = 'submit_messaging_provider_delete', MessagingProviderUpdate = 'submit_messaging_provider_update', MessagingMessageCreate = 'submit_messaging_message_create', - MessagingMessageDelete = 'submit_messaging_message_delete' + MessagingMessageDelete = 'submit_messaging_message_delete', + MessagingTopicCreate = 'submit_messaging_topic_create', + MessagingTopicDelete = 'submit_messaging_topic_delete', + MessagingTopicUpdateName = 'submit_messaging_topic_update_name', } diff --git a/src/routes/console/project-[project]/messaging/topics/create.svelte b/src/routes/console/project-[project]/messaging/topics/create.svelte index 65528f2c00..f3246a1960 100644 --- a/src/routes/console/project-[project]/messaging/topics/create.svelte +++ b/src/routes/console/project-[project]/messaging/topics/create.svelte @@ -17,21 +17,33 @@ const create = async () => { try { - const team = await sdk.forProject.teams.create(id ?? ID.unique(), name); + const topic = await sdk.forProject.client.call( + 'POST', + new URL(sdk.forProject.client.config.endpoint + '/messaging/topics'), + { + 'X-Appwrite-Project': sdk.forProject.client.config.project, + 'content-type': 'application/json', + 'X-Appwrite-Mode': 'admin' + }, + { + topicId: id ?? ID.unique(), + name: name + } + ); name = ''; showCreate = false; showCustomId = false; addNotification({ type: 'success', - message: `${team.name} has been created` + message: `${topic.name} has been created` }); - trackEvent(Submit.TeamCreate, { + trackEvent(Submit.MessagingTopicCreate, { customId: !!id }); - dispatch('created', team); + dispatch('created', topic); } catch (e) { error = e.message; - trackError(e, Submit.TeamCreate); + trackError(e, Submit.MessagingTopicCreate); } }; @@ -41,7 +53,7 @@ } - + (showCustomId = !showCustomId)} > {:else} - + {/if} From 5856b5161516a521ac427d1276e4a925426af630 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 30 Oct 2023 15:55:02 -0700 Subject: [PATCH 3/9] Add topic detail route --- src/lib/constants.ts | 3 +- .../topics/topic-[topic]/+layout.svelte | 5 ++ .../messaging/topics/topic-[topic]/+layout.ts | 30 +++++++++ .../topics/topic-[topic]/+page.svelte | 12 ++++ .../topics/topic-[topic]/breadcrumbs.svelte | 27 ++++++++ .../topics/topic-[topic]/dangerZone.svelte | 45 +++++++++++++ .../topics/topic-[topic]/deleteTopic.svelte | 56 ++++++++++++++++ .../topics/topic-[topic]/details.svelte | 19 ++++++ .../topics/topic-[topic]/header.svelte | 54 +++++++++++++++ .../messaging/topics/topic-[topic]/store.ts | 9 +++ .../topics/topic-[topic]/updateName.svelte | 66 +++++++++++++++++++ 11 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.ts create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/breadcrumbs.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/dangerZone.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/deleteTopic.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/details.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateName.svelte diff --git a/src/lib/constants.ts b/src/lib/constants.ts index c9868fdcd0..bcf4f966b2 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -49,7 +49,8 @@ export enum Dependencies { CONSOLE_VARIABLES = 'dependency:console_variables', MESSAGING_PROVIDERS = 'dependency:messaging_providers', MESSAGING_PROVIDER = 'dependency:messaging_provider', - MESSAGING_MESSAGE = 'dependency:messaging_message' + MESSAGING_MESSAGE = 'dependency:messaging_message', + MESSAGING_TOPIC = 'dependency:messaging_topic' } export const scopes: { diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.svelte new file mode 100644 index 0000000000..525eedb90f --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.svelte @@ -0,0 +1,5 @@ + + Topic - Appwrite + + + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.ts new file mode 100644 index 0000000000..594acc96ba --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+layout.ts @@ -0,0 +1,30 @@ +import type { LayoutLoad } from './$types'; +import Breadcrumbs from './breadcrumbs.svelte'; +import Header from './header.svelte'; +import { sdk } from '$lib/stores/sdk'; +import { Dependencies } from '$lib/constants'; +import { error } from '@sveltejs/kit'; + +export const load: LayoutLoad = async ({ params, depends }) => { + depends(Dependencies.MESSAGING_TOPIC); + + const response = await sdk.forProject.client.call( + 'GET', + new URL(sdk.forProject.client.config.endpoint + '/messaging/topics/' + params.topic), + { + 'X-Appwrite-Project': sdk.forProject.client.config.project, + 'content-type': 'application/json', + 'X-Appwrite-Mode': 'admin' + } + ); + + try { + return { + header: Header, + breadcrumbs: Breadcrumbs, + topic: response + }; + } catch (e) { + throw error(e.code, e.message); + } +}; diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte new file mode 100644 index 0000000000..9776c07f37 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte @@ -0,0 +1,12 @@ + + + +
+ + + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/breadcrumbs.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/breadcrumbs.svelte new file mode 100644 index 0000000000..c41df553b9 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/breadcrumbs.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/dangerZone.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/dangerZone.svelte new file mode 100644 index 0000000000..113c2550cd --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/dangerZone.svelte @@ -0,0 +1,45 @@ + + + + + +
+ Delete topic +
+

+ The topic will be permanently deleted, including all data associated with this topic. This + action is irreversible. +

+ + + +
{$topic.name}
+
+

+ {$topic.total} subscriber{$topic.total === 1 ? '' : 's'} +

+
+
+ + + + +
+ + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/deleteTopic.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/deleteTopic.svelte new file mode 100644 index 0000000000..a3e4226e33 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/deleteTopic.svelte @@ -0,0 +1,56 @@ + + + +

+ Are you sure you want to delete {$topic.name} from '{$project.name}'? +

+ + + + +
diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/details.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/details.svelte new file mode 100644 index 0000000000..1e81e5044e --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/details.svelte @@ -0,0 +1,19 @@ + + + +
+ Details +
+ +
+
+

{$topic.total} subscriber{$topic.total === 1 ? '' : 's'}

+

Created: {toLocaleDateTime($topic.$createdAt)}

+
+
+
+
diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte new file mode 100644 index 0000000000..7b1998220f --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte @@ -0,0 +1,54 @@ + + + + + + {$topic.name} + + {$topic.$id} + + + + {#each tabs as tab} + + {tab.title} + + {/each} + + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts new file mode 100644 index 0000000000..5678f26174 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts @@ -0,0 +1,9 @@ +import { derived } from 'svelte/store'; +import { page } from '$app/stores'; +import type { Topic } from '../+page'; + +export const topic = derived( + page, + // TODO: Set actual type + ($page) => $page.data.topic as Topic +); diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateName.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateName.svelte new file mode 100644 index 0000000000..dfc6dd1af4 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateName.svelte @@ -0,0 +1,66 @@ + + +
+ + Name + + +
    + +
+
+ + + + +
+
From ece8ec7c9bdf0dd2abf33191550804d2272d649c Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 1 Nov 2023 16:41:03 -0700 Subject: [PATCH 4/9] Add subscribers list route --- src/lib/actions/analytics.ts | 1 + src/lib/constants.ts | 3 +- .../topics/topic-[topic]/header.svelte | 10 +- .../topic-[topic]/subscribers/+page.svelte | 173 ++++++++++++++++++ .../topics/topic-[topic]/subscribers/+page.ts | 51 ++++++ 5 files changed, 232 insertions(+), 6 deletions(-) create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 5d9a037c9b..78603fbe33 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -296,4 +296,5 @@ export enum Submit { MessagingTopicCreate = 'submit_messaging_topic_create', MessagingTopicDelete = 'submit_messaging_topic_delete', MessagingTopicUpdateName = 'submit_messaging_topic_update_name', + MessagingTopicSubscriberAdd = 'submit_messaging_topic_subscriber_add' } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index bcf4f966b2..2c0a1a75d6 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -50,7 +50,8 @@ export enum Dependencies { MESSAGING_PROVIDERS = 'dependency:messaging_providers', MESSAGING_PROVIDER = 'dependency:messaging_provider', MESSAGING_MESSAGE = 'dependency:messaging_message', - MESSAGING_TOPIC = 'dependency:messaging_topic' + MESSAGING_TOPIC = 'dependency:messaging_topic', + MESSAGING_TOPIC_SUBSCRIBERS = 'dependency:messaging_topic_subscribers' } export const scopes: { diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte index 7b1998220f..660b177e71 100644 --- a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte @@ -14,11 +14,11 @@ title: 'Overview', event: 'overview' }, - // { - // href: `${path}/memberships`, - // title: 'Memberships', - // event: 'memberships' - // }, + { + href: `${path}/subscribers`, + title: 'Subscribers', + event: 'subscribers' + } // { // href: `${path}/sessions`, // title: 'Sessions', diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte new file mode 100644 index 0000000000..84fd06bf18 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte @@ -0,0 +1,173 @@ + + + +
+
+ Subscribers +
+ +
+
+ + +
+ +
+
+
+ {#if data.subscribers.total} + + + Name + Created + + + + {#each data.subscribers.subscribers as subscriber} + {@const user = data.targetsById.get(subscriber.$id)} + + + + {user.name ?? '-'} + + + {toLocaleDateTime(subscriber.$createdAt)} + + + + + + {/each} + + + + + + {:else if data.search && data.search != 'empty'} + +
+ Sorry, we couldn't find '{data.search}' +

There are no subscribers that match your search.

+
+ +
+ {:else} + + (showAdd = true)} + href="https://appwrite.io/docs/references/cloud/client-web/teams" + target="subscriber" /> + {/if} +
+ + + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts new file mode 100644 index 0000000000..cc5b47a476 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts @@ -0,0 +1,51 @@ +import { sdk } from '$lib/stores/sdk'; +import { getLimit, getPage, getSearch, pageToOffset } from '$lib/helpers/load'; +import { Dependencies, PAGE_LIMIT } from '$lib/constants'; +import type { PageLoad } from './$types'; +import type { Models } from '@appwrite.io/console'; + +export type Subscriber = { + $id: string; + $createdAt: string; + targetId: string; + topicId: string; +}; + +export const load: PageLoad = async ({ params, url, route, depends }) => { + depends(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS); + const page = getPage(url); + const limit = getLimit(url, route, PAGE_LIMIT); + const offset = pageToOffset(page, limit); + const search = getSearch(url); + + // TODO: remove when the API is ready with data + // This allows us to mock w/ data and when search returns 0 results + const subscribers: { subscribers: Subscriber[]; total: number } = + await sdk.forProject.client.call( + 'GET', + new URL( + `${sdk.forProject.client.config.endpoint}/messaging/topics/${params.topic}/subscribers` + ), + { + 'X-Appwrite-Project': sdk.forProject.client.config.project, + 'content-type': 'application/json', + 'X-Appwrite-Mode': 'admin' + } + ); + + const targetsById = new Map>(); + const userPromises = subscribers.subscribers.map((subscriber) => + sdk.forProject.users.get(subscriber.targetId) + ); + for await (const user of userPromises) { + targetsById.set(user.$id, user); + } + + return { + offset, + limit, + search, + subscribers, + targetsById + }; +}; From dbad10b897eba367099717d951b32c3a8362561a Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 5 Dec 2023 14:56:27 +0100 Subject: [PATCH 5/9] Add support for adding a subscriber to a topic --- src/lib/actions/analytics.ts | 3 +- .../messaging/actions.svelte | 44 ++++ .../project-[project]/messaging/store.ts | 25 +++ .../messaging/topics/+page.ts | 12 +- .../messaging/topics/topic-[topic]/store.ts | 2 +- .../topic-[topic]/subscribers/+page.svelte | 178 ++++++++-------- .../topics/topic-[topic]/subscribers/+page.ts | 46 +++-- .../topics/topic-[topic]/subscribers/store.ts | 11 + .../topic-[topic]/subscribers/table.svelte | 191 ++++++++++++++++++ .../messaging/topicsModal.svelte | 167 +++++++++++++++ .../messaging/userTargetsModal.svelte | 14 +- 11 files changed, 559 insertions(+), 134 deletions(-) create mode 100644 src/routes/console/project-[project]/messaging/actions.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/store.ts create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/table.svelte create mode 100644 src/routes/console/project-[project]/messaging/topicsModal.svelte diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 78603fbe33..9b6196be7c 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -296,5 +296,6 @@ export enum Submit { MessagingTopicCreate = 'submit_messaging_topic_create', MessagingTopicDelete = 'submit_messaging_topic_delete', MessagingTopicUpdateName = 'submit_messaging_topic_update_name', - MessagingTopicSubscriberAdd = 'submit_messaging_topic_subscriber_add' + MessagingTopicSubscriberAdd = 'submit_messaging_topic_subscriber_add', + MessagingTopicSubscriberDelete = 'submit_messaging_topic_subscriber_delete' } diff --git a/src/routes/console/project-[project]/messaging/actions.svelte b/src/routes/console/project-[project]/messaging/actions.svelte new file mode 100644 index 0000000000..bf2bb5b7bf --- /dev/null +++ b/src/routes/console/project-[project]/messaging/actions.svelte @@ -0,0 +1,44 @@ + + + + + + (showTopics = true)}>Select topics + (showUserTargets = true)}>Select targets + + + + { + showTopics = false; + dispatch('addTopics', e.detail); + }} /> + { + showUserTargets = false; + dispatch('addTargets', e.detail); + }} /> diff --git a/src/routes/console/project-[project]/messaging/store.ts b/src/routes/console/project-[project]/messaging/store.ts index 6a08966c05..d30c67b808 100644 --- a/src/routes/console/project-[project]/messaging/store.ts +++ b/src/routes/console/project-[project]/messaging/store.ts @@ -15,6 +15,9 @@ export const columns = writable([ { id: 'deliveredAt', title: 'Delivered at', type: 'datetime', show: false, width: 120 } ]); +export const targetsById = writable>({}); +export const topicsById = writable>({}); + // TODO: remove this when the SDK and API are ready export type Message = { $id: string; @@ -238,3 +241,25 @@ export const providersById: { [providerId: string]: Provider } = { options: {} } }; + +// TODO: remove when sdk has the model +export type Topic = { + $id: string; + $createdAt: string; + $updatedAt: string; + providerId: string; + name: string; + total: number; + description: string; +}; + +export type Target = { + $id: string; + $createdAt: string; + $updatedAt: string; + name: string; + userId: string; + providerId: string; + providerType: ProviderTypes; + identifier: string; +}; diff --git a/src/routes/console/project-[project]/messaging/topics/+page.ts b/src/routes/console/project-[project]/messaging/topics/+page.ts index ead3791dfa..08e899df61 100644 --- a/src/routes/console/project-[project]/messaging/topics/+page.ts +++ b/src/routes/console/project-[project]/messaging/topics/+page.ts @@ -3,17 +3,7 @@ import { sdk } from '$lib/stores/sdk'; import { getLimit, getPage, getQuery, getSearch, pageToOffset } from '$lib/helpers/load'; import { PAGE_LIMIT } from '$lib/constants'; import { queryParamToMap, queries } from '$lib/components/filters/store'; - -// TODO: remove when sdk has the model -export type Topic = { - $id: string; - $createdAt: string; - $updatedAt: string; - providerId: string; - name: string; - total: number; - description: string; -}; +import type { Topic } from '../store'; export const load = async ({ url, route }) => { const page = getPage(url); diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts index 5678f26174..a2379dadd2 100644 --- a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/store.ts @@ -1,6 +1,6 @@ import { derived } from 'svelte/store'; import { page } from '$app/stores'; -import type { Topic } from '../+page'; +import type { Topic } from '../../store'; export const topic = derived( page, diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte index 84fd06bf18..02aef8a3e0 100644 --- a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.svelte @@ -1,14 +1,5 @@ @@ -87,59 +93,44 @@
+ +
+
+
+ + +
+
+ + +
+
{#if data.subscribers.total} - - - Name - Created - - - - {#each data.subscribers.subscribers as subscriber} - {@const user = data.targetsById.get(subscriber.$id)} - - - - {user.name ?? '-'} - - - {toLocaleDateTime(subscriber.$createdAt)} - - - - - - {/each} - - + - - + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts index cc5b47a476..fe22003d8e 100644 --- a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/+page.ts @@ -1,22 +1,46 @@ +import { Query } from '@appwrite.io/console'; import { sdk } from '$lib/stores/sdk'; -import { getLimit, getPage, getSearch, pageToOffset } from '$lib/helpers/load'; +import { getLimit, getPage, getQuery, getSearch, pageToOffset } from '$lib/helpers/load'; import { Dependencies, PAGE_LIMIT } from '$lib/constants'; import type { PageLoad } from './$types'; -import type { Models } from '@appwrite.io/console'; +import type { Target } from '../../../store'; +import { queryParamToMap, queries } from '$lib/components/filters/store'; export type Subscriber = { $id: string; $createdAt: string; + $updatedAt: string; targetId: string; + target: Target; + userName: string; topicId: string; }; -export const load: PageLoad = async ({ params, url, route, depends }) => { +export const load: PageLoad = async ({ params, url, route, depends, parent }) => { depends(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS); const page = getPage(url); const limit = getLimit(url, route, PAGE_LIMIT); const offset = pageToOffset(page, limit); const search = getSearch(url); + const query = getQuery(url); + + const parsedQueries = queryParamToMap(query || '[]'); + queries.set(parsedQueries); + + const payload = { + queries: [ + Query.limit(limit), + Query.offset(offset), + Query.orderDesc(''), + ...parsedQueries.values() + ] + }; + + if (search) { + payload['search'] = search; + } + + const { topic } = await parent(); // TODO: remove when the API is ready with data // This allows us to mock w/ data and when search returns 0 results @@ -30,22 +54,16 @@ export const load: PageLoad = async ({ params, url, route, depends }) => { 'X-Appwrite-Project': sdk.forProject.client.config.project, 'content-type': 'application/json', 'X-Appwrite-Mode': 'admin' - } + }, + payload ); - const targetsById = new Map>(); - const userPromises = subscribers.subscribers.map((subscriber) => - sdk.forProject.users.get(subscriber.targetId) - ); - for await (const user of userPromises) { - targetsById.set(user.$id, user); - } - return { offset, limit, search, - subscribers, - targetsById + query, + topic, + subscribers }; }; diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/store.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/store.ts new file mode 100644 index 0000000000..014bca76cf --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/store.ts @@ -0,0 +1,11 @@ +import type { Column } from '$lib/helpers/types'; +import { writable } from 'svelte/store'; + +export const columns = writable([ + { id: '$id', title: 'Subscriber ID', type: 'string', show: true, width: 140 }, + { id: 'userName', title: 'Name', type: 'string', show: true, width: 100 }, + { id: 'targetId', title: 'Target ID', type: 'string', show: true, width: 140 }, + { id: 'target', title: 'Target', type: 'string', show: true, width: 140 }, + { id: 'type', title: 'Type', type: 'string', show: true, width: 80 }, + { id: '$createdAt', title: 'Created', type: 'string', show: true, width: 100 } +]); diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/table.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/table.svelte new file mode 100644 index 0000000000..13fbbf7f0a --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/subscribers/table.svelte @@ -0,0 +1,191 @@ + + + + + d.$id)} /> + {#each $columns as column} + {#if column.show} + {column.title} + {/if} + {/each} + + + {#each data.subscribers.subscribers as subscriber (subscriber.$id)} + {@const target = subscriber.target} + + + + {#each $columns as column} + {#if column.show} + {#if column.id === '$id'} + {#key $columns} + + + {subscriber.$id} + + + {/key} + {:else if column.id === 'targetId'} + + + {subscriber[column.id]} + + + {:else if column.id === 'target'} + + {#if target.providerType === ProviderTypes.Push} + {target.name} + {:else} + {target.identifier} + {/if} + + {:else if column.id === 'type'} + + + + {:else if column.id === '$createdAt'} + + {toLocaleDateTime(subscriber[column.id])} + + {:else} + + {subscriber[column.id]} + + {/if} + {/if} + {/each} + + {/each} + + + + 0}> +
+
+ {selectedIds.length} +

+ + {selectedIds.length > 1 ? 'subscribers' : 'subscriber'} + + selected +

+
+ +
+ + +
+
+
+ + +

+ Are you sure you want to delete {selectedIds.length} + {selectedIds.length > 1 ? 'subscribers' : 'subscriber'}? +

+ + + + +
diff --git a/src/routes/console/project-[project]/messaging/topicsModal.svelte b/src/routes/console/project-[project]/messaging/topicsModal.svelte new file mode 100644 index 0000000000..f1141b26e0 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topicsModal.svelte @@ -0,0 +1,167 @@ + + + +

Select existing topics you want to send this message to its recipients.

+ + {#if Object.keys(topicResultsById).length > 0} + {#each Object.entries(topicResultsById) as [topicId, topic]} + onTopicSelection(event, topic)}> + + + + {topic.name} + ({topic.total} subscribers) + + + + {/each} +
+

Total results: {totalResults}

+ +
+ {:else if search} + +
+
+ Sorry we couldn't find "{search}" +

There are no topics that match your search.

+
+
+ + +
+
+
+ {:else} + +
+
+

+ You have no topics. Create a topic to see them here. +

+ +

+ Need a hand? Learn more in our + documentation. +

+
+
+
+ {/if} + + + + +
diff --git a/src/routes/console/project-[project]/messaging/userTargetsModal.svelte b/src/routes/console/project-[project]/messaging/userTargetsModal.svelte index fad9a00226..878e77a277 100644 --- a/src/routes/console/project-[project]/messaging/userTargetsModal.svelte +++ b/src/routes/console/project-[project]/messaging/userTargetsModal.svelte @@ -1,16 +1,3 @@ - - @@ -29,6 +30,7 @@ ) return; $providerType = type; + $topicsById = {}; $targetsById = {}; const common = { topics: [], diff --git a/src/routes/console/project-[project]/messaging/wizard/step2.svelte b/src/routes/console/project-[project]/messaging/wizard/step2.svelte index 3b1c0433fa..cb456bc95e 100644 --- a/src/routes/console/project-[project]/messaging/wizard/step2.svelte +++ b/src/routes/console/project-[project]/messaging/wizard/step2.svelte @@ -1,6 +1,5 @@ @@ -40,12 +55,20 @@ Select users to whom this message should be directed. - {#if targetIdsLength == 0} + {#if targetIdsLength === 0 && topicIdsLength === 0}
- + + +
Select recipients to get started
@@ -59,6 +82,32 @@ + {#each Object.entries($topicsById) as [topicId, topic] (topicId)} + + +
+ + + {topic.name} + ({topic.total} subscribers) + +
+
+ +
+ +
+
+
+ {/each} {#each Object.entries($targetsById) as [targetId, target] (targetId)} @@ -84,15 +133,17 @@
- + + + {/if} - - diff --git a/src/routes/console/project-[project]/messaging/wizard/store.ts b/src/routes/console/project-[project]/messaging/wizard/store.ts index df3fecdcec..3b02644524 100644 --- a/src/routes/console/project-[project]/messaging/wizard/store.ts +++ b/src/routes/console/project-[project]/messaging/wizard/store.ts @@ -1,6 +1,6 @@ import { ProviderTypes } from '../providerType.svelte'; -import type { Target } from '../userTargetsModal.svelte'; import { writable } from 'svelte/store'; +import type { Target } from '../store'; export enum MessageStatuses { DRAFT = 'draft', From 9b84ea5947e4ccba4b12c3a09b6e835fe261dff3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Thu, 14 Dec 2023 10:54:42 -0800 Subject: [PATCH 7/9] Refactor topics table and add bulk delete --- src/lib/constants.ts | 1 + .../messaging/topics/+page.svelte | 60 +------ .../messaging/topics/+page.ts | 5 +- .../messaging/topics/table.svelte | 154 ++++++++++++++++++ 4 files changed, 160 insertions(+), 60 deletions(-) create mode 100644 src/routes/console/project-[project]/messaging/topics/table.svelte diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 2c0a1a75d6..da342ce92c 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -50,6 +50,7 @@ export enum Dependencies { MESSAGING_PROVIDERS = 'dependency:messaging_providers', MESSAGING_PROVIDER = 'dependency:messaging_provider', MESSAGING_MESSAGE = 'dependency:messaging_message', + MESSAGING_TOPICS = 'dependency:messaging_topics', MESSAGING_TOPIC = 'dependency:messaging_topic', MESSAGING_TOPIC_SUBSCRIBERS = 'dependency:messaging_topic_subscribers' } diff --git a/src/routes/console/project-[project]/messaging/topics/+page.svelte b/src/routes/console/project-[project]/messaging/topics/+page.svelte index 1a990e8b88..a31adb1659 100644 --- a/src/routes/console/project-[project]/messaging/topics/+page.svelte +++ b/src/routes/console/project-[project]/messaging/topics/+page.svelte @@ -1,16 +1,5 @@ + + + + d.$id)} /> + {#each $columns as column} + {#if column.show} + {column.title} + {/if} + {/each} + + + {#each data.topics.topics as topic (topic.$id)} + + + + {#each $columns as column (column.id)} + {#if column.show} + {#if column.id === '$id'} + {#key $columns} + + {topic.$id} + + {/key} + {:else if column.type === 'datetime'} + + {#if !topic[column.id]} + - + {:else} + {toLocaleDateTime(topic[column.id])} + {/if} + + {:else} + + {topic[column.id]} + + {/if} + {/if} + {/each} + + {/each} + + + + 0}> +
+
+ {selectedIds.length} +

+ + {selectedIds.length > 1 ? 'topics' : 'topic'} + + selected +

+
+ +
+ + +
+
+
+ + +

+ Are you sure you want to delete {selectedIds.length} + {selectedIds.length > 1 ? 'topics' : 'topic'}? +

+ + + + +
From 842da80e4b35369d036c800845e4bb5d4fa5fb95 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 15 Dec 2023 10:21:28 -0800 Subject: [PATCH 8/9] Add description to topics table and details --- src/lib/actions/analytics.ts | 1 + .../messaging/topics/+page.svelte | 1 - .../messaging/topics/create.svelte | 14 +++- .../messaging/topics/store.ts | 1 + .../topics/topic-[topic]/+page.svelte | 4 +- .../topic-[topic]/updateDescription.svelte | 65 +++++++++++++++++++ 6 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateDescription.svelte diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 9b6196be7c..39551514b2 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -296,6 +296,7 @@ export enum Submit { MessagingTopicCreate = 'submit_messaging_topic_create', MessagingTopicDelete = 'submit_messaging_topic_delete', MessagingTopicUpdateName = 'submit_messaging_topic_update_name', + MessagingTopicUpdateDescription = 'submit_messaging_topic_update_description', MessagingTopicSubscriberAdd = 'submit_messaging_topic_subscriber_add', MessagingTopicSubscriberDelete = 'submit_messaging_topic_subscriber_delete' } diff --git a/src/routes/console/project-[project]/messaging/topics/+page.svelte b/src/routes/console/project-[project]/messaging/topics/+page.svelte index a31adb1659..659b734de9 100644 --- a/src/routes/console/project-[project]/messaging/topics/+page.svelte +++ b/src/routes/console/project-[project]/messaging/topics/+page.svelte @@ -100,5 +100,4 @@ {/if}
- diff --git a/src/routes/console/project-[project]/messaging/topics/create.svelte b/src/routes/console/project-[project]/messaging/topics/create.svelte index f3246a1960..2bb52245e2 100644 --- a/src/routes/console/project-[project]/messaging/topics/create.svelte +++ b/src/routes/console/project-[project]/messaging/topics/create.svelte @@ -12,7 +12,7 @@ const dispatch = createEventDispatcher(); - let name: string, id: string, error: string; + let name: string, description: string, id: string, error: string; let showCustomId = false; const create = async () => { @@ -26,11 +26,13 @@ 'X-Appwrite-Mode': 'admin' }, { - topicId: id ?? ID.unique(), - name: name + name, + description, + topicId: id ?? ID.unique() } ); name = ''; + description = ''; showCreate = false; showCustomId = false; addNotification({ @@ -62,6 +64,12 @@ autofocus={true} required bind:value={name} /> + + {#if !showCustomId}
(showCustomId = !showCustomId)} diff --git a/src/routes/console/project-[project]/messaging/topics/store.ts b/src/routes/console/project-[project]/messaging/topics/store.ts index f8bd1df06a..0c950c385b 100644 --- a/src/routes/console/project-[project]/messaging/topics/store.ts +++ b/src/routes/console/project-[project]/messaging/topics/store.ts @@ -6,6 +6,7 @@ export let showCreate = writable(false); export const columns = writable([ { id: '$id', title: 'Topic ID', type: 'string', show: true, width: 140 }, { id: 'name', title: 'Name', type: 'string', show: true, width: 140 }, + { id: 'description', title: 'Description', type: 'string', show: true, width: 140 }, { id: 'total', title: 'Subscribers', type: 'integer', show: true, width: 140 }, { id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 140 } ]); diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte index 9776c07f37..dd88db1906 100644 --- a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/+page.svelte @@ -1,12 +1,14 @@
+ diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateDescription.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateDescription.svelte new file mode 100644 index 0000000000..137e7944c1 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/updateDescription.svelte @@ -0,0 +1,65 @@ + + +
+ + Description + + +
    + +
+
+ + + + +
+
From fee8e9cbed0aee769355aff386bc4da6480bd9a4 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 15 Dec 2023 11:51:39 -0800 Subject: [PATCH 9/9] Add topic activity page --- .../topic-[topic]/activity/+page.svelte | 8 +++++ .../topics/topic-[topic]/activity/+page.ts | 34 +++++++++++++++++++ .../topics/topic-[topic]/header.svelte | 16 +++------ 3 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.svelte create mode 100644 src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.ts diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.svelte new file mode 100644 index 0000000000..f805a79f04 --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.svelte @@ -0,0 +1,8 @@ + + + diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.ts b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.ts new file mode 100644 index 0000000000..97f3d29aeb --- /dev/null +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/activity/+page.ts @@ -0,0 +1,34 @@ +import { Query, type Models } from '@appwrite.io/console'; +import { sdk } from '$lib/stores/sdk'; +import { getLimit, getPage, pageToOffset } from '$lib/helpers/load'; +import { PAGE_LIMIT } from '$lib/constants'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async ({ params, url, route }) => { + const page = getPage(url); + const limit = getLimit(url, route, PAGE_LIMIT); + const offset = pageToOffset(page, limit); + + const payload = { + queries: [Query.limit(limit), Query.offset(offset)] + }; + + // TODO: remove when the API is ready with data + // This allows us to mock w/ data and when search returns 0 results + const logs: Models.LogList = await sdk.forProject.client.call( + 'GET', + new URL(`${sdk.forProject.client.config.endpoint}/messaging/topics/${params.topic}/logs`), + { + 'X-Appwrite-Project': sdk.forProject.client.config.project, + 'content-type': 'application/json', + 'X-Appwrite-Mode': 'admin' + }, + payload + ); + + return { + offset, + limit, + logs + }; +}; diff --git a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte index 660b177e71..0848e48db2 100644 --- a/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte +++ b/src/routes/console/project-[project]/messaging/topics/topic-[topic]/header.svelte @@ -18,18 +18,12 @@ href: `${path}/subscribers`, title: 'Subscribers', event: 'subscribers' + }, + { + href: `${path}/activity`, + title: 'Activity', + event: 'activity' } - // { - // href: `${path}/sessions`, - // title: 'Sessions', - // event: 'sessions' - // }, - // { - // href: `${path}/activity`, - // title: 'Activity', - // event: 'activity', - // hasChildren: true - // } ];