feat(backend,frontend): feed gets a bots tab so private is people only, a folder's context menu opens the feed of its chats
This commit is contained in:
1 parent
b9e8a96774
commit
c2180e80ad
11 files changed
+128
-16
No files matched your search
@@ -284,6 +284,7 @@ export interface FeedQuery {
|
||||
chat_id?: number;
|
||||
chat_kind?: FeedChatKind;
|
||||
cursor?: string;
|
||||
folder_id?: number;
|
||||
kind?: FeedKind;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export interface MessageVersion {
|
||||
}
|
||||
|
||||
export type FeedKind = "all" | "deleted" | "edited" | "profile";
|
||||
export type FeedChatKind = "dm" | "group" | "channel";
|
||||
export type FeedChatKind = "dm" | "bot" | "group" | "channel";
|
||||
|
||||
export interface FeedChat {
|
||||
chat_id: number;
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
import ContextMenu from "#lib/components/ui/ContextMenu.svelte";
|
||||
import ContextMenuItem from "#lib/components/ui/ContextMenuItem.svelte";
|
||||
import { t } from "#lib/i18n/locale.svelte.js";
|
||||
import { feed } from "#lib/stores/feed.svelte.js";
|
||||
import { folders } from "#lib/stores/folders.svelte.js";
|
||||
import { policyUi } from "#lib/stores/policy.svelte.js";
|
||||
import { page } from "$app/state";
|
||||
|
||||
interface Tab {
|
||||
id: number | null;
|
||||
@@ -93,6 +95,12 @@
|
||||
</button>
|
||||
{/snippet}
|
||||
{#snippet menu()}
|
||||
<ContextMenuItem
|
||||
icon="recent"
|
||||
onselect={() => feed.open(page.url, tab.id)}
|
||||
>
|
||||
{t("feed.folder")}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem icon="settings" onselect={() => editPolicy(tab)}>
|
||||
{t("settings.policy")}
|
||||
</ContextMenuItem>
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
<script lang="ts">
|
||||
import type { FeedChatKind, FeedKind } from "#lib/api/types.js";
|
||||
import Icon from "#lib/components/ui/Icon.svelte";
|
||||
import { t } from "#lib/i18n/locale.svelte.js";
|
||||
import { FEED_CHAT_KINDS, FEED_KINDS } from "#lib/stores/feed.svelte.js";
|
||||
import { folders } from "#lib/stores/folders.svelte.js";
|
||||
|
||||
interface Props {
|
||||
chats?: FeedChatKind | null;
|
||||
folder?: number | null;
|
||||
kind: FeedKind;
|
||||
onchats?: (chats: FeedChatKind | null) => void;
|
||||
onfolder?: (folder: number | null) => void;
|
||||
onkind: (kind: FeedKind) => void;
|
||||
}
|
||||
|
||||
let { kind, chats = null, onkind, onchats }: Props = $props();
|
||||
let {
|
||||
kind,
|
||||
chats = null,
|
||||
folder = null,
|
||||
onkind,
|
||||
onchats,
|
||||
onfolder,
|
||||
}: Props = $props();
|
||||
|
||||
const folderTitle = $derived(
|
||||
folders.list.find((item) => item.folder_id === folder)?.title ??
|
||||
t("feed.folder")
|
||||
);
|
||||
|
||||
const KIND_KEYS = {
|
||||
all: "feed.filter.all",
|
||||
@@ -20,6 +36,7 @@
|
||||
} as const;
|
||||
|
||||
const CHAT_KEYS = {
|
||||
bot: "feed.chats.bot",
|
||||
channel: "feed.chats.channel",
|
||||
dm: "feed.chats.dm",
|
||||
group: "feed.chats.group",
|
||||
@@ -55,6 +72,20 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if folder !== null && onfolder}
|
||||
<div class="group">
|
||||
<button
|
||||
aria-pressed="true"
|
||||
class="chip on"
|
||||
onclick={() => onfolder(null)}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="folder" size="0.875rem" />
|
||||
{folderTitle}
|
||||
<Icon name="close" size="0.875rem" />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -78,6 +109,7 @@
|
||||
cursor: pointer;
|
||||
|
||||
display: inline-flex;
|
||||
gap: 0.25rem;
|
||||
align-items: center;
|
||||
|
||||
height: 1.75rem;
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
interface Props {
|
||||
chatId?: number;
|
||||
chats?: FeedChatKind | null;
|
||||
folderId?: number | null;
|
||||
kind: FeedKind;
|
||||
onopen: (item: FeedItem, messageId: number) => void;
|
||||
}
|
||||
|
||||
let { kind, chats = null, chatId, onopen }: Props = $props();
|
||||
let { kind, chats = null, folderId = null, chatId, onopen }: Props = $props();
|
||||
|
||||
const PAGE = 30;
|
||||
const EMPTY_KEYS = {
|
||||
@@ -38,6 +39,7 @@
|
||||
const filter = $derived<FeedQuery>({
|
||||
chat_id: chatId,
|
||||
chat_kind: chats ?? undefined,
|
||||
folder_id: folderId ?? undefined,
|
||||
kind,
|
||||
limit: PAGE,
|
||||
});
|
||||
|
||||
@@ -55,12 +55,19 @@
|
||||
</header>
|
||||
<FeedFilters
|
||||
chats={filter.chats}
|
||||
folder={filter.folder}
|
||||
kind={filter.kind}
|
||||
onchats={(chats) => feed.filter(page.url, { ...filter, chats })}
|
||||
onfolder={(folder) => feed.filter(page.url, { ...filter, folder })}
|
||||
onkind={(kind) => feed.filter(page.url, { ...filter, kind })}
|
||||
/>
|
||||
<div class="feed-body custom-scroll">
|
||||
<FeedList chats={filter.chats} kind={filter.kind} onopen={open} />
|
||||
<FeedList
|
||||
chats={filter.chats}
|
||||
folderId={filter.folder}
|
||||
kind={filter.kind}
|
||||
onopen={open}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ export const feed = {
|
||||
},
|
||||
},
|
||||
"feed.changes": { en: "Changes", ru: "Изменения" },
|
||||
"feed.chats.bot": { en: "Bots", ru: "Боты" },
|
||||
"feed.chats.channel": { en: "Channels", ru: "Каналы" },
|
||||
"feed.chats.dm": { en: "Private", ru: "Личка" },
|
||||
"feed.chats.group": { en: "Groups", ru: "Группы" },
|
||||
@@ -92,6 +93,7 @@ export const feed = {
|
||||
"feed.filter.deleted": { en: "Deleted", ru: "Удалённые" },
|
||||
"feed.filter.edited": { en: "Edited", ru: "Изменённые" },
|
||||
"feed.filter.profile": { en: "Profiles", ru: "Профили" },
|
||||
"feed.folder": { en: "Folder feed", ru: "Лента папки" },
|
||||
"feed.lostText": {
|
||||
en: "The earlier text wasn't saved",
|
||||
ru: "Прежний текст не сохранился",
|
||||
|
||||
@@ -8,14 +8,21 @@ import { goto } from "$app/navigation";
|
||||
const LIVE_DELAY = 600;
|
||||
const KIND_PARAM = "feed";
|
||||
const CHATS_PARAM = "chats";
|
||||
const FOLDER_PARAM = "folder";
|
||||
const MESSAGE_PARAM = "message";
|
||||
export const MARK_PARAM = "mark";
|
||||
|
||||
export const FEED_KINDS: FeedKind[] = ["all", "deleted", "edited", "profile"];
|
||||
export const FEED_CHAT_KINDS: FeedChatKind[] = ["dm", "group", "channel"];
|
||||
export const FEED_CHAT_KINDS: FeedChatKind[] = [
|
||||
"dm",
|
||||
"bot",
|
||||
"group",
|
||||
"channel",
|
||||
];
|
||||
|
||||
export interface FeedFilter {
|
||||
chats: FeedChatKind | null;
|
||||
folder: number | null;
|
||||
kind: FeedKind;
|
||||
}
|
||||
|
||||
@@ -30,10 +37,12 @@ export function feedFilterOf(url: UrlLike): FeedFilter | null {
|
||||
return null;
|
||||
}
|
||||
const chats = searchParams.get(CHATS_PARAM);
|
||||
const folder = Number(searchParams.get(FOLDER_PARAM));
|
||||
return {
|
||||
chats: FEED_CHAT_KINDS.includes(chats as FeedChatKind)
|
||||
? (chats as FeedChatKind)
|
||||
: null,
|
||||
folder: Number.isInteger(folder) && folder > 0 ? folder : null,
|
||||
kind: kind as FeedKind,
|
||||
};
|
||||
}
|
||||
@@ -42,12 +51,16 @@ function withFilter(url: UrlLike, filter: FeedFilter | null): URL {
|
||||
const next = new URL(url.href);
|
||||
next.searchParams.delete(KIND_PARAM);
|
||||
next.searchParams.delete(CHATS_PARAM);
|
||||
next.searchParams.delete(FOLDER_PARAM);
|
||||
next.searchParams.delete(MESSAGE_PARAM);
|
||||
if (filter) {
|
||||
next.searchParams.set(KIND_PARAM, filter.kind);
|
||||
if (filter.chats) {
|
||||
next.searchParams.set(CHATS_PARAM, filter.chats);
|
||||
}
|
||||
if (filter.folder !== null) {
|
||||
next.searchParams.set(FOLDER_PARAM, String(filter.folder));
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
@@ -140,8 +153,10 @@ function createFeed() {
|
||||
unseen = next;
|
||||
}
|
||||
},
|
||||
async open(url: UrlLike) {
|
||||
const target = href(withFilter(url, { chats: null, kind: "all" }));
|
||||
async open(url: UrlLike, folder: number | null = null) {
|
||||
const target = href(
|
||||
withFilter(url, { chats: null, folder, kind: "all" })
|
||||
);
|
||||
openedHref = target;
|
||||
await goto(target, { reset: false });
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user