From 2e0f8064411cc8d06013276353ce72ac8d9a0e2b Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 6 Mar 2024 16:12:37 +0000 Subject: [PATCH 01/11] Fix jumpy moderation icon on desktop (#3125) --- src/view/shell/desktop/LeftNav.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index def0333c..c56ba941 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -391,7 +391,7 @@ export function DesktopLeftNav() { } label={_(msg`Moderation`)} From 5b8d116e335bd7574928485adc1d4e9cf5ee0564 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 6 Mar 2024 17:27:59 +0000 Subject: [PATCH 02/11] By default, hide replies to people you don't follow in the Following feed (#3124) * Show replies from followed by default * Update @atproto/api --- package.json | 2 +- src/state/queries/preferences/const.ts | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 59ee3319..c9909dcf 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "update-extensions": "scripts/updateExtensions.sh" }, "dependencies": { - "@atproto/api": "^0.10.4", + "@atproto/api": "^0.10.5", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@emoji-mart/react": "^1.1.1", diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts index 25d28499..53c9e482 100644 --- a/src/state/queries/preferences/const.ts +++ b/src/state/queries/preferences/const.ts @@ -7,7 +7,7 @@ import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/ export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] = { hideReplies: false, - hideRepliesByUnfollowed: false, + hideRepliesByUnfollowed: true, hideRepliesByLikeCount: 0, hideReposts: false, hideQuotePosts: false, diff --git a/yarn.lock b/yarn.lock index 3add1af6..9d73b068 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,10 +34,10 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@atproto/api@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.10.4.tgz#b73446f2344783c42c6040082756449443f15750" - integrity sha512-9gwZt4v4pngfD4mgsET9i9Ym0PpMSzftTzqBjCbFpObx15zMkFemYnLUnyT/NEww2u/aRxjAe2TeBnU0dIbbuQ== +"@atproto/api@^0.10.5": + version "0.10.5" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.10.5.tgz#e778e2843d08690df8df81f24028a7578e9b3cb4" + integrity sha512-GYdST5sPKU2JnPmm8x3KqjOSlDiYXrp4GkW7bpQTVLPabnUNq5NLN6HJEoJABjjOAsaLF12rBoV+JpRb1UjNsQ== dependencies: "@atproto/common-web" "^0.2.3" "@atproto/lexicon" "^0.3.2" From f61d1e1f9410865505a316d825a314354a84171d Mon Sep 17 00:00:00 2001 From: Eiichi Yoshikawa Date: Thu, 7 Mar 2024 03:24:08 +0900 Subject: [PATCH 03/11] Apply notification icon settings of FCM on Android (#3113) Co-authored-by: Hailey --- app.config.js | 3 +- plugins/withAndroidManifestFCMIconPlugin.js | 37 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 plugins/withAndroidManifestFCMIconPlugin.js diff --git a/app.config.js b/app.config.js index 530e07b9..2552d348 100644 --- a/app.config.js +++ b/app.config.js @@ -153,10 +153,11 @@ module.exports = function (config) { 'expo-notifications', { icon: './assets/icon-android-notification.png', - color: '#ffffff', + color: '#1185fe', }, ], './plugins/withAndroidManifestPlugin.js', + './plugins/withAndroidManifestFCMIconPlugin.js', './plugins/withAndroidStylesWindowBackgroundPlugin.js', './plugins/shareExtension/withShareExtensions.js', ].filter(Boolean), diff --git a/plugins/withAndroidManifestFCMIconPlugin.js b/plugins/withAndroidManifestFCMIconPlugin.js new file mode 100644 index 00000000..066a975d --- /dev/null +++ b/plugins/withAndroidManifestFCMIconPlugin.js @@ -0,0 +1,37 @@ +const {withAndroidManifest} = require('expo/config-plugins') + +module.exports = function withAndroidManifestFCMIconPlugin(appConfig) { + return withAndroidManifest(appConfig, function (decoratedAppConfig) { + try { + function addOrModifyMetaData(metaData, name, resource) { + const elem = metaData.find(elem => elem.$['android:name'] === name) + if (elem === undefined) { + metaData.push({ + $: { + 'android:name': name, + 'android:resource': resource, + }, + }) + } else { + elem.$['android:resource'] = resource + } + } + const androidManifest = decoratedAppConfig.modResults.manifest + const metaData = androidManifest.application[0]['meta-data'] + addOrModifyMetaData( + metaData, + 'com.google.firebase.messaging.default_notification_color', + '@color/notification_icon_color', + ) + addOrModifyMetaData( + metaData, + 'com.google.firebase.messaging.default_notification_icon', + '@drawable/notification_icon', + ) + return decoratedAppConfig + } catch (e) { + console.error(`withAndroidManifestFCMIconPlugin failed`, e) + } + return decoratedAppConfig + }) +} From 7a592d8140cbb907535b482cb43a369b0a7e4750 Mon Sep 17 00:00:00 2001 From: Jake Gold <52801504+Jacob2161@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:33:41 -0800 Subject: [PATCH 04/11] bskyweb: remove waitlist email endpoint (#3127) --- bskyweb/cmd/bskyweb/mailmodo.go | 70 --------------------------------- bskyweb/cmd/bskyweb/main.go | 12 ------ bskyweb/cmd/bskyweb/server.go | 55 +++----------------------- 3 files changed, 5 insertions(+), 132 deletions(-) delete mode 100644 bskyweb/cmd/bskyweb/mailmodo.go diff --git a/bskyweb/cmd/bskyweb/mailmodo.go b/bskyweb/cmd/bskyweb/mailmodo.go deleted file mode 100644 index e892971f..00000000 --- a/bskyweb/cmd/bskyweb/mailmodo.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/json" - "fmt" - "net/http" - "time" -) - -type Mailmodo struct { - httpClient *http.Client - APIKey string - BaseURL string - ListName string -} - -func NewMailmodo(apiKey, listName string) *Mailmodo { - return &Mailmodo{ - APIKey: apiKey, - BaseURL: "https://api.mailmodo.com/api/v1", - httpClient: &http.Client{}, - ListName: listName, - } -} - -func (m *Mailmodo) request(ctx context.Context, httpMethod string, apiMethod string, data any) error { - endpoint := fmt.Sprintf("%s/%s", m.BaseURL, apiMethod) - js, err := json.Marshal(data) - if err != nil { - return fmt.Errorf("Mailmodo JSON encoding failed: %w", err) - } - req, err := http.NewRequestWithContext(ctx, httpMethod, endpoint, bytes.NewBuffer(js)) - if err != nil { - return fmt.Errorf("Mailmodo HTTP creating request %s %s failed: %w", httpMethod, apiMethod, err) - } - req.Header.Set("mmApiKey", m.APIKey) - req.Header.Set("Content-Type", "application/json") - - res, err := m.httpClient.Do(req) - if err != nil { - return fmt.Errorf("Mailmodo HTTP making request %s %s failed: %w", httpMethod, apiMethod, err) - } - defer res.Body.Close() - - status := struct { - Success bool `json:"success"` - Message string `json:"message"` - }{} - if err := json.NewDecoder(res.Body).Decode(&status); err != nil { - return fmt.Errorf("Mailmodo HTTP parsing response %s %s failed: %w", httpMethod, apiMethod, err) - } - if !status.Success { - return fmt.Errorf("Mailmodo API response %s %s failed: %s", httpMethod, apiMethod, status.Message) - } - return nil -} - -func (m *Mailmodo) AddToList(ctx context.Context, email string) error { - return m.request(ctx, "POST", "addToList", map[string]any{ - "listName": m.ListName, - "email": email, - "data": map[string]any{ - "email_hashed": fmt.Sprintf("%x", sha256.Sum256([]byte(email))), - }, - "created_at": time.Now().UTC().Format(time.RFC3339), - }) -} diff --git a/bskyweb/cmd/bskyweb/main.go b/bskyweb/cmd/bskyweb/main.go index a2952cae..5185ff57 100644 --- a/bskyweb/cmd/bskyweb/main.go +++ b/bskyweb/cmd/bskyweb/main.go @@ -40,18 +40,6 @@ func run(args []string) { // retain old PDS env var for easy transition EnvVars: []string{"ATP_APPVIEW_HOST", "ATP_PDS_HOST"}, }, - &cli.StringFlag{ - Name: "mailmodo-api-key", - Usage: "Mailmodo API key", - Required: false, - EnvVars: []string{"MAILMODO_API_KEY"}, - }, - &cli.StringFlag{ - Name: "mailmodo-list-name", - Usage: "Mailmodo contact list to add email addresses to", - Required: false, - EnvVars: []string{"MAILMODO_LIST_NAME"}, - }, &cli.StringFlag{ Name: "http-address", Usage: "Specify the local IP/port to bind to", diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 6b76acc9..e159d780 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -2,11 +2,9 @@ package main import ( "context" - "encoding/json" "errors" "fmt" "io/fs" - "io/ioutil" "net/http" "os" "os/signal" @@ -29,25 +27,19 @@ import ( ) type Server struct { - echo *echo.Echo - httpd *http.Server - mailmodo *Mailmodo - xrpcc *xrpc.Client + echo *echo.Echo + httpd *http.Server + xrpcc *xrpc.Client } func serve(cctx *cli.Context) error { debug := cctx.Bool("debug") httpAddress := cctx.String("http-address") appviewHost := cctx.String("appview-host") - mailmodoAPIKey := cctx.String("mailmodo-api-key") - mailmodoListName := cctx.String("mailmodo-list-name") // Echo e := echo.New() - // Mailmodo client. - mailmodo := NewMailmodo(mailmodoAPIKey, mailmodoListName) - // create a new session (no auth) xrpcc := &xrpc.Client{ Client: cliutil.NewHttpClient(), @@ -77,9 +69,8 @@ func serve(cctx *cli.Context) error { // server // server := &Server{ - echo: e, - mailmodo: mailmodo, - xrpcc: xrpcc, + echo: e, + xrpcc: xrpcc, } // Create the HTTP server. @@ -221,9 +212,6 @@ func serve(cctx *cli.Context) error { e.GET("/profile/:handleOrDID/post/:rkey/liked-by", server.WebGeneric) e.GET("/profile/:handleOrDID/post/:rkey/reposted-by", server.WebGeneric) - // Mailmodo - e.POST("/api/waitlist", server.apiWaitlist) - // Start the server. log.Infof("starting server address=%s", httpAddress) go func() { @@ -398,36 +386,3 @@ func (srv *Server) WebProfile(c echo.Context) error { data["requestHost"] = req.Host return c.Render(http.StatusOK, "profile.html", data) } - -func (srv *Server) apiWaitlist(c echo.Context) error { - type jsonError struct { - Error string `json:"error"` - } - - // Read the API request. - type apiRequest struct { - Email string `json:"email"` - } - - bodyReader := http.MaxBytesReader(c.Response(), c.Request().Body, 16*1024) - payload, err := ioutil.ReadAll(bodyReader) - if err != nil { - return err - } - var req apiRequest - if err := json.Unmarshal(payload, &req); err != nil { - return c.JSON(http.StatusBadRequest, jsonError{Error: "Invalid API request"}) - } - - if req.Email == "" { - return c.JSON(http.StatusBadRequest, jsonError{Error: "Please enter a valid email address."}) - } - - if err := srv.mailmodo.AddToList(c.Request().Context(), req.Email); err != nil { - log.Errorf("adding email to waitlist failed: %s", err) - return c.JSON(http.StatusBadRequest, jsonError{ - Error: "Storing email in waitlist failed. Please enter a valid email address.", - }) - } - return c.JSON(http.StatusOK, map[string]bool{"success": true}) -} From b8f36a8bca0eab08e09e0a56be63ade40e710f34 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 6 Mar 2024 11:02:48 -0800 Subject: [PATCH 05/11] Fix double border on web hashtag list (#3091) * add `topBorder` to Views.tsx * fix double border --- src/components/Lists.tsx | 13 ++++++++----- src/screens/Hashtag.tsx | 7 ++----- src/view/com/util/Views.d.ts | 4 +++- src/view/com/util/Views.web.tsx | 11 ++++++++++- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx index 12a93580..58aa74b3 100644 --- a/src/components/Lists.tsx +++ b/src/components/Lists.tsx @@ -1,6 +1,7 @@ import React from 'react' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {View} from 'react-native' +import {CenteredView} from 'view/com/util/Views' import {Loader} from '#/components/Loader' import {Trans} from '@lingui/macro' import {cleanError} from 'lib/strings/errors' @@ -143,7 +144,7 @@ export function ListMaybePlaceholder({ }) { const navigation = useNavigation() const t = useTheme() - const {gtMobile} = useBreakpoints() + const {gtMobile, gtTablet} = useBreakpoints() const canGoBack = navigation.canGoBack() const onGoBack = React.useCallback(() => { @@ -165,14 +166,16 @@ export function ListMaybePlaceholder({ if (!isEmpty) return null return ( - + ]} + sideBorders={gtMobile} + topBorder={!gtTablet}> {isLoading ? ( @@ -241,6 +244,6 @@ export function ListMaybePlaceholder({ )} - + ) } diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx index 09a1f282..82ea75aa 100644 --- a/src/screens/Hashtag.tsx +++ b/src/screens/Hashtag.tsx @@ -1,6 +1,5 @@ import React from 'react' import {ListRenderItemInfo, Pressable} from 'react-native' -import {atoms as a, useBreakpoints} from '#/alf' import {useFocusEffect} from '@react-navigation/native' import {useSetMinimalShellMode} from 'state/shell' import {ViewHeader} from 'view/com/util/ViewHeader' @@ -19,7 +18,6 @@ import {List} from 'view/com/util/List' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {sanitizeHandle} from 'lib/strings/handles' -import {CenteredView} from 'view/com/util/Views' import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox' import {shareUrl} from 'lib/sharing' import {HITSLOP_10} from 'lib/constants' @@ -38,7 +36,6 @@ export default function HashtagScreen({ }: NativeStackScreenProps) { const {tag, author} = route.params const setMinimalShellMode = useSetMinimalShellMode() - const {gtMobile} = useBreakpoints() const {_} = useLingui() const [isPTR, setIsPTR] = React.useState(false) @@ -103,7 +100,7 @@ export default function HashtagScreen({ }, [isFetching, hasNextPage, error, fetchNextPage]) return ( - + <> )} - + ) } diff --git a/src/view/com/util/Views.d.ts b/src/view/com/util/Views.d.ts index 6a90cc22..16713921 100644 --- a/src/view/com/util/Views.d.ts +++ b/src/view/com/util/Views.d.ts @@ -5,4 +5,6 @@ export function CenteredView({ style, sideBorders, ...props -}: React.PropsWithChildren) +}: React.PropsWithChildren< + ViewProps & {sideBorders?: boolean; topBorder?: boolean} +>) diff --git a/src/view/com/util/Views.web.tsx b/src/view/com/util/Views.web.tsx index db3b9de0..ae165077 100644 --- a/src/view/com/util/Views.web.tsx +++ b/src/view/com/util/Views.web.tsx @@ -32,8 +32,11 @@ interface AddedProps { export function CenteredView({ style, sideBorders, + topBorder, ...props -}: React.PropsWithChildren) { +}: React.PropsWithChildren< + ViewProps & {sideBorders?: boolean; topBorder?: boolean} +>) { const pal = usePalette('default') const {isMobile} = useWebMediaQueries() if (!isMobile) { @@ -46,6 +49,12 @@ export function CenteredView({ }) style = addStyle(style, pal.border) } + if (topBorder) { + style = addStyle(style, { + borderTopWidth: 1, + }) + style = addStyle(style, pal.border) + } return } From 2d9a5db967c7eb98228ae372c1139e4069974b90 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 6 Mar 2024 11:13:03 -0800 Subject: [PATCH 06/11] Switch date picker libraries (#3126) * Revert other base PR changes, switch date picker library rm expo-linear-gradient Revert "remove unused packages, switch to `expo-linear-gradient`" This reverts commit 20a0ffcf Revert "upgrade expo deps" This reverts commit a43dca92caa120d45fb771431752dd2db3b37ab5. Revert other library changes This reverts commit 5219f66e Revert "re-add normalize-url" This reverts commit 654019c4babe2e455f6069a6c725eb51140ab1ab. Revert "add `expo-haptics`" This reverts commit ff3a0399b1c7eae07b83946f13543eedf7cdfe82. Revert "add `expo-clipboard`" This reverts commit 440ae91632153e22ff79050d8ac803a7305e88a0. Revert "add `expo-file-system`" This reverts commit c0fe0c94534564982399c22299a8a19052bf3e54. fix android alf use modal on android migrate to `react-native-date-picker` rm `@reactnativecommunity/datetimepicker` add `react-native-date-picker` add `expo-file-system` add `expo-clipboard` add `expo-haptics` re-add normalize-url rm blur view upgrade expo deps remove unused packages, switch to `expo-linear-gradient` * rm old library * Use UTC for dates --------- Co-authored-by: Eric Bailey --- package.json | 2 +- .../forms/DateField/index.android.tsx | 39 +++++++++---------- src/components/forms/DateField/index.tsx | 20 ++++------ src/state/queries/preferences/index.ts | 2 +- src/view/com/util/forms/DateInput.tsx | 32 ++++++++------- yarn.lock | 12 +++--- 6 files changed, 51 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index c9909dcf..294b138c 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "@react-native-camera-roll/camera-roll": "^5.2.2", "@react-native-clipboard/clipboard": "^1.10.0", "@react-native-community/blur": "^4.3.0", - "@react-native-community/datetimepicker": "7.6.1", "@react-native-masked-view/masked-view": "0.3.0", "@react-native-menu/menu": "^0.8.0", "@react-native-picker/picker": "2.6.1", @@ -152,6 +151,7 @@ "react-keyed-flatten-children": "^3.0.0", "react-native": "0.73.2", "react-native-appstate-hook": "^1.0.6", + "react-native-date-picker": "^4.4.0", "react-native-drawer-layout": "^4.0.0-alpha.3", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.14.0", diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx index cddb643d..451810a5 100644 --- a/src/components/forms/DateField/index.android.tsx +++ b/src/components/forms/DateField/index.android.tsx @@ -1,8 +1,5 @@ import React from 'react' import {View, Pressable} from 'react-native' -import DateTimePicker, { - BaseProps as DateTimePickerProps, -} from '@react-native-community/datetimepicker' import {useTheme, atoms} from '#/alf' import {Text} from '#/components/Typography' @@ -15,6 +12,8 @@ import { localizeDate, toSimpleDateString, } from '#/components/forms/DateField/utils' +import DatePicker from 'react-native-date-picker' +import {isAndroid} from 'platform/detection' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -38,20 +37,20 @@ export function DateField({ const {chromeFocus, chromeError, chromeErrorHover} = TextField.useSharedInputStyles() - const onChangeInternal = React.useCallback< - Required['onChange'] - >( - (_event, date) => { + const onChangeInternal = React.useCallback( + (date: Date) => { setOpen(false) - if (date) { - const formatted = toSimpleDateString(date) - onChangeDate(formatted) - } + const formatted = toSimpleDateString(date) + onChangeDate(formatted) }, [onChangeDate, setOpen], ) + const onCancel = React.useCallback(() => { + setOpen(false) + }, []) + return ( {open && ( - )} diff --git a/src/components/forms/DateField/index.tsx b/src/components/forms/DateField/index.tsx index e65936e0..49e47a01 100644 --- a/src/components/forms/DateField/index.tsx +++ b/src/components/forms/DateField/index.tsx @@ -1,13 +1,11 @@ import React from 'react' import {View} from 'react-native' -import DateTimePicker, { - DateTimePickerEvent, -} from '@react-native-community/datetimepicker' import {useTheme, atoms} from '#/alf' import * as TextField from '#/components/forms/TextField' import {toSimpleDateString} from '#/components/forms/DateField/utils' import {DateFieldProps} from '#/components/forms/DateField/types' +import DatePicker from 'react-native-date-picker' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -28,7 +26,7 @@ export function DateField({ const t = useTheme() const onChangeInternal = React.useCallback( - (event: DateTimePickerEvent, date: Date | undefined) => { + (date: Date | undefined) => { if (date) { const formatted = toSimpleDateString(date) onChangeDate(formatted) @@ -39,17 +37,15 @@ export function DateField({ return ( - ) diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 07198de7..37ef10ae 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -169,7 +169,7 @@ export function usePreferencesSetBirthDateMutation() { return useMutation({ mutationFn: async ({birthDate}: {birthDate: Date}) => { - await getAgent().setPersonalDetails({birthDate}) + await getAgent().setPersonalDetails({birthDate: birthDate.toISOString()}) // triggers a refetch await queryClient.invalidateQueries({ queryKey: preferencesQueryKey, diff --git a/src/view/com/util/forms/DateInput.tsx b/src/view/com/util/forms/DateInput.tsx index c5f0afc8..0104562a 100644 --- a/src/view/com/util/forms/DateInput.tsx +++ b/src/view/com/util/forms/DateInput.tsx @@ -1,8 +1,5 @@ import React, {useState, useCallback} from 'react' import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native' -import DateTimePicker, { - DateTimePickerEvent, -} from '@react-native-community/datetimepicker' import { FontAwesomeIcon, FontAwesomeIconStyle, @@ -14,6 +11,7 @@ import {TypographyVariant} from 'lib/ThemeContext' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {getLocales} from 'expo-localization' +import DatePicker from 'react-native-date-picker' const LOCALE = getLocales()[0] @@ -43,11 +41,9 @@ export function DateInput(props: Props) { }, [props.handleAsUTC]) const onChangeInternal = useCallback( - (event: DateTimePickerEvent, date: Date | undefined) => { + (date: Date) => { setShow(false) - if (date) { - props.onChange(date) - } + props.onChange(date) }, [setShow, props], ) @@ -56,6 +52,10 @@ export function DateInput(props: Props) { setShow(true) }, [setShow]) + const onCancel = useCallback(() => { + setShow(false) + }, []) + return ( {isAndroid && ( @@ -80,15 +80,17 @@ export function DateInput(props: Props) { )} {(isIOS || show) && ( - Date: Wed, 6 Mar 2024 11:34:48 -0800 Subject: [PATCH 07/11] fix internal links showing up as external (#3128) * fix internal links showing up as external * fix internal links showing up as external --- src/lib/strings/url-helpers.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts index ba2cdb39..ee88225e 100644 --- a/src/lib/strings/url-helpers.ts +++ b/src/lib/strings/url-helpers.ts @@ -158,7 +158,12 @@ export function linkRequiresWarning(uri: string, label: string) { const host = urip.hostname.toLowerCase() // Hosts that end with bsky.app or bsky.social should be trusted by default. - if (host.endsWith('bsky.app') || host.endsWith('bsky.social')) { + if ( + uri.startsWith('/') || + host.endsWith('bsky.app') || + host.endsWith('bsky.social') || + host.endsWith('blueskyweb.xyz') + ) { // if this is a link to internal content, // warn if it represents itself as a URL to another app return !!labelDomain && labelDomain !== host && isPossiblyAUrl(labelDomain) From 800eecbfe4746c887933ecd34c20c862220d07ee Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 6 Mar 2024 11:40:10 -0800 Subject: [PATCH 08/11] fix internal links showing up as external pt2 (#3129) * fix internal links showing up as external pt2 * fix internal links showing up as external pt2 --- src/lib/strings/url-helpers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts index ee88225e..7729e4a3 100644 --- a/src/lib/strings/url-helpers.ts +++ b/src/lib/strings/url-helpers.ts @@ -148,6 +148,11 @@ export function feedUriToHref(url: string): string { export function linkRequiresWarning(uri: string, label: string) { const labelDomain = labelToDomain(label) + // If the uri started with a / we know it is internal. + if (uri.startsWith('/')) { + return false + } + let urip try { urip = new URL(uri) @@ -156,10 +161,8 @@ export function linkRequiresWarning(uri: string, label: string) { } const host = urip.hostname.toLowerCase() - // Hosts that end with bsky.app or bsky.social should be trusted by default. if ( - uri.startsWith('/') || host.endsWith('bsky.app') || host.endsWith('bsky.social') || host.endsWith('blueskyweb.xyz') From d5b0a33ae3b36463684e53b2b99196826fa9d538 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 6 Mar 2024 12:27:49 -0800 Subject: [PATCH 09/11] Run `extract` and `compile` when building for web (#3132) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fcd2413c..3ad05b6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN \. "$NVM_DIR/nvm.sh" && \ nvm use $NODE_VERSION && \ npm install --global yarn && \ yarn && \ - yarn intl:compile && \ + yarn intl:build && \ yarn build-web # DEBUG From 357b61d0a5573209214589112d5f0ac829fc6293 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 6 Mar 2024 15:24:37 -0800 Subject: [PATCH 10/11] version bump (wait for release) (#3095) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 294b138c..2571fe77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsky.app", - "version": "1.71.0", + "version": "1.72.0", "private": true, "engines": { "node": ">=18" From 8b0e575f6423575c08e9a6748be41c888611d631 Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 6 Mar 2024 15:33:23 -0800 Subject: [PATCH 11/11] Adjust FlatList performance in main feeds (#3134) * adjust flatlist perf settings * calculate initial num to render based on screen height * adjust window size * don't react to screen height changes --- src/lib/hooks/useInitialNumToRender.ts | 11 +++++++++++ src/screens/Hashtag.tsx | 4 ++++ src/view/com/posts/Feed.tsx | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 src/lib/hooks/useInitialNumToRender.ts diff --git a/src/lib/hooks/useInitialNumToRender.ts b/src/lib/hooks/useInitialNumToRender.ts new file mode 100644 index 00000000..942f0404 --- /dev/null +++ b/src/lib/hooks/useInitialNumToRender.ts @@ -0,0 +1,11 @@ +import React from 'react' +import {Dimensions} from 'react-native' + +const MIN_POST_HEIGHT = 100 + +export function useInitialNumToRender(minItemHeight: number = MIN_POST_HEIGHT) { + return React.useMemo(() => { + const screenHeight = Dimensions.get('window').height + return Math.ceil(screenHeight / minItemHeight) + 1 + }, [minItemHeight]) +} diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx index 82ea75aa..f1b81737 100644 --- a/src/screens/Hashtag.tsx +++ b/src/screens/Hashtag.tsx @@ -22,6 +22,7 @@ import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOut import {shareUrl} from 'lib/sharing' import {HITSLOP_10} from 'lib/constants' import {isNative} from 'platform/detection' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' const renderItem = ({item}: ListRenderItemInfo) => { return @@ -37,6 +38,7 @@ export default function HashtagScreen({ const {tag, author} = route.params const setMinimalShellMode = useSetMinimalShellMode() const {_} = useLingui() + const initialNumToRender = useInitialNumToRender() const [isPTR, setIsPTR] = React.useState(false) const fullTag = React.useMemo(() => { @@ -154,6 +156,8 @@ export default function HashtagScreen({ onRetry={fetchNextPage} /> } + initialNumToRender={initialNumToRender} + windowSize={11} /> )} diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 54d8aa22..cd3e9878 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -32,6 +32,7 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {DiscoverFallbackHeader} from './DiscoverFallbackHeader' import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' const LOADING_ITEM = {_reactKey: '__loading__'} const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} @@ -84,6 +85,7 @@ let Feed = ({ const {_} = useLingui() const queryClient = useQueryClient() const {currentAccount} = useSession() + const initialNumToRender = useInitialNumToRender() const [isPTRing, setIsPTRing] = React.useState(false) const checkForNewRef = React.useRef<(() => void) | null>(null) const lastFetchRef = React.useRef(Date.now()) @@ -327,6 +329,8 @@ let Feed = ({ desktopFixedHeight={ desktopFixedHeightOffset ? desktopFixedHeightOffset : true } + initialNumToRender={initialNumToRender} + windowSize={11} /> )