+47
-11
src/components/explore/my-feeds-section.tsx
+47
-11
src/components/explore/my-feeds-section.tsx
···
4
4
5
5
import type { SavedFeed } from '~/lib/preferences/account';
6
6
import { useSession } from '~/lib/states/session';
7
+
import { assertUnreachable } from '~/lib/utils/invariant';
7
8
import { reconcile } from '~/lib/utils/misc';
8
9
9
10
import Avatar from '../avatar';
11
+
import MagnifyingGlassOutlinedIcon from '../icons-central/magnifying-glass-outline';
10
12
11
13
const MyFeedsSection = () => {
12
14
const { currentAccount } = useSession();
···
16
18
}
17
19
18
20
const feeds = createMemo((prev: SavedFeed[] | undefined) => {
19
-
return reconcile(prev, currentAccount.preferences.feeds, (item) => item.info.uri);
21
+
return reconcile(prev, currentAccount.preferences.feeds, (feed) => {
22
+
switch (feed.type) {
23
+
case 'generator':
24
+
case 'list': {
25
+
return `${feed.type}:${feed.info.uri}`;
26
+
}
27
+
case 'search': {
28
+
return `${feed.type}:${feed.query}:${feed.kind}`;
29
+
}
30
+
}
31
+
});
20
32
});
21
33
22
34
return (
···
36
48
{(feed) => {
37
49
const type = feed.type;
38
50
39
-
let href: string | undefined;
40
-
{
41
-
const uri = parseAtUri(feed.info.uri);
42
-
if (type === 'generator') {
51
+
let href: string;
52
+
switch (type) {
53
+
case 'generator': {
54
+
const uri = parseAtUri(feed.info.uri);
43
55
href = `/${uri.repo}/feeds/${uri.rkey}`;
44
-
} else if (type === 'list') {
56
+
break;
57
+
}
58
+
case 'list': {
59
+
const uri = parseAtUri(feed.info.uri);
45
60
href = `/${uri.repo}/lists/${uri.rkey}`;
61
+
break;
62
+
}
63
+
case 'search': {
64
+
href = `/search?q=${encodeURIComponent(feed.query)}&t=${feed.kind}`;
65
+
break;
66
+
}
67
+
default: {
68
+
assertUnreachable(feed);
46
69
}
47
70
}
48
71
···
51
74
href={href}
52
75
class="flex items-center gap-4 px-4 py-3 hover:bg-contrast/sm-pressed active:bg-contrast/md"
53
76
>
54
-
<Avatar type={feed.type} src={feed.info.avatar} />
77
+
{type === 'generator' || type === 'list' ? (
78
+
<Avatar type={type} src={feed.info.avatar} />
79
+
) : type === 'search' ? (
80
+
<div class="grid h-9 w-9 place-items-center rounded-md bg-accent text-xl text-accent-fg">
81
+
<MagnifyingGlassOutlinedIcon />
82
+
</div>
83
+
) : null}
84
+
55
85
<span class="text-sm font-bold">
56
86
{(() => {
57
-
if (type === 'generator') {
58
-
return feed.info.displayName;
59
-
} else if (type === 'list') {
60
-
return feed.info.name;
87
+
switch (type) {
88
+
case 'generator': {
89
+
return feed.info.displayName;
90
+
}
91
+
case 'list': {
92
+
return feed.info.name;
93
+
}
94
+
case 'search': {
95
+
return feed.name || feed.query;
96
+
}
61
97
}
62
98
})()}
63
99
</span>
+8
-1
src/lib/preferences/account.ts
+8
-1
src/lib/preferences/account.ts
···
31
31
definitions: Record<At.DID, ModerationLabeler>;
32
32
}
33
33
34
-
export type SavedFeed = SavedGeneratorFeed | SavedListFeed;
34
+
export type SavedFeed = SavedGeneratorFeed | SavedListFeed | SavedSearchFeed;
35
35
36
36
export interface SavedGeneratorFeed {
37
37
readonly type: 'generator';
···
43
43
readonly type: 'list';
44
44
pinned: boolean;
45
45
info: AppBskyGraphDefs.ListView;
46
+
}
47
+
48
+
export interface SavedSearchFeed {
49
+
readonly type: 'search';
50
+
name: string;
51
+
query: string;
52
+
kind: string;
46
53
}
47
54
48
55
export interface PersistedThreadgate {
+11
-2
src/views/search.tsx
+11
-2
src/views/search.tsx
···
3
3
import { tokenize } from '@atcute/bluesky-search-parser';
4
4
import { Freeze, ShowFreeze } from '@mary/solid-freeze';
5
5
6
-
import { hasModals } from '~/globals/modals';
6
+
import { hasModals, openModal } from '~/globals/modals';
7
7
8
8
import { parseEndDate, parseStartDate, splitFilters, stringifySearch } from '~/lib/bsky/search';
9
9
import { createDerivedSignal } from '~/lib/hooks/derived-signal';
···
18
18
import SearchBar from '~/components/main/search-bar';
19
19
import * as Page from '~/components/page';
20
20
import { SearchBarProvider } from '~/components/search/context';
21
+
import SearchOverflowMenu from '~/components/search/search-overflow-menu';
21
22
import SearchSuggestionsView from '~/components/search/search-suggestions-view';
22
23
import TabBar from '~/components/tab-bar';
23
24
···
93
94
94
95
{!isInputFocused() && (
95
96
<Page.HeaderAccessory>
96
-
<IconButton icon={MoreHorizOutlinedIcon} title="Search actions" />
97
+
<IconButton
98
+
icon={MoreHorizOutlinedIcon}
99
+
title="Search actions"
100
+
onClick={(ev) => {
101
+
const anchor = ev.currentTarget;
102
+
103
+
openModal(() => <SearchOverflowMenu anchor={anchor} query={params.q} kind={params.t} />);
104
+
}}
105
+
/>
97
106
</Page.HeaderAccessory>
98
107
)}
99
108
</Page.Header>