Skip to content

Commit a2720e3

Browse files
committed
Add messaging overview route
1 parent c6935ba commit a2720e3

9 files changed

Lines changed: 510 additions & 0 deletions

File tree

src/lib/commandCenter/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const groups = [
1717
'platforms',
1818
'databases',
1919
'functions',
20+
'messaging',
2021
'storage',
2122
'domains',
2223
'webhooks',
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<script lang="ts">
2+
import { goto } from '$app/navigation';
3+
import { page } from '$app/stores';
4+
import { registerCommands, updateCommandGroupRanks } from '$lib/commandCenter';
5+
import { project } from '../store';
6+
import { showCreate } from './store';
7+
8+
// TODO: finalize the commands
9+
10+
$: $registerCommands([
11+
{
12+
label: 'Create message',
13+
callback: () => {
14+
if (!$page.url.pathname.endsWith('messaging')) {
15+
goto(`/console/project-${$project.$id}/messaging`);
16+
}
17+
$showCreate = true;
18+
},
19+
keys: $page.url.pathname.endsWith('messaging') ? ['c'] : ['c', 'm'],
20+
icon: 'plus',
21+
group: 'messaging'
22+
},
23+
{
24+
label: 'Go to topics',
25+
callback() {
26+
goto(`/console/project-${$project.$id}/messaging/topics`);
27+
},
28+
keys: ['g', 't'],
29+
disabled:
30+
$page.url.pathname.endsWith('topics') || $page.url.pathname.includes('message-'),
31+
group: 'navigation',
32+
rank: 10
33+
},
34+
{
35+
label: 'Go to providers',
36+
callback() {
37+
goto(`/console/project-${$project.$id}/messaging/providers`);
38+
},
39+
keys: ['g', 'p'],
40+
disabled:
41+
$page.url.pathname.endsWith('topics') || $page.url.pathname.includes('message-'),
42+
group: 'navigation',
43+
rank: 10
44+
}
45+
// {
46+
// label: 'Find messages',
47+
// callback: () => {
48+
// addSubPanel(BucketsPanel);
49+
// },
50+
// group: 'messaging',
51+
// rank: -1
52+
// }
53+
]);
54+
55+
$: $updateCommandGroupRanks({ messaging: 200, navigation: 100 });
56+
</script>
57+
58+
<svelte:head>
59+
<title>Messaging - Appwrite</title>
60+
</svelte:head>
61+
62+
<slot />
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Breadcrumbs from './breadcrumbs.svelte';
2+
import Header from './header.svelte';
3+
import type { LayoutLoad } from './$types';
4+
5+
export const load: LayoutLoad = async () => {
6+
return {
7+
header: Header,
8+
breadcrumbs: Breadcrumbs
9+
};
10+
};
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<script lang="ts">
2+
import { goto } from '$app/navigation';
3+
import { base } from '$app/paths';
4+
import { page } from '$app/stores';
5+
import {
6+
Empty,
7+
EmptySearch,
8+
FloatingActionBar,
9+
Heading,
10+
Id,
11+
PaginationWithLimit,
12+
SearchQuery,
13+
ViewSelector
14+
} from '$lib/components';
15+
import { Button } from '$lib/elements/forms';
16+
import { Container } from '$lib/layout';
17+
import type { Models } from '@appwrite.io/console';
18+
import type { PageData } from './$types';
19+
import Create from './create.svelte';
20+
import {
21+
TableHeader,
22+
TableCellHead,
23+
TableBody,
24+
TableCell,
25+
TableCellText,
26+
TableRowLink,
27+
TableCellHeadCheck,
28+
TableCellCheck
29+
} from '$lib/elements/table';
30+
import { toLocaleDateTime } from '$lib/helpers/date';
31+
import Filters from '../databases/database-[database]/collection-[collection]/(filters)/filters.svelte';
32+
import { columns, showCreate } from './store';
33+
import TableScroll from '$lib/elements/table/tableScroll.svelte';
34+
35+
export let data: PageData;
36+
let selected: string[] = [];
37+
let showDelete = false;
38+
39+
const project = $page.params.project;
40+
41+
async function messageCreated(event: CustomEvent<Models.Bucket>) {
42+
$showCreate = false;
43+
await goto(`${base}/console/project-${project}/messaging/message-${event.detail.$id}`);
44+
}
45+
</script>
46+
47+
<Container>
48+
<div class="u-flex u-flex-vertical">
49+
<div class="u-flex u-main-space-between">
50+
<Heading tag="h2" size="5">Messages</Heading>
51+
<div class="is-only-mobile">
52+
<Button on:click={() => ($showCreate = true)} event="create_message">
53+
<span class="icon-plus" aria-hidden="true" />
54+
<span class="text">Create message</span>
55+
</Button>
56+
</div>
57+
</div>
58+
<!-- TODO: fix width of search input in mobile -->
59+
<SearchQuery search={data.search} placeholder="Search by channel, topic, provider, or ID">
60+
<div class="u-flex u-gap-16 is-not-mobile">
61+
<!-- TODO: make this not database-specific -->
62+
<Filters />
63+
<ViewSelector
64+
view={data.view}
65+
{columns}
66+
hideView
67+
allowNoColumns
68+
showColsTextMobile />
69+
<Button on:click={() => ($showCreate = true)} event="create_message">
70+
<span class="icon-plus" aria-hidden="true" />
71+
<span class="text">Create message</span>
72+
</Button>
73+
</div>
74+
</SearchQuery>
75+
<div class="u-flex u-gap-16 is-only-mobile u-margin-block-start-16">
76+
<div class="u-flex-basis-50-percent">
77+
<!-- TODO: fix width -->
78+
<ViewSelector
79+
view={data.view}
80+
{columns}
81+
hideView
82+
allowNoColumns
83+
showColsTextMobile />
84+
</div>
85+
<div class="u-flex-basis-50-percent">
86+
<!-- TODO: fix width -->
87+
<Filters />
88+
</div>
89+
</div>
90+
</div>
91+
92+
{#if data.messages.total}
93+
<TableScroll>
94+
<TableHeader>
95+
<TableCellHeadCheck
96+
bind:selected
97+
pageItemsIds={data.messages.messages.map((d) => d.$id)} />
98+
{#each $columns as column}
99+
{#if column.show}
100+
<TableCellHead width={column.width}>{column.title}</TableCellHead>
101+
{/if}
102+
{/each}
103+
</TableHeader>
104+
<TableBody>
105+
{#each data.messages.messages as message}
106+
<TableRowLink
107+
href={`${base}/console/project-${project}/messaging/message-${message.$id}`}>
108+
<TableCellCheck bind:selectedIds={selected} id={message.$id} />
109+
110+
{#each $columns as column}
111+
{#if column.show}
112+
{#if column.id === '$id'}
113+
{#key $columns}
114+
<TableCell title={column.title} width={column.width}>
115+
<Id value={message.$id}>{message.$id}</Id>
116+
</TableCell>
117+
{/key}
118+
{:else if column.id === 'deliveryTime'}
119+
<TableCellText title={column.title} width={column.width}>
120+
{toLocaleDateTime(message[column.id])}
121+
</TableCellText>
122+
{:else}
123+
<TableCellText title={column.title} width={column.width}>
124+
{message[column.id]}
125+
</TableCellText>
126+
{/if}
127+
{/if}
128+
{/each}
129+
</TableRowLink>
130+
{/each}
131+
</TableBody>
132+
</TableScroll>
133+
134+
<FloatingActionBar show={selected.length > 0}>
135+
<div class="u-flex u-cross-center u-main-space-between actions">
136+
<div class="u-flex u-cross-center u-gap-8">
137+
<span class="indicator body-text-2 u-bold">{selected.length}</span>
138+
<p>
139+
<span class="is-only-desktop">
140+
{selected.length > 1 ? 'messages' : 'message'}
141+
</span>
142+
selected
143+
</p>
144+
</div>
145+
146+
<div class="u-flex u-cross-center u-gap-8">
147+
<Button text on:click={() => (selected = [])}>Cancel</Button>
148+
<!-- TODO: handle delete -->
149+
<Button secondary on:click={() => (showDelete = true)}>
150+
<p>Delete</p>
151+
</Button>
152+
</div>
153+
</div>
154+
</FloatingActionBar>
155+
156+
<PaginationWithLimit
157+
name="Messages"
158+
limit={data.limit}
159+
offset={data.offset}
160+
total={data.messages.total} />
161+
<!-- TODO: remove data.search != 'empty' when the API is ready with data -->
162+
{:else if data.search && data.search != 'empty'}
163+
<EmptySearch>
164+
<div class="u-text-center">
165+
<b>Sorry, we couldn't find '{data.search}'</b>
166+
<p>There are no messages that match your search.</p>
167+
</div>
168+
<div class="u-flex u-gap-16">
169+
<!-- TODO: update docs link -->
170+
<Button
171+
external
172+
href="https://appwrite.io/docs/products/storage/upload-download"
173+
text>
174+
Documentation
175+
</Button>
176+
<Button
177+
secondary
178+
href={`/console/project-${$page.params.project}/storage/bucket-${$page.params.bucket}`}>
179+
Clear Search
180+
</Button>
181+
</div>
182+
</EmptySearch>
183+
{:else}
184+
<!-- TODO: update docs link -->
185+
<Empty
186+
single
187+
href="https://appwrite.io/docs"
188+
target="message"
189+
on:click={() => ($showCreate = true)} />
190+
{/if}
191+
</Container>
192+
193+
<!-- TODO: handle create -->
194+
<Create bind:showCreate={$showCreate} on:created={messageCreated} />
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { View, getLimit, getPage, getSearch, getView, pageToOffset } from '$lib/helpers/load';
2+
import { CARD_LIMIT } from '$lib/constants';
3+
import type { PageLoad } from './$types';
4+
5+
// TODO: remove this when the SDK and API are ready
6+
export type Message = {
7+
$id: string;
8+
providerId: string;
9+
to: string[];
10+
deliveryTime: string;
11+
deliveredAt: string;
12+
deliveryErrors: string;
13+
deliveredTo: number;
14+
status: string;
15+
description: string;
16+
};
17+
18+
// TODO: remove this when the SDK and API are ready
19+
let data: { messages: Message[]; total: number } = {
20+
messages: [
21+
{
22+
$id: '637a40ba7a703e3936e1',
23+
description: 'Welcome to the Cloud',
24+
providerId: 'push',
25+
to: ['user-1', 'user-2'],
26+
deliveryTime: '2021-03-01T00:00:00.000Z',
27+
deliveredAt: '2021-03-01T00:00:00.000Z',
28+
deliveryErrors: '',
29+
deliveredTo: 2,
30+
status: 'sent'
31+
},
32+
{
33+
$id: '637a40ba7a703e3936e2',
34+
description: 'Welcome to the Cloud',
35+
providerId: 'sms',
36+
to: ['user-1', 'user-2'],
37+
deliveryTime: '2021-03-01T00:00:00.000Z',
38+
deliveredAt: '2021-03-01T00:00:00.000Z',
39+
deliveryErrors: '',
40+
deliveredTo: 2,
41+
status: 'scheduled'
42+
},
43+
{
44+
$id: '637a40ba7a703e3936e3',
45+
description: 'Welcome to the Cloud',
46+
providerId: 'email',
47+
to: ['user-1', 'user-2'],
48+
deliveryTime: '',
49+
deliveredAt: '',
50+
deliveryErrors: '',
51+
deliveredTo: 2,
52+
status: 'draft'
53+
}
54+
],
55+
total: 3
56+
};
57+
58+
export const load: PageLoad = async ({ url, route }) => {
59+
const page = getPage(url);
60+
const search = getSearch(url);
61+
const view = getView(url, route, View.Grid);
62+
const limit = getLimit(url, route, CARD_LIMIT);
63+
const offset = pageToOffset(page, limit);
64+
65+
// TODO: uncomment when the API is ready with data
66+
// const messages = await sdk.forProject.client.call(
67+
// 'GET',
68+
// new URL(sdk.forProject.client.config.endpoint + '/messaging/messages'),
69+
// {
70+
// 'X-Appwrite-Project': sdk.forProject.client.config.project,
71+
// 'content-type': 'application/json'
72+
// }
73+
// ) as {total: number, messages: Message[]};
74+
75+
// TODO: remove when the API is ready with data
76+
// This allows us to mock w/ data and when search returns 0 results
77+
let messages = { messages: [], total: 0 };
78+
if (!search) {
79+
messages = data;
80+
}
81+
82+
return {
83+
offset,
84+
limit,
85+
search,
86+
page,
87+
view,
88+
messages
89+
};
90+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script lang="ts">
2+
import { Breadcrumbs } from '$lib/layout';
3+
import { organization } from '$lib/stores/organization';
4+
import { project } from '../store';
5+
6+
$: breadcrumbs = [
7+
{
8+
href: `/console/organization-${$organization.$id}`,
9+
title: $organization.name
10+
},
11+
{
12+
href: `/console/project-${$project.$id}`,
13+
title: $project.name
14+
},
15+
{
16+
href: `/console/project-${$project.$id}/messaging`,
17+
title: 'Messaging'
18+
}
19+
];
20+
</script>
21+
22+
<Breadcrumbs {breadcrumbs} />

0 commit comments

Comments
 (0)