From 8a6d83de3b5723497e2bbebf10290cde15cfe1d7 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 11 Sep 2024 23:04:40 +0100 Subject: [PATCH 01/13] make container relative (#5280) --- bskyembed/src/components/embed.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx index 3b4f5e77..1ed107b5 100644 --- a/bskyembed/src/components/embed.tsx +++ b/bskyembed/src/components/embed.tsx @@ -372,7 +372,7 @@ function VideoEmbed({content}: {content: AppBskyEmbedVideo.View}) { return (
Date: Thu, 12 Sep 2024 00:42:21 +0200 Subject: [PATCH 02/13] remove double closing tag (#5257) --- bskyweb/templates/base.html | 1 - 1 file changed, 1 deletion(-) diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index c2480279..aa7efc5e 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -259,7 +259,6 @@ pointer-events: none !important; } - {% include "scripts.html" %} From cff7cbb4aa0a945399f4d44bb56a12ae0ed27278 Mon Sep 17 00:00:00 2001 From: Eduardo Tachotte <58338880+0xEDU@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:28:23 -0300 Subject: [PATCH 03/13] Add autoCapitalize to password field (#5216) --- src/screens/Signup/StepInfo/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx index 47fb4c70..e0a7912f 100644 --- a/src/screens/Signup/StepInfo/index.tsx +++ b/src/screens/Signup/StepInfo/index.tsx @@ -172,6 +172,7 @@ export function StepInfo({ defaultValue={state.password} secureTextEntry autoComplete="new-password" + autoCapitalize="none" /> From ae71f5ce84165b683b880c4a585b5a617f2c36bb Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 11 Sep 2024 19:56:00 -0500 Subject: [PATCH 04/13] NUX API (#5278) * Set up nux API * Bump SDK * Naming * Imports --- package.json | 2 +- src/state/queries/nuxs/definitions.ts | 29 +++++++++ src/state/queries/nuxs/index.ts | 83 ++++++++++++++++++++++++++ src/state/queries/nuxs/types.ts | 9 +++ src/state/queries/nuxs/util.ts | 52 ++++++++++++++++ src/state/queries/preferences/const.ts | 1 + yarn.lock | 35 +++++------ 7 files changed, 193 insertions(+), 18 deletions(-) create mode 100644 src/state/queries/nuxs/definitions.ts create mode 100644 src/state/queries/nuxs/index.ts create mode 100644 src/state/queries/nuxs/types.ts create mode 100644 src/state/queries/nuxs/util.ts diff --git a/package.json b/package.json index eff665a6..92b6cfe1 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web" }, "dependencies": { - "@atproto/api": "0.13.5", + "@atproto/api": "^0.13.7", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts new file mode 100644 index 00000000..c5cb1e9d --- /dev/null +++ b/src/state/queries/nuxs/definitions.ts @@ -0,0 +1,29 @@ +import zod from 'zod' + +import {BaseNux} from '#/state/queries/nuxs/types' + +export enum Nux { + One = 'one', + Two = 'two', +} + +export const nuxNames = new Set(Object.values(Nux)) + +export type AppNux = + | BaseNux<{ + id: Nux.One + data: { + likes: number + } + }> + | BaseNux<{ + id: Nux.Two + data: undefined + }> + +export const NuxSchemas = { + [Nux.One]: zod.object({ + likes: zod.number(), + }), + [Nux.Two]: undefined, +} diff --git a/src/state/queries/nuxs/index.ts b/src/state/queries/nuxs/index.ts new file mode 100644 index 00000000..2945e67e --- /dev/null +++ b/src/state/queries/nuxs/index.ts @@ -0,0 +1,83 @@ +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {AppNux, Nux} from '#/state/queries/nuxs/definitions' +import {parseAppNux, serializeAppNux} from '#/state/queries/nuxs/util' +import { + preferencesQueryKey, + usePreferencesQuery, +} from '#/state/queries/preferences' +import {useAgent} from '#/state/session' + +export {Nux} from '#/state/queries/nuxs/definitions' + +export function useNuxs() { + const {data, ...rest} = usePreferencesQuery() + + if (data && rest.isSuccess) { + const nuxs = data.bskyAppState.nuxs + ?.map(parseAppNux) + ?.filter(Boolean) as AppNux[] + + if (nuxs) { + return { + nuxs, + ...rest, + } + } + } + + return { + nuxs: undefined, + ...rest, + } +} + +export function useNux(id: T) { + const {nuxs, ...rest} = useNuxs() + + if (nuxs && rest.isSuccess) { + const nux = nuxs.find(nux => nux.id === id) + + if (nux) { + return { + nux: nux as Extract, + ...rest, + } + } + } + + return { + nux: undefined, + ...rest, + } +} + +export function useUpsertNuxMutation() { + const queryClient = useQueryClient() + const agent = useAgent() + + return useMutation({ + mutationFn: async (nux: AppNux) => { + await agent.bskyAppUpsertNux(serializeAppNux(nux)) + // triggers a refetch + await queryClient.invalidateQueries({ + queryKey: preferencesQueryKey, + }) + }, + }) +} + +export function useRemoveNuxsMutation() { + const queryClient = useQueryClient() + const agent = useAgent() + + return useMutation({ + mutationFn: async (ids: string[]) => { + await agent.bskyAppRemoveNuxs(ids) + // triggers a refetch + await queryClient.invalidateQueries({ + queryKey: preferencesQueryKey, + }) + }, + }) +} diff --git a/src/state/queries/nuxs/types.ts b/src/state/queries/nuxs/types.ts new file mode 100644 index 00000000..5b791847 --- /dev/null +++ b/src/state/queries/nuxs/types.ts @@ -0,0 +1,9 @@ +import {AppBskyActorDefs} from '@atproto/api' + +export type Data = Record | undefined + +export type BaseNux< + T extends Pick & {data: Data}, +> = T & { + completed: boolean +} diff --git a/src/state/queries/nuxs/util.ts b/src/state/queries/nuxs/util.ts new file mode 100644 index 00000000..d65b86a3 --- /dev/null +++ b/src/state/queries/nuxs/util.ts @@ -0,0 +1,52 @@ +import {AppBskyActorDefs, nuxSchema} from '@atproto/api' + +import { + AppNux, + Nux, + nuxNames, + NuxSchemas, +} from '#/state/queries/nuxs/definitions' + +export function parseAppNux(nux: AppBskyActorDefs.Nux): AppNux | undefined { + if (!nuxNames.has(nux.id as Nux)) return + if (!nuxSchema.safeParse(nux).success) return + + const {data, ...rest} = nux + + const schema = NuxSchemas[nux.id as Nux] + + if (schema && data) { + const parsedData = JSON.parse(data) + + if (!schema.safeParse(parsedData).success) return + + return { + ...rest, + data: parsedData, + } as AppNux + } + + return { + ...rest, + data: undefined, + } as AppNux +} + +export function serializeAppNux(nux: AppNux): AppBskyActorDefs.Nux { + const {data, ...rest} = nux + const schema = NuxSchemas[nux.id as Nux] + + const result: AppBskyActorDefs.Nux = { + ...rest, + data: undefined, + } + + if (schema) { + schema.parse(data) + result.data = JSON.stringify(data) + } + + nuxSchema.parse(result) + + return result +} diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts index 1ae7d206..e07f40ec 100644 --- a/src/state/queries/preferences/const.ts +++ b/src/state/queries/preferences/const.ts @@ -37,5 +37,6 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = { bskyAppState: { queuedNudges: [], activeProgressGuide: undefined, + nuxs: [], }, } diff --git a/yarn.lock b/yarn.lock index cc440109..b2e389aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -72,19 +72,6 @@ resolved "https://registry.yarnpkg.com/@atproto-labs/simple-store/-/simple-store-0.1.1.tgz#e743a2722b5d8732166f0a72aca8bd10e9bff106" integrity sha512-WKILW2b3QbAYKh+w5U2x6p5FqqLl0nAeLwGeDY+KjX01K4Dq3vQTR9b/qNp0jZm48CabPQVrqCv0PPU9LgRRRg== -"@atproto/api@0.13.5": - version "0.13.5" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.5.tgz#04305cdb0a467ba366305c5e95cebb7ce0d39735" - integrity sha512-yT/YimcKYkrI0d282Zxo7O30OSYR+KDW89f81C6oYZfDRBcShC1aniVV8kluP5LrEAg8O27yrOSnBgx2v7XPew== - dependencies: - "@atproto/common-web" "^0.3.0" - "@atproto/lexicon" "^0.4.1" - "@atproto/syntax" "^0.3.0" - "@atproto/xrpc" "^0.6.1" - await-lock "^2.2.2" - multiformats "^9.9.0" - tlds "^1.234.0" - "@atproto/api@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.0.tgz#d1c65a407f1c3c6aba5be9425f4f739a01419bd8" @@ -98,6 +85,20 @@ multiformats "^9.9.0" tlds "^1.234.0" +"@atproto/api@^0.13.7": + version "0.13.7" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.7.tgz#072eba2025d5251505f17b0b5d2de33749ea5ee4" + integrity sha512-41kSLmFWDbuPOenb52WRq1lnBkSZrL+X29tWcvEt6SZXK4xBoKAalw1MjF+oabhzff12iMtNaNvmmt2fu1L+cw== + dependencies: + "@atproto/common-web" "^0.3.0" + "@atproto/lexicon" "^0.4.1" + "@atproto/syntax" "^0.3.0" + "@atproto/xrpc" "^0.6.2" + await-lock "^2.2.2" + multiformats "^9.9.0" + tlds "^1.234.0" + zod "^3.23.8" + "@atproto/aws@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.2.tgz#703e5e06f288bcf61c6d99a990738f1e7299e653" @@ -443,10 +444,10 @@ "@atproto/lexicon" "^0.4.1" zod "^3.23.8" -"@atproto/xrpc@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.1.tgz#dcd1315c8c60eef5af2db7fa4e35a38ebc6d79d5" - integrity sha512-Zy5ydXEdk6sY7FDUZcEVfCL1jvbL4tXu5CcdPqbEaW6LQtk9GLds/DK1bCX9kswTGaBC88EMuqQMfkxOhp2t4A== +"@atproto/xrpc@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.2.tgz#634228a7e533de01bda2214837d11574fdadad55" + integrity sha512-as/gb08xJb02HAGNrSQSumCe10WnOAcnM6bR6KMatQyQJuEu7OY6ZDSTM/4HfjjoxsNqdvPmbYuoUab1bKTNlA== dependencies: "@atproto/lexicon" "^0.4.1" zod "^3.23.8" From 7da3ddbe247a0b272e615a7bbbe91ba9805cb309 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 12 Sep 2024 15:16:38 +0100 Subject: [PATCH 05/13] [Video] speculative .vtt file selection fix (#5296) * speculative vtt fix * add logging --- src/view/com/composer/videos/SubtitleFilePicker.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/view/com/composer/videos/SubtitleFilePicker.tsx b/src/view/com/composer/videos/SubtitleFilePicker.tsx index 9e0fe0ae..beb3f07a 100644 --- a/src/view/com/composer/videos/SubtitleFilePicker.tsx +++ b/src/view/com/composer/videos/SubtitleFilePicker.tsx @@ -3,6 +3,7 @@ import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {logger} from '#/logger' import * as Toast from '#/view/com/util/Toast' import {atoms as a} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' @@ -25,9 +26,16 @@ export function SubtitleFilePicker({ const handlePick = (evt: React.ChangeEvent) => { const selectedFile = evt.target.files?.[0] if (selectedFile) { - if (selectedFile.type === 'text/vtt') { + if ( + selectedFile.type === 'text/vtt' || + (selectedFile.type === 'text/plain' && + selectedFile.name.endsWith('.vtt')) + ) { onSelectFile(selectedFile) } else { + logger.error('Invalid subtitle file type', { + safeMessage: `File: ${selectedFile.name} (${selectedFile.type})`, + }) Toast.show(_(msg`Only WebVTT (.vtt) files are supported`)) } } From 4d22adbcf6866fb2995e090b2528bdb5968d7f78 Mon Sep 17 00:00:00 2001 From: Minseo Lee Date: Thu, 12 Sep 2024 23:30:57 +0900 Subject: [PATCH 06/13] Tabular numbers to video timestamp (#5293) --- .../com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx | 4 ++-- .../com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx index 6636883f..be3f9071 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx @@ -37,11 +37,11 @@ export function TimeIndicator({time}: {time: number}) { ]}> - {minutes}:{seconds} + {`${minutes}:${seconds}`} ) diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx index bb15db08..791025f7 100644 --- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx +++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx @@ -370,7 +370,7 @@ export function Controls({ onPress={onPressPlayPause} /> - + {formatTime(currentTime)} / {formatTime(duration)} {hasSubtitleTrack && ( From e0d9e75407b053dd3b7a3472f925d8cd4bd92d45 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 12 Sep 2024 15:39:04 +0100 Subject: [PATCH 07/13] Fix notification scroll jump (#5297) --- src/state/queries/notifications/util.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts index e0ee0229..133d3ebc 100644 --- a/src/state/queries/notifications/util.ts +++ b/src/state/queries/notifications/util.ts @@ -175,9 +175,19 @@ async function fetchSubjects( }> { const postUris = new Set() const packUris = new Set() + + const postUrisWithLikes = new Set() + const postUrisWithReposts = new Set() + for (const notif of groupedNotifs) { if (notif.subjectUri?.includes('app.bsky.feed.post')) { postUris.add(notif.subjectUri) + if (notif.type === 'post-like') { + postUrisWithLikes.add(notif.subjectUri) + } + if (notif.type === 'repost') { + postUrisWithReposts.add(notif.subjectUri) + } } else if ( notif.notification.reasonSubject?.includes('app.bsky.graph.starterpack') ) { @@ -206,6 +216,15 @@ async function fetchSubjects( AppBskyFeedPost.validateRecord(post.record).success ) { postsMap.set(post.uri, post) + + // HACK. In some cases, the appview appears to lag behind and returns empty counters. + // To prevent scroll jump due to missing metrics, fill in 1 like/repost instead of 0. + if (post.likeCount === 0 && postUrisWithLikes.has(post.uri)) { + post.likeCount = 1 + } + if (post.repostCount === 0 && postUrisWithReposts.has(post.uri)) { + post.repostCount = 1 + } } } for (const pack of packsChunks.flat()) { From 86abeb80b9e863b0a1d5eaebe725a676cee4ae09 Mon Sep 17 00:00:00 2001 From: Wesley <46491851+wscld@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:59:12 -0300 Subject: [PATCH 08/13] Renaming the Follow button to "Follow back" when followed by user (#5281) * Renaming the follow button to follow back when followed by user * Fixing conditions and reusing existing translation --- src/screens/Profile/Header/ProfileHeaderStandard.tsx | 2 ++ src/view/com/profile/FollowButton.tsx | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index cf5fcb97..846fa442 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -219,6 +219,8 @@ let ProfileHeaderStandard = ({ {profile.viewer?.following ? ( Following + ) : profile.viewer?.followedBy ? ( + Follow Back ) : ( Follow )} diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx index 42adea3c..aaa5d345 100644 --- a/src/view/com/profile/FollowButton.tsx +++ b/src/view/com/profile/FollowButton.tsx @@ -61,7 +61,7 @@ export function FollowButton({ label={_(msg({message: 'Unfollow', context: 'action'}))} /> ) - } else { + } else if (!profile.viewer.followedBy) { return (