Merge pull request #2497 from appwrite/fix-SER-SER-416-More-Messaging-flow-issues

This commit is contained in:
Darshan
2026-02-02 12:12:16 +05:30
committed by GitHub
12 changed files with 87 additions and 59 deletions
+2
View File
@@ -12,6 +12,8 @@
style="inline-size: 100%; block-size: 100%;"
target="_blank"
rel="noopener noreferrer"
on:click|stopPropagation
on:mousedown|stopPropagation
aria-label="open file in new window">
<div class="file-preview-image">
<img src={$app.themeInUse === 'dark' ? darkSrc : lightSrc} {alt} />
+1 -1
View File
@@ -5,7 +5,7 @@
</script>
<p
class="helper u-margin-block-start-8 {classes}"
class="helper u-margin-block-start-6 {classes}"
class:u-color-text-info={type === 'info'}
class:u-color-text-danger={type === 'error'}
class:u-color-text-success={type === 'success'}
@@ -1,6 +1,11 @@
<script lang="ts">
import { SearchQuery, ViewSelector } from '$lib/components';
import { FiltersBottomSheet, ParsedTagList, queryParamToMap } from '$lib/components/filters';
import {
FiltersBottomSheet,
ParsedTagList,
queryParamToMap,
Filters
} from '$lib/components/filters';
import QuickFilters from '$lib/components/filters/quickFilters.svelte';
import Button from '$lib/elements/forms/button.svelte';
import { View } from '$lib/helpers/load';
@@ -23,6 +28,7 @@
hasSearch = false,
searchPlaceholder = 'Search by ID',
hasFilters = false,
hasCustomFiltersOnly = false,
analyticsSource = '',
children
}: {
@@ -33,6 +39,7 @@
hasSearch?: boolean;
searchPlaceholder?: string;
hasFilters?: boolean;
hasCustomFiltersOnly?: boolean;
analyticsSource?: string;
children?: Snippet;
} = $props();
@@ -109,7 +116,11 @@
</Layout.Stack>
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
{#if hasFilters && $columns?.length}
<QuickFilters {columns} {analyticsSource} {filterCols} />
{#if hasCustomFiltersOnly}
<Filters query="[]" {columns} {analyticsSource} />
{:else}
<QuickFilters {columns} {analyticsSource} {filterCols} />
{/if}
{/if}
{#if hasDisplaySettings}
<ViewSelector ui="new" {view} {columns} {hideView} {hideColumns} />
@@ -7,7 +7,7 @@
import { page } from '$app/state';
</script>
<Popover let:toggle padding="none" placement="bottom-start">
<Popover let:toggle padding="none" placement="bottom-end">
<slot {toggle}>
<Button on:click={toggle} event="create_message">
<Icon icon={IconPlus} slot="start" size="s" />
@@ -1,6 +1,6 @@
<script lang="ts">
import { MessagingProviderType, type Models } from '@appwrite.io/console';
import { CardGrid, Empty, PaginationInline, EmptySearch } from '$lib/components';
import { CardGrid, Empty, PaginationInline } from '$lib/components';
import { onMount } from 'svelte';
import { sdk } from '$lib/stores/sdk';
import { invalidate } from '$app/navigation';
@@ -12,8 +12,17 @@
import UserTargetsModal from '../userTargetsModal.svelte';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Alert, Icon, Layout, Table, Typography } from '@appwrite.io/pink-svelte';
import {
Alert,
Card,
Empty as PinkEmpty,
Icon,
Layout,
Table,
Typography
} from '@appwrite.io/pink-svelte';
import { page } from '$app/state';
import { Link } from '$lib/elements';
export let message: Models.Message & { data: Record<string, unknown> };
export let selectedTargetsById: Record<string, Models.Target>;
@@ -207,17 +216,17 @@
{:else if isDraft}
<Empty on:click={() => (showTargets = true)}>Add a target</Empty>
{:else if !sum && !hasDeletedUsers}
<EmptySearch hidePagination>
<div class="u-text-center">
No targets have been selected.
<p>
Need a hand? Check out our <Button
href="https://appwrite.io/docs/products/messaging/targets">
documentation</Button
<Card.Base padding="none">
<PinkEmpty type="secondary" title="No targets were selected">
<svelte:fragment slot="description">
Need a hand? Check out our <Link
variant="muted"
href="https://appwrite.io/docs/products/messaging/targets"
external>documentation</Link
>.
</p>
</div>
</EmptySearch>
</svelte:fragment>
</PinkEmpty>
</Card.Base>
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
@@ -23,6 +23,7 @@
view={View.Table}
hideView
hasFilters
hasCustomFiltersOnly
hasSearch
analyticsSource="messaging_providers"
searchPlaceholder="Search by name or ID">
@@ -200,7 +200,7 @@
}
</script>
<Wizard title="Create provider" columnSize="l">
<Wizard title="Create provider" columnSize="l" confirmExit>
<Form bind:this={formRef} onSubmit={create} isModal={false}>
<Layout.Stack gap="xxl">
<Fieldset legend="Provider">
@@ -9,7 +9,7 @@
import { ActionMenu, Popover } from '@appwrite.io/pink-svelte';
</script>
<Popover let:toggle padding="none" placement="bottom-start">
<Popover let:toggle padding="none" placement="bottom-end">
<slot {toggle} />
<ActionMenu.Root slot="tooltip">
{#each Object.entries(providers) as [type, option]}
@@ -70,6 +70,7 @@
view={View.Table}
hideView
hasFilters
hasCustomFiltersOnly
hasSearch
analyticsSource="messaging_topics_filter"
searchPlaceholder="Search by name or ID">
@@ -72,7 +72,7 @@
await Promise.all(promises);
addNotification({
type: 'success',
message: `${targetIds.length} subscriber${targetIds.length !== 1 ? 's' : ''} have been added`
message: `${targetIds.length} subscriber${targetIds.length !== 1 ? 's' : ''} created`
});
trackEvent(Submit.MessagingTopicSubscriberAdd);
await invalidate(Dependencies.MESSAGING_TOPIC_SUBSCRIBERS);
@@ -88,7 +88,7 @@
<Container>
<Layout.Stack direction="row" justifyContent="space-between">
<SearchQuery placeholder="Search by type or IDs"></SearchQuery>
<SearchQuery placeholder="Search by type or ID"></SearchQuery>
<Layout.Stack direction="row" inline>
<Filters query={data.query} {columns} analyticsSource="messaging_topics" />
<ViewSelector ui="new" view={View.Table} {columns} hideView />
@@ -99,7 +99,7 @@
}}
event="create_subscriber">
<Icon icon={IconPlus} slot="start" size="s" />
Add subscriber
Create subscriber
</Button>
</Layout.Stack>
</Layout.Stack>
@@ -115,14 +115,11 @@
{:else if $hasPageQueries}
<EmptyFilter resource="subscribers" />
{:else if data.search}
<EmptySearch>
<div class="u-text-center">
<b>Sorry, we couldn't find '{data.search}'</b>
<p>There are no subscribers that match your search.</p>
</div>
<EmptySearch bind:search={data.search} target="subscribers">
<Button
secondary
href={`${base}/project-${page.params.region}-${page.params.project}/messaging/topics/topic-${page.params.topic}/subscribers`}>
Clear Search
</Button>
</EmptySearch>
{:else}
@@ -141,7 +138,7 @@
on:update={addTargets}>
<svelte:fragment slot="description">
<Typography.Text>
Add subscribers to this topic by selecting the targets for directing messages.{' '}
Create subscribers for this topic by selecting the targets for directing messages.{' '}
<Link
href="https://appwrite.io/docs/products/messaging/topics#subscribe-targets-to-topics"
external>
@@ -21,24 +21,23 @@
show = $bindable(),
topicsById = $bindable(),
title = 'Select topics'
} = $props<{
}: {
providerType: MessagingProviderType;
show: boolean;
topicsById: Record<string, Models.Topic>;
title?: string;
}>();
} = $props();
const dispatch = createEventDispatcher();
let search = $state('');
let offset = $state(0);
let search = $state('');
let totalResults = $state(0);
let topicResultsById = $state<Record<string, Models.Topic>>({});
let selected = $state<Record<string, Models.Topic>>({});
let emptyTopicsExists = $state(false);
let previousSearch = $state('');
let selected = $state<Record<string, Models.Topic>>({});
let topicResultsById = $state<Record<string, Models.Topic>>({});
let wasOpen = $state(false);
let previousSearch = $state('');
function getTopicTotal(topic: Models.Topic): number {
switch (providerType) {
@@ -96,44 +95,52 @@
}
function onTopicSelection(event: CustomEvent<boolean>, topic: Models.Topic) {
const updatedSelected = { ...selected };
if (event.detail) {
selected = {
...selected,
[topic.$id]: topic
};
updatedSelected[topic.$id] = topic;
} else {
const { [topic.$id]: _, ...rest } = selected;
selected = rest;
delete updatedSelected[topic.$id];
}
selected = updatedSelected;
}
const selectedSize = $derived(Object.keys(selected).length);
const hasSelection = $derived(selectedSize > 0);
const topicsWithState = $derived(
Object.entries(topicResultsById).map(([topicId, topic]) => ({
topicId,
topic,
checked: !!selected[topicId],
disabled: !!topicsById[topicId]
}))
);
$effect(() => {
if (search !== previousSearch) {
const searchChanged = search !== previousSearch;
if (searchChanged) {
previousSearch = search;
offset = 0;
}
});
$effect(() => {
if (!show) return;
offset ?? null;
search ?? null;
request();
const hasValidData = offset !== null || search !== null;
const shouldFetch = show && hasValidData;
if (shouldFetch) {
request();
}
});
$effect(() => {
if (show && !wasOpen) {
const isOpening = show && !wasOpen;
if (isOpening) {
selected = topicsById;
}
wasOpen = show;
});
let selectedSize = $derived(Object.keys(selected).length);
let hasSelection = $derived(selectedSize > 0);
let topicSelectionStates = $derived(
Object.fromEntries(Object.keys(topicResultsById).map((id) => [id, !!selected[id]]))
);
</script>
<Modal {title} bind:show onSubmit={submit} on:close={reset}>
@@ -149,17 +156,17 @@
disabled={totalResults === 0 && !search}
placeholder="Search for topics"
bind:value={search} />
{#if Object.keys(topicResultsById).length > 0 && !emptyTopicsExists}
{#if topicsWithState.length > 0 && !emptyTopicsExists}
<Table.Root columns={1} let:root>
{#each Object.entries(topicResultsById) as [topicId, topic]}
{#each topicsWithState as { topicId, topic, checked, disabled } (topicId)}
<Table.Row.Base {root}>
<Table.Cell {root}>
<Layout.Stack direction="row" alignItems="center" gap="s">
<Selector.Checkbox
id={topicId}
label={topic.name}
disabled={!!topicsById[topicId]}
checked={topicSelectionStates[topicId] || false}
{checked}
{disabled}
on:change={(event) => onTopicSelection(event, topic)}>
</Selector.Checkbox>
<span>
@@ -185,7 +192,7 @@
<Empty
type="secondary"
title={`You have no topics${
emptyTopicsExists ? ` with ${providerType.toUpperCase()} targets` : ''
emptyTopicsExists ? ` with ${providerType.toLowerCase()} targets` : ''
}`}
description={`Create a topic to see them here.`}>
<Button
@@ -139,7 +139,7 @@
</script>
<Modal {title} bind:show onSubmit={submit} on:close={reset}>
<slot name="description" />
<slot name="description" slot="description" />
<Layout.Stack>
<InputSearch
autofocus