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
@@ -298,10 +298,11 @@ async def get_feed(
|
|||||||
"""Deleted and edited messages and profile changes across chats, newest first.
|
"""Deleted and edited messages and profile changes across chats, newest first.
|
||||||
|
|
||||||
`kind` picks deleted, edited, profile, messages (deleted and edited) or all;
|
`kind` picks deleted, edited, profile, messages (deleted and edited) or all;
|
||||||
`since` keeps items at or after that time; `chat_id` or `chat_kind` (dm,
|
`since` keeps items at or after that time; `chat_id` or `chat_kind` (dm
|
||||||
group, channel) narrow it down. Edited items carry `before` and `after`
|
with people, bot, group, channel) narrow it down. Edited items carry
|
||||||
texts. A deleted item with `message_count` above 1 is a bulk deletion in
|
`before` and `after` texts. A deleted item with `message_count` above 1 is
|
||||||
one chat; read its messages with `get_deleted_messages` for that chat.
|
a bulk deletion in one chat; read its messages with `get_deleted_messages`
|
||||||
|
for that chat.
|
||||||
Profile items cover people in private chats or contacts and chats in the
|
Profile items cover people in private chats or contacts and chats in the
|
||||||
dialog list: `detail` maps `name`, `username`, `photo` (unique file ids) or
|
dialog list: `detail` maps `name`, `username`, `photo` (unique file ids) or
|
||||||
`title` to `[old, new]`, changes a day apart or closer are merged. Pass
|
`title` to `[old, new]`, changes a day apart or closer are merged. Pass
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ async def list_feed(
|
|||||||
kind: Annotated[FeedKind, Query()] = FeedKind.ALL,
|
kind: Annotated[FeedKind, Query()] = FeedKind.ALL,
|
||||||
chat_id: Annotated[int | None, Query()] = None,
|
chat_id: Annotated[int | None, Query()] = None,
|
||||||
chat_kind: Annotated[FeedChatKind | None, Query()] = None,
|
chat_kind: Annotated[FeedChatKind | None, Query()] = None,
|
||||||
|
folder_id: Annotated[int | None, Query()] = None,
|
||||||
since: Annotated[datetime | None, Query()] = None,
|
since: Annotated[datetime | None, Query()] = None,
|
||||||
cursor: Cursor = None,
|
cursor: Cursor = None,
|
||||||
limit: Limit = DEFAULT_LIMIT,
|
limit: Limit = DEFAULT_LIMIT,
|
||||||
@@ -36,7 +37,13 @@ async def list_feed(
|
|||||||
return await feed.list_feed(
|
return await feed.list_feed(
|
||||||
pool,
|
pool,
|
||||||
account_id,
|
account_id,
|
||||||
FeedFilter(kind=kind, chat_id=chat_id, chat_kind=chat_kind, since=since),
|
FeedFilter(
|
||||||
|
kind=kind,
|
||||||
|
chat_id=chat_id,
|
||||||
|
chat_kind=chat_kind,
|
||||||
|
folder_id=folder_id,
|
||||||
|
since=since,
|
||||||
|
),
|
||||||
limit=limit,
|
limit=limit,
|
||||||
cursor=cursor,
|
cursor=cursor,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from enum import StrEnum
|
|||||||
|
|
||||||
import asyncpg
|
import asyncpg
|
||||||
|
|
||||||
|
from utils.policy import repository as policy_repository
|
||||||
from utils.read import chats, peers
|
from utils.read import chats, peers
|
||||||
from utils.read.accounts import self_user_id
|
from utils.read.accounts import self_user_id
|
||||||
from utils.read.models import (
|
from utils.read.models import (
|
||||||
@@ -45,6 +46,13 @@ LEFT JOIN LATERAL (
|
|||||||
) p ON true
|
) p ON true
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_IS_BOT = """EXISTS (
|
||||||
|
SELECT 1 FROM peers p
|
||||||
|
WHERE p.account_id = feed_items.account_id AND p.peer_id = feed_items.chat_id
|
||||||
|
AND COALESCE((p.raw->>'is_bot')::bool, (p.raw->>'bot')::bool,
|
||||||
|
p.raw->>'type' = 'ChatType.BOT', false)
|
||||||
|
)"""
|
||||||
|
|
||||||
_UNSEEN = """
|
_UNSEEN = """
|
||||||
SELECT count(*) FILTER (WHERE kind = 'deleted') AS deleted,
|
SELECT count(*) FILTER (WHERE kind = 'deleted') AS deleted,
|
||||||
count(*) FILTER (WHERE kind = 'edited') AS edited
|
count(*) FILTER (WHERE kind = 'edited') AS edited
|
||||||
@@ -69,15 +77,25 @@ class FeedKind(StrEnum):
|
|||||||
|
|
||||||
class FeedChatKind(StrEnum):
|
class FeedChatKind(StrEnum):
|
||||||
DM = "dm"
|
DM = "dm"
|
||||||
|
BOT = "bot"
|
||||||
GROUP = "group"
|
GROUP = "group"
|
||||||
CHANNEL = "channel"
|
CHANNEL = "channel"
|
||||||
|
|
||||||
|
|
||||||
|
_CHAT_KIND_CLAUSES = {
|
||||||
|
FeedChatKind.DM: f"chat_kind = 'dm' AND NOT {_IS_BOT}",
|
||||||
|
FeedChatKind.BOT: f"chat_kind = 'dm' AND {_IS_BOT}",
|
||||||
|
FeedChatKind.GROUP: "chat_kind = 'group'",
|
||||||
|
FeedChatKind.CHANNEL: "chat_kind = 'channel'",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class FeedFilter:
|
class FeedFilter:
|
||||||
kind: FeedKind = FeedKind.ALL
|
kind: FeedKind = FeedKind.ALL
|
||||||
chat_id: int | None = None
|
chat_id: int | None = None
|
||||||
chat_kind: FeedChatKind | None = None
|
chat_kind: FeedChatKind | None = None
|
||||||
|
folder_id: int | None = None
|
||||||
since: datetime | None = None
|
since: datetime | None = None
|
||||||
|
|
||||||
|
|
||||||
@@ -90,18 +108,34 @@ def decode_cursor(cursor: str) -> tuple[datetime, int]:
|
|||||||
return _EPOCH + int(micros) * _MICROSECOND, int(key)
|
return _EPOCH + int(micros) * _MICROSECOND, int(key)
|
||||||
|
|
||||||
|
|
||||||
|
async def _folder_chat_ids(
|
||||||
|
pool: asyncpg.Pool, account_id: int, folder_id: int | None
|
||||||
|
) -> list[int] | None:
|
||||||
|
if folder_id is None:
|
||||||
|
return None
|
||||||
|
folder = await policy_repository.get_folder(pool, account_id, folder_id)
|
||||||
|
if folder is None:
|
||||||
|
return []
|
||||||
|
return await chats.folder_chat_ids(pool, account_id, folder)
|
||||||
|
|
||||||
|
|
||||||
def _where(
|
def _where(
|
||||||
account_id: int, feed: FeedFilter, cursor: str | None
|
account_id: int,
|
||||||
|
feed: FeedFilter,
|
||||||
|
folder_chat_ids: list[int] | None,
|
||||||
|
cursor: str | None,
|
||||||
) -> tuple[str, list[object]]:
|
) -> tuple[str, list[object]]:
|
||||||
params: list[object] = [account_id]
|
params: list[object] = [account_id]
|
||||||
clauses = ["account_id = $1"]
|
clauses = ["account_id = $1"]
|
||||||
if feed.kind is FeedKind.MESSAGES:
|
if feed.kind is FeedKind.MESSAGES:
|
||||||
clauses.append("kind <> 'profile'")
|
clauses.append("kind <> 'profile'")
|
||||||
|
if feed.chat_kind is not None:
|
||||||
|
clauses.append(_CHAT_KIND_CLAUSES[feed.chat_kind])
|
||||||
exact = feed.kind not in {FeedKind.ALL, FeedKind.MESSAGES}
|
exact = feed.kind not in {FeedKind.ALL, FeedKind.MESSAGES}
|
||||||
conditions = (
|
conditions = (
|
||||||
("kind = {}", feed.kind.value if exact else None),
|
("kind = {}", feed.kind.value if exact else None),
|
||||||
("chat_id = {}", feed.chat_id),
|
("chat_id = {}", feed.chat_id),
|
||||||
("chat_kind = {}", feed.chat_kind.value if feed.chat_kind else None),
|
("chat_id = ANY({}::bigint[])", folder_chat_ids),
|
||||||
("at >= {}", feed.since),
|
("at >= {}", feed.since),
|
||||||
)
|
)
|
||||||
for template, value in conditions:
|
for template, value in conditions:
|
||||||
@@ -191,7 +225,9 @@ async def _build_items(
|
|||||||
chat=FeedChat(
|
chat=FeedChat(
|
||||||
chat_id=row["chat_id"],
|
chat_id=row["chat_id"],
|
||||||
title=chat.title if chat else None,
|
title=chat.title if chat else None,
|
||||||
kind=row["chat_kind"],
|
kind=FeedChatKind.BOT
|
||||||
|
if chat and chat.is_bot and row["chat_kind"] == FeedChatKind.DM
|
||||||
|
else row["chat_kind"],
|
||||||
has_avatar=chat.has_avatar if chat else False,
|
has_avatar=chat.has_avatar if chat else False,
|
||||||
),
|
),
|
||||||
message_id=row["message_id"],
|
message_id=row["message_id"],
|
||||||
@@ -218,7 +254,8 @@ async def list_feed(
|
|||||||
cursor: str | None = None,
|
cursor: str | None = None,
|
||||||
) -> FeedPage:
|
) -> FeedPage:
|
||||||
size = max(1, min(limit, MAX_LIMIT))
|
size = max(1, min(limit, MAX_LIMIT))
|
||||||
where, params = _where(account_id, feed, cursor)
|
folder_chat_ids = await _folder_chat_ids(pool, account_id, feed.folder_id)
|
||||||
|
where, params = _where(account_id, feed, folder_chat_ids, cursor)
|
||||||
params.append(size + 1)
|
params.append(size + 1)
|
||||||
rows = await pool.fetch(
|
rows = await pool.fetch(
|
||||||
f"SELECT {_ITEM_COLS} FROM feed_items WHERE {where} " # noqa: S608
|
f"SELECT {_ITEM_COLS} FROM feed_items WHERE {where} " # noqa: S608
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ export interface FeedQuery {
|
|||||||
chat_id?: number;
|
chat_id?: number;
|
||||||
chat_kind?: FeedChatKind;
|
chat_kind?: FeedChatKind;
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
|
folder_id?: number;
|
||||||
kind?: FeedKind;
|
kind?: FeedKind;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ export interface MessageVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type FeedKind = "all" | "deleted" | "edited" | "profile";
|
export type FeedKind = "all" | "deleted" | "edited" | "profile";
|
||||||
export type FeedChatKind = "dm" | "group" | "channel";
|
export type FeedChatKind = "dm" | "bot" | "group" | "channel";
|
||||||
|
|
||||||
export interface FeedChat {
|
export interface FeedChat {
|
||||||
chat_id: number;
|
chat_id: number;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
import ContextMenu from "#lib/components/ui/ContextMenu.svelte";
|
import ContextMenu from "#lib/components/ui/ContextMenu.svelte";
|
||||||
import ContextMenuItem from "#lib/components/ui/ContextMenuItem.svelte";
|
import ContextMenuItem from "#lib/components/ui/ContextMenuItem.svelte";
|
||||||
import { t } from "#lib/i18n/locale.svelte.js";
|
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 { folders } from "#lib/stores/folders.svelte.js";
|
||||||
import { policyUi } from "#lib/stores/policy.svelte.js";
|
import { policyUi } from "#lib/stores/policy.svelte.js";
|
||||||
|
import { page } from "$app/state";
|
||||||
|
|
||||||
interface Tab {
|
interface Tab {
|
||||||
id: number | null;
|
id: number | null;
|
||||||
@@ -93,6 +95,12 @@
|
|||||||
</button>
|
</button>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet menu()}
|
{#snippet menu()}
|
||||||
|
<ContextMenuItem
|
||||||
|
icon="recent"
|
||||||
|
onselect={() => feed.open(page.url, tab.id)}
|
||||||
|
>
|
||||||
|
{t("feed.folder")}
|
||||||
|
</ContextMenuItem>
|
||||||
<ContextMenuItem icon="settings" onselect={() => editPolicy(tab)}>
|
<ContextMenuItem icon="settings" onselect={() => editPolicy(tab)}>
|
||||||
{t("settings.policy")}
|
{t("settings.policy")}
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
|
|||||||
@@ -1,16 +1,32 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { FeedChatKind, FeedKind } from "#lib/api/types.js";
|
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 { t } from "#lib/i18n/locale.svelte.js";
|
||||||
import { FEED_CHAT_KINDS, FEED_KINDS } from "#lib/stores/feed.svelte.js";
|
import { FEED_CHAT_KINDS, FEED_KINDS } from "#lib/stores/feed.svelte.js";
|
||||||
|
import { folders } from "#lib/stores/folders.svelte.js";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chats?: FeedChatKind | null;
|
chats?: FeedChatKind | null;
|
||||||
|
folder?: number | null;
|
||||||
kind: FeedKind;
|
kind: FeedKind;
|
||||||
onchats?: (chats: FeedChatKind | null) => void;
|
onchats?: (chats: FeedChatKind | null) => void;
|
||||||
|
onfolder?: (folder: number | null) => void;
|
||||||
onkind: (kind: FeedKind) => 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 = {
|
const KIND_KEYS = {
|
||||||
all: "feed.filter.all",
|
all: "feed.filter.all",
|
||||||
@@ -20,6 +36,7 @@
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const CHAT_KEYS = {
|
const CHAT_KEYS = {
|
||||||
|
bot: "feed.chats.bot",
|
||||||
channel: "feed.chats.channel",
|
channel: "feed.chats.channel",
|
||||||
dm: "feed.chats.dm",
|
dm: "feed.chats.dm",
|
||||||
group: "feed.chats.group",
|
group: "feed.chats.group",
|
||||||
@@ -55,6 +72,20 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -78,6 +109,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
gap: 0.25rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
height: 1.75rem;
|
height: 1.75rem;
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
interface Props {
|
interface Props {
|
||||||
chatId?: number;
|
chatId?: number;
|
||||||
chats?: FeedChatKind | null;
|
chats?: FeedChatKind | null;
|
||||||
|
folderId?: number | null;
|
||||||
kind: FeedKind;
|
kind: FeedKind;
|
||||||
onopen: (item: FeedItem, messageId: number) => void;
|
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 PAGE = 30;
|
||||||
const EMPTY_KEYS = {
|
const EMPTY_KEYS = {
|
||||||
@@ -38,6 +39,7 @@
|
|||||||
const filter = $derived<FeedQuery>({
|
const filter = $derived<FeedQuery>({
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
chat_kind: chats ?? undefined,
|
chat_kind: chats ?? undefined,
|
||||||
|
folder_id: folderId ?? undefined,
|
||||||
kind,
|
kind,
|
||||||
limit: PAGE,
|
limit: PAGE,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,12 +55,19 @@
|
|||||||
</header>
|
</header>
|
||||||
<FeedFilters
|
<FeedFilters
|
||||||
chats={filter.chats}
|
chats={filter.chats}
|
||||||
|
folder={filter.folder}
|
||||||
kind={filter.kind}
|
kind={filter.kind}
|
||||||
onchats={(chats) => feed.filter(page.url, { ...filter, chats })}
|
onchats={(chats) => feed.filter(page.url, { ...filter, chats })}
|
||||||
|
onfolder={(folder) => feed.filter(page.url, { ...filter, folder })}
|
||||||
onkind={(kind) => feed.filter(page.url, { ...filter, kind })}
|
onkind={(kind) => feed.filter(page.url, { ...filter, kind })}
|
||||||
/>
|
/>
|
||||||
<div class="feed-body custom-scroll">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export const feed = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"feed.changes": { en: "Changes", ru: "Изменения" },
|
"feed.changes": { en: "Changes", ru: "Изменения" },
|
||||||
|
"feed.chats.bot": { en: "Bots", ru: "Боты" },
|
||||||
"feed.chats.channel": { en: "Channels", ru: "Каналы" },
|
"feed.chats.channel": { en: "Channels", ru: "Каналы" },
|
||||||
"feed.chats.dm": { en: "Private", ru: "Личка" },
|
"feed.chats.dm": { en: "Private", ru: "Личка" },
|
||||||
"feed.chats.group": { en: "Groups", ru: "Группы" },
|
"feed.chats.group": { en: "Groups", ru: "Группы" },
|
||||||
@@ -92,6 +93,7 @@ export const feed = {
|
|||||||
"feed.filter.deleted": { en: "Deleted", ru: "Удалённые" },
|
"feed.filter.deleted": { en: "Deleted", ru: "Удалённые" },
|
||||||
"feed.filter.edited": { en: "Edited", ru: "Изменённые" },
|
"feed.filter.edited": { en: "Edited", ru: "Изменённые" },
|
||||||
"feed.filter.profile": { en: "Profiles", ru: "Профили" },
|
"feed.filter.profile": { en: "Profiles", ru: "Профили" },
|
||||||
|
"feed.folder": { en: "Folder feed", ru: "Лента папки" },
|
||||||
"feed.lostText": {
|
"feed.lostText": {
|
||||||
en: "The earlier text wasn't saved",
|
en: "The earlier text wasn't saved",
|
||||||
ru: "Прежний текст не сохранился",
|
ru: "Прежний текст не сохранился",
|
||||||
|
|||||||
@@ -8,14 +8,21 @@ import { goto } from "$app/navigation";
|
|||||||
const LIVE_DELAY = 600;
|
const LIVE_DELAY = 600;
|
||||||
const KIND_PARAM = "feed";
|
const KIND_PARAM = "feed";
|
||||||
const CHATS_PARAM = "chats";
|
const CHATS_PARAM = "chats";
|
||||||
|
const FOLDER_PARAM = "folder";
|
||||||
const MESSAGE_PARAM = "message";
|
const MESSAGE_PARAM = "message";
|
||||||
export const MARK_PARAM = "mark";
|
export const MARK_PARAM = "mark";
|
||||||
|
|
||||||
export const FEED_KINDS: FeedKind[] = ["all", "deleted", "edited", "profile"];
|
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 {
|
export interface FeedFilter {
|
||||||
chats: FeedChatKind | null;
|
chats: FeedChatKind | null;
|
||||||
|
folder: number | null;
|
||||||
kind: FeedKind;
|
kind: FeedKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,10 +37,12 @@ export function feedFilterOf(url: UrlLike): FeedFilter | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const chats = searchParams.get(CHATS_PARAM);
|
const chats = searchParams.get(CHATS_PARAM);
|
||||||
|
const folder = Number(searchParams.get(FOLDER_PARAM));
|
||||||
return {
|
return {
|
||||||
chats: FEED_CHAT_KINDS.includes(chats as FeedChatKind)
|
chats: FEED_CHAT_KINDS.includes(chats as FeedChatKind)
|
||||||
? (chats as FeedChatKind)
|
? (chats as FeedChatKind)
|
||||||
: null,
|
: null,
|
||||||
|
folder: Number.isInteger(folder) && folder > 0 ? folder : null,
|
||||||
kind: kind as FeedKind,
|
kind: kind as FeedKind,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -42,12 +51,16 @@ function withFilter(url: UrlLike, filter: FeedFilter | null): URL {
|
|||||||
const next = new URL(url.href);
|
const next = new URL(url.href);
|
||||||
next.searchParams.delete(KIND_PARAM);
|
next.searchParams.delete(KIND_PARAM);
|
||||||
next.searchParams.delete(CHATS_PARAM);
|
next.searchParams.delete(CHATS_PARAM);
|
||||||
|
next.searchParams.delete(FOLDER_PARAM);
|
||||||
next.searchParams.delete(MESSAGE_PARAM);
|
next.searchParams.delete(MESSAGE_PARAM);
|
||||||
if (filter) {
|
if (filter) {
|
||||||
next.searchParams.set(KIND_PARAM, filter.kind);
|
next.searchParams.set(KIND_PARAM, filter.kind);
|
||||||
if (filter.chats) {
|
if (filter.chats) {
|
||||||
next.searchParams.set(CHATS_PARAM, filter.chats);
|
next.searchParams.set(CHATS_PARAM, filter.chats);
|
||||||
}
|
}
|
||||||
|
if (filter.folder !== null) {
|
||||||
|
next.searchParams.set(FOLDER_PARAM, String(filter.folder));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
@@ -140,8 +153,10 @@ function createFeed() {
|
|||||||
unseen = next;
|
unseen = next;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async open(url: UrlLike) {
|
async open(url: UrlLike, folder: number | null = null) {
|
||||||
const target = href(withFilter(url, { chats: null, kind: "all" }));
|
const target = href(
|
||||||
|
withFilter(url, { chats: null, folder, kind: "all" })
|
||||||
|
);
|
||||||
openedHref = target;
|
openedHref = target;
|
||||||
await goto(target, { reset: false });
|
await goto(target, { reset: false });
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user