personal web client for Bluesky
typescript solidjs bluesky atcute

refactor: array-fns :)

mary.my.id 225fe562 210bbdb4

verified
+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
··· 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 1 import type { AppBskyLabelerDefs } from '@atcute/client/lexicons'; 2 - 3 - import { mapDefined } from '~/lib/utils/misc'; 2 + import { mapDefined } from '@mary/array-fns'; 4 3 5 4 import type { LabelDefinitionMapping, ModerationLabeler } from '.'; 6 5 import {
+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 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
··· 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
··· 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
··· 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
··· 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
··· 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 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
··· 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
··· 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
··· 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 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
··· 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
··· 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
··· 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 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
··· 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
··· 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
··· 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';