+1
package.json
+1
package.json
···
20
20
"@atcute/tid": "^1.0.2",
21
21
"@floating-ui/dom": "^1.6.13",
22
22
"@floating-ui/utils": "^0.2.9",
23
+
"@mary/array-fns": "npm:@jsr/mary__array-fns@^0.1.0",
23
24
"@mary/async-iterator-fns": "npm:@jsr/mary__async-iterator-fns@^0.1.1",
24
25
"@mary/date-fns": "npm:@jsr/mary__date-fns@^0.1.2",
25
26
"@mary/events": "npm:@jsr/mary__events@^0.1.0",
+8
pnpm-lock.yaml
+8
pnpm-lock.yaml
···
66
66
'@floating-ui/utils':
67
67
specifier: ^0.2.9
68
68
version: 0.2.9(patch_hash=1cf283fbaa686f96f7b3029bbc0955bff698af6f4a75ba6c276ad1a7a7c40aea)
69
+
'@mary/array-fns':
70
+
specifier: npm:@jsr/mary__array-fns@^0.1.0
71
+
version: '@jsr/mary__array-fns@0.1.0'
69
72
'@mary/async-iterator-fns':
70
73
specifier: npm:@jsr/mary__async-iterator-fns@^0.1.1
71
74
version: '@jsr/mary__async-iterator-fns@0.1.1'
···
1081
1084
1082
1085
'@jridgewell/trace-mapping@0.3.9':
1083
1086
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
1087
+
1088
+
'@jsr/mary__array-fns@0.1.0':
1089
+
resolution: {integrity: sha512-rG8Ng1Arl86T1Lv4of4LC8OcdpGxcxG/j8K01K6lyG0lI9imLT7e6FknuMVs5MQ5jH6NUBYnPOfmVAv4sd/OkA==, tarball: https://npm.jsr.io/~/11/@jsr/mary__array-fns/0.1.0.tgz}
1084
1090
1085
1091
'@jsr/mary__async-iterator-fns@0.1.1':
1086
1092
resolution: {integrity: sha512-ef/TDpu6yGTAb4fbGEjSFPO7u49WrxJeXa9T6fvmjlpEfR84qxrjTR6MEUQ1hMySD0+O9yQKvBl/KL5x7K9+iA==, tarball: https://npm.jsr.io/~/11/@jsr/mary__async-iterator-fns/0.1.1.tgz}
···
3473
3479
dependencies:
3474
3480
'@jridgewell/resolve-uri': 3.1.2
3475
3481
'@jridgewell/sourcemap-codec': 1.5.0
3482
+
3483
+
'@jsr/mary__array-fns@0.1.0': {}
3476
3484
3477
3485
'@jsr/mary__async-iterator-fns@0.1.1': {}
3478
3486
+1
-2
src/api/moderation/labeler.ts
+1
-2
src/api/moderation/labeler.ts
+1
-1
src/api/queries/bookmark-feed.ts
+1
-1
src/api/queries/bookmark-feed.ts
···
1
1
import { tokenize } from '@atcute/bluesky-search-parser';
2
2
import type { AppBskyFeedDefs } from '@atcute/client/lexicons';
3
+
import { mapDefined } from '@mary/array-fns';
3
4
import { filter, map, take, toArray } from '@mary/async-iterator-fns';
4
5
import { createInfiniteQuery, createQuery } from '@mary/solid-query';
5
6
···
7
8
import { createSearchPredicate } from '~/lib/aglais-bookmarks/search';
8
9
import { useAgent } from '~/lib/states/agent';
9
10
import { useBookmarks } from '~/lib/states/bookmarks';
10
-
import { mapDefined } from '~/lib/utils/misc';
11
11
12
12
export const createBookmarkFolderMetaQuery = (tagId: () => string) => {
13
13
const bookmarks = useBookmarks();
+1
-2
src/api/queries/notification-feed.tsx
+1
-2
src/api/queries/notification-feed.tsx
···
1
1
import { createSignal } from 'solid-js';
2
2
3
3
import type { AppBskyFeedDefs, AppBskyNotificationListNotifications } from '@atcute/client/lexicons';
4
+
import { chunked, mapDefined } from '@mary/array-fns';
4
5
import { type QueryFunctionContext as QC, createInfiniteQuery, useQueryClient } from '@mary/solid-query';
5
6
6
7
import { useAgent } from '~/lib/states/agent';
7
-
import { mapDefined } from '~/lib/utils/misc';
8
8
9
9
import { dequal } from '../utils/dequal';
10
-
import { chunked } from '../utils/misc';
11
10
import { resetInfiniteData } from '../utils/query';
12
11
import { parseAtUri } from '../utils/strings';
13
12
+3
-2
src/api/queries/timeline.ts
+3
-2
src/api/queries/timeline.ts
···
8
8
AppBskyFeedPost,
9
9
At,
10
10
} from '@atcute/client/lexicons';
11
+
import { type FalsyValue, definite } from '@mary/array-fns';
11
12
import { type InfiniteData, createInfiniteQuery, createQuery, useQueryClient } from '@mary/solid-query';
12
13
13
14
import { globalEvents } from '~/globals/events';
···
412
413
/// Timeline filters
413
414
type FilterFn<T> = (data: T) => boolean;
414
415
415
-
const combine = <T>(filters: Array<undefined | false | FilterFn<T>>): FilterFn<T> | undefined => {
416
-
const filtered = filters.filter((filter): filter is FilterFn<T> => !!filter);
416
+
const combine = <T>(filters: Array<FalsyValue | FilterFn<T>>): FilterFn<T> | undefined => {
417
+
const filtered = definite(filters);
417
418
const len = filtered.length;
418
419
419
420
// if (len === 1) {
-10
src/api/utils/misc.ts
-10
src/api/utils/misc.ts
···
1
1
export const getCurrentDate = () => {
2
2
return new Date().toISOString();
3
3
};
4
-
5
-
export const chunked = <T>(arr: T[], size: number): T[][] => {
6
-
const chunks: T[][] = [];
7
-
8
-
for (let i = 0, il = arr.length; i < il; i += size) {
9
-
chunks.push(arr.slice(i, i + size));
10
-
}
11
-
12
-
return chunks;
13
-
};
+3
-2
src/components/bookmarks/add-post-to-folder-dialog.tsx
+3
-2
src/components/bookmarks/add-post-to-folder-dialog.tsx
···
1
1
import { For, createMemo } from 'solid-js';
2
2
3
3
import type { AppBskyFeedDefs } from '@atcute/client/lexicons';
4
+
import { intersection } from '@mary/array-fns';
4
5
import { useQueryClient } from '@mary/solid-query';
5
6
6
7
import { createBookmarkMetaQuery } from '~/api/queries/bookmark';
···
47
48
const existing = entry.data.item;
48
49
49
50
// Prune folders that we don't recognize
50
-
const availableFolderIds = new Set(meta.data!.tags.map((tag) => tag.id));
51
-
const intersect = new Set(folderIds()).intersection(availableFolderIds);
51
+
const availableFolderIds = meta.data!.tags.map((tag) => tag.id);
52
+
const intersect = intersection(folderIds(), availableFolderIds);
52
53
53
54
await db.put('bookmarks', {
54
55
view: post,
+2
-1
src/components/composer/dialogs/language-select-dialog.tsx
+2
-1
src/components/composer/dialogs/language-select-dialog.tsx
···
1
1
import { For, createMemo, createSignal, untrack } from 'solid-js';
2
2
3
+
import { mapDefined } from '@mary/array-fns';
4
+
3
5
import { dequal } from '~/api/utils/dequal';
4
6
5
7
import { useModalContext } from '~/globals/modals';
6
8
7
9
import { LANGUAGE_CODES, getEnglishLanguageName, getNativeLanguageName } from '~/lib/intl/languages';
8
-
import { mapDefined } from '~/lib/utils/misc';
9
10
10
11
import Button from '../../button';
11
12
import * as Dialog from '../../dialog';
+2
-1
src/components/composer/gifs/gif-search-dialog.tsx
+2
-1
src/components/composer/gifs/gif-search-dialog.tsx
···
1
1
import { For, Match, Switch, createSignal } from 'solid-js';
2
2
3
+
import { chunked } from '@mary/array-fns';
4
+
3
5
import { type Gif, createGifSearchQuery } from '~/api/queries/composer-gif';
4
-
import { chunked } from '~/api/utils/misc';
5
6
6
7
import { useModalContext } from '~/globals/modals';
7
8
+1
-2
src/components/date-picker/date-picker.tsx
+1
-2
src/components/date-picker/date-picker.tsx
···
1
1
import { createMemo } from 'solid-js';
2
2
3
+
import { chunked } from '@mary/array-fns';
3
4
import {
4
5
addDays,
5
6
addMonths,
···
16
17
startOfMonth,
17
18
startOfWeek,
18
19
} from '@mary/date-fns';
19
-
20
-
import { chunked } from '~/api/utils/misc';
21
20
22
21
import { createDerivedSignal } from '~/lib/hooks/derived-signal';
23
22
+2
-2
src/components/threads/post-translation.tsx
+2
-2
src/components/threads/post-translation.tsx
···
1
1
import { Match, Switch, createMemo, createSignal } from 'solid-js';
2
2
3
3
import { XRPC, simpleFetchHandler } from '@atcute/client';
4
+
import { sampleOne } from '@mary/array-fns';
4
5
import { createQuery } from '@mary/solid-query';
5
6
6
7
import { systemLanguages } from '~/globals/locales';
···
8
9
import { createDerivedSignal } from '~/lib/hooks/derived-signal';
9
10
import { getEnglishLanguageName } from '~/lib/intl/languages';
10
11
import { useSession } from '~/lib/states/session';
11
-
import { pickRandom } from '~/lib/utils/misc';
12
12
13
13
import CircularProgressView from '~/components/circular-progress-view';
14
14
···
37
37
return previous;
38
38
}
39
39
40
-
return pickRandom(instances);
40
+
return sampleOne(instances)!;
41
41
});
42
42
43
43
const query = createQuery(() => {
+3
-1
src/components/threads/thread-lines.tsx
+3
-1
src/components/threads/thread-lines.tsx
···
1
1
import { type JSX, createMemo } from 'solid-js';
2
2
3
+
import { mapDefined } from '@mary/array-fns';
4
+
3
5
import { LineType } from '~/api/models/post-thread';
4
6
import { EQUALS_DEQUAL } from '~/api/utils/dequal';
5
7
6
-
import { mapDefined, on } from '~/lib/utils/misc';
8
+
import { on } from '~/lib/utils/misc';
7
9
8
10
export interface ThreadLinesProps {
9
11
lines: LineType[] | undefined;
+2
-2
src/globals/locales.ts
+2
-2
src/globals/locales.ts
···
1
-
import { uniq } from '~/lib/utils/misc';
1
+
import { unique } from '@mary/array-fns';
2
2
3
-
export const systemLanguages = uniq(navigator.languages.map((lang) => lang.split('-')[0]));
3
+
export const systemLanguages = unique(navigator.languages.map((lang) => lang.split('-')[0]));
4
4
5
5
export const primarySystemLanguage = systemLanguages[0];
+1
-1
src/lib/aglais-bookmarks/search.ts
+1
-1
src/lib/aglais-bookmarks/search.ts
···
1
1
import type { Token } from '@atcute/bluesky-search-parser';
2
2
import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons';
3
+
import { mapDefined } from '@mary/array-fns';
3
4
4
5
import { DID_RE, HANDLE_RE } from '~/api/utils/strings';
5
6
6
7
import { parseEndDate, parseStartDate, splitFilters } from '../bsky/search';
7
-
import { mapDefined } from '../utils/misc';
8
8
import { escapeRegex } from '../utils/regex';
9
9
10
10
export const createSearchPredicate = (tokens: Token[]) => {
+1
-1
src/lib/states/moderation.tsx
+1
-1
src/lib/states/moderation.tsx
···
2
2
import { unwrap } from 'solid-js/store';
3
3
4
4
import type { AppBskyLabelerDefs, At } from '@atcute/client/lexicons';
5
+
import { mapDefined } from '@mary/array-fns';
5
6
import { createQueries } from '@mary/solid-query';
6
7
7
8
import { BLUESKY_MODERATION_DID } from '~/api/defaults';
···
10
11
11
12
import { createBatchedFetch } from '~/lib/utils/batch-fetch';
12
13
import { assert } from '~/lib/utils/invariant';
13
-
import { mapDefined } from '~/lib/utils/misc';
14
14
15
15
import { useAgent } from './agent';
16
16
import { useSession } from './session';
+2
-2
src/lib/states/session.tsx
+2
-2
src/lib/states/session.tsx
···
13
13
import { type FetchHandler, type FetchHandlerObject, XRPC, XRPCError } from '@atcute/client';
14
14
import type { At } from '@atcute/client/lexicons';
15
15
import { OAuthUserAgent, deleteStoredSession, getSession } from '@atcute/oauth-browser-client';
16
+
import { mapDefined } from '@mary/array-fns';
16
17
17
18
import { BLUESKY_MODERATION_DID } from '~/api/defaults';
18
19
···
24
25
import type { PerAccountPreferenceSchema } from '../preferences/account';
25
26
import type { AccountData } from '../preferences/sessions';
26
27
import { assert } from '../utils/invariant';
27
-
import { mapDefined } from '../utils/misc';
28
28
29
29
export interface CurrentAccountState {
30
30
readonly did: At.DID;
···
87
87
88
88
const filters = preferences.moderation.keywords;
89
89
90
-
const times = [...mapDefined(filters, (filter) => filter.expires)];
90
+
const times = mapDefined(filters, (filter) => filter.expires);
91
91
92
92
const nextAt = times.reduce((time, x) => (x < time ? x : time), Infinity);
93
93
-24
src/lib/utils/misc.ts
-24
src/lib/utils/misc.ts
···
2
2
3
3
import { replaceEqualDeep } from '@mary/solid-query';
4
4
5
-
export const mapDefined = <T, R>(array: T[], mapper: (value: T) => R | undefined): R[] => {
6
-
var mapped: R[] = [];
7
-
8
-
var idx = 0;
9
-
var len = array.length;
10
-
var temp: R | undefined;
11
-
12
-
for (; idx < len; idx++) {
13
-
if ((temp = mapper(array[idx])) !== undefined) {
14
-
mapped.push(temp);
15
-
}
16
-
}
17
-
18
-
return mapped;
19
-
};
20
-
21
5
const _on = <T, R>(accessor: () => T, callback: (value: T) => R): (() => R) => {
22
6
return () => {
23
7
const value = accessor();
···
74
58
};
75
59
76
60
export const requestIdle = typeof requestIdleCallback === 'function' ? requestIdleCallback : setTimeout;
77
-
78
-
export const uniq = <T>(items: T[]): T[] => {
79
-
return Array.from(new Set(items));
80
-
};
81
-
82
-
export const pickRandom = <T>(items: T[]): T => {
83
-
return items[(Math.random() * items.length) | 0];
84
-
};
85
61
86
62
export const isSetEqual = <T>(a: Set<T>, b: Set<T>): boolean => {
87
63
if (a.size !== b.size) {
+1
-1
src/views/profile-labels.tsx
+1
-1
src/views/profile-labels.tsx
···
1
1
import { Match, Show, Switch, createMemo } from 'solid-js';
2
2
3
3
import type { At } from '@atcute/client/lexicons';
4
+
import { mapDefined } from '@mary/array-fns';
4
5
5
6
import {
6
7
GLOBAL_LABELS,
···
29
30
import { Key } from '~/lib/keyed';
30
31
import { useParams, useTitle } from '~/lib/navigation/router';
31
32
import { useSession } from '~/lib/states/session';
32
-
import { mapDefined } from '~/lib/utils/misc';
33
33
34
34
import Avatar from '~/components/avatar';
35
35
import * as Boxed from '~/components/boxed';
+2
-1
src/views/settings-content-translation-exclusion.tsx
+2
-1
src/views/settings-content-translation-exclusion.tsx
···
1
1
import { For, Show, createMemo, createSignal, untrack } from 'solid-js';
2
2
3
+
import { mapDefined } from '@mary/array-fns';
4
+
3
5
import { LANGUAGE_CODES, getEnglishLanguageName, getNativeLanguageName } from '~/lib/intl/languages';
4
6
import { useTitle } from '~/lib/navigation/router';
5
7
import { useSession } from '~/lib/states/session';
6
-
import { mapDefined } from '~/lib/utils/misc';
7
8
8
9
import * as Boxed from '~/components/boxed';
9
10
import EndOfListView from '~/components/end-of-list-view';
+2
-1
src/views/settings-content-translation.tsx
+2
-1
src/views/settings-content-translation.tsx
···
1
1
import { For } from 'solid-js';
2
2
3
+
import { mapDefined } from '@mary/array-fns';
4
+
3
5
import { googleLanguages } from '~/api/basa/languages';
4
6
5
7
import { openModal } from '~/globals/modals';
···
7
9
import { getEnglishLanguageName } from '~/lib/intl/languages';
8
10
import { useTitle } from '~/lib/navigation/router';
9
11
import { useSession } from '~/lib/states/session';
10
-
import { mapDefined } from '~/lib/utils/misc';
11
12
12
13
import * as Boxed from '~/components/boxed';
13
14
import IconButton from '~/components/icon-button';
+2
-1
src/views/settings-content.tsx
+2
-1
src/views/settings-content.tsx
···
1
1
import { batch } from 'solid-js';
2
2
3
+
import { mapDefined } from '@mary/array-fns';
4
+
3
5
import { dequal } from '~/api/utils/dequal';
4
6
5
7
import { openModal } from '~/globals/modals';
···
13
15
toPersistedThreadgate,
14
16
} from '~/lib/preferences/snippets/composer';
15
17
import { useSession } from '~/lib/states/session';
16
-
import { mapDefined } from '~/lib/utils/misc';
17
18
18
19
import * as Boxed from '~/components/boxed';
19
20
import ComposedInteractionDialogLazy from '~/components/composer/dialogs/composed-interaction-dialog-lazy';