mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2497 from appwrite/fix-SER-SER-416-More-Messaging-flow-issues
This commit is contained in:
@@ -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} />
|
||||
|
||||
@@ -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} />
|
||||
|
||||
+1
-1
@@ -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" />
|
||||
|
||||
+21
-12
@@ -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">
|
||||
|
||||
+1
-1
@@ -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">
|
||||
|
||||
+6
-9
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user