From 5dd673d1ff37cf85481de1692c493f1913491608 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 10 Oct 2023 17:32:19 -0700 Subject: [PATCH 1/9] 1.53 --- app.config.js | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app.config.js b/app.config.js index 82d0d5a0ca..1f4de0370b 100644 --- a/app.config.js +++ b/app.config.js @@ -6,7 +6,7 @@ module.exports = function () { slug: 'bluesky', scheme: 'bluesky', owner: 'blueskysocial', - version: '1.52.0', + version: '1.53.0', runtimeVersion: { policy: 'appVersion', }, @@ -19,7 +19,7 @@ module.exports = function () { backgroundColor: '#ffffff', }, ios: { - buildNumber: '2', + buildNumber: '1', supportsTablet: false, bundleIdentifier: 'xyz.blueskyweb.app', config: { @@ -43,7 +43,7 @@ module.exports = function () { backgroundColor: '#ffffff', }, android: { - versionCode: 41, + versionCode: 42, adaptiveIcon: { foregroundImage: './assets/adaptive-icon.png', backgroundColor: '#ffffff', diff --git a/package.json b/package.json index 9c98688bf4..054c7a6d8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsky.app", - "version": "1.52.0", + "version": "1.53.0", "private": true, "scripts": { "prepare": "is-ci || husky install", From d40d931d86480bbadba85cbb323beb6ec932566a Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 11 Oct 2023 11:04:06 -0500 Subject: [PATCH 2/9] use new zeed-dom version (#1671) --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 054c7a6d8c..eddf1dc403 100644 --- a/package.json +++ b/package.json @@ -214,7 +214,7 @@ }, "resolutions": { "@types/react": "^18", - "**/zeed-dom": "estrattonbailey/zeed-dom#publish" + "**/zeed-dom": "0.10.9" }, "jest": { "preset": "jest-expo/ios", diff --git a/yarn.lock b/yarn.lock index 7a6923fc58..bd7dbeaaef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19191,9 +19191,10 @@ yocto-queue@^1.0.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== -zeed-dom@^0.9.19, zeed-dom@estrattonbailey/zeed-dom#publish: - version "0.10.8" - resolved "https://codeload.github.com/estrattonbailey/zeed-dom/tar.gz/aad32339dc2473b75aa0a90d8baee21c40a1e914" +zeed-dom@0.10.9, zeed-dom@^0.9.19: + version "0.10.9" + resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.9.tgz#b3eb5d9b7cf1be17e1fb3a708379df5edce195be" + integrity sha512-qQQ7Wu7IJ3Vo/LjeKWj97A2Hi17di4ZdmgNZj6AWbDbpt3hvO4EMfjYVA2/2unLYT+XpmMq5fqaLqCeU7Im83A== dependencies: css-what "^6.1.0" From d98e3a8b45a400bb7f0abbf3c69d509f8269d07a Mon Sep 17 00:00:00 2001 From: Jake Gold Date: Wed, 11 Oct 2023 09:27:05 -0700 Subject: [PATCH 3/9] bskyweb: add rate limiting to reduce DoSability --- bskyweb/cmd/bskyweb/server.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index d5d864069b..5be96ce0e3 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -91,6 +91,11 @@ func serve(cctx *cli.Context) error { } e.HideBanner = true + e.Renderer = NewRenderer("templates/", &bskyweb.TemplateFS, debug) + e.HTTPErrorHandler = server.errorHandler + + e.IPExtractor = echo.ExtractIPFromXFFHeader() + // SECURITY: Do not modify without due consideration. e.Use(middleware.SecureWithConfig(middleware.SecureConfig{ ContentTypeNosniff: "nosniff", @@ -106,8 +111,23 @@ func serve(cctx *cli.Context) error { return strings.HasPrefix(c.Request().URL.Path, "/static") }, })) - e.Renderer = NewRenderer("templates/", &bskyweb.TemplateFS, debug) - e.HTTPErrorHandler = server.errorHandler + e.Use(middleware.RateLimiterWithConfig(middleware.RateLimiterConfig{ + Skipper: middleware.DefaultSkipper, + Store: middleware.NewRateLimiterMemoryStoreWithConfig( + middleware.RateLimiterMemoryStoreConfig{ + Rate: 10, // requests per second + Burst: 30, // allow bursts + ExpiresIn: 3 * time.Minute, // garbage collect entries older than 3 minutes + }, + ), + IdentifierExtractor: func(ctx echo.Context) (string, error) { + id := ctx.RealIP() + return id, nil + }, + DenyHandler: func(c echo.Context, identifier string, err error) error { + return c.String(http.StatusTooManyRequests, "Your request has been rate limited. Please try again later. Contact security@bsky.app if you believe this was a mistake.\n") + }, + })) // redirect trailing slash to non-trailing slash. // all of our current endpoints have no trailing slash. From c2a1cf4e56780b60fa8d140f9c7c855567851f5c Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 12 Oct 2023 19:14:27 +0100 Subject: [PATCH 4/9] Fix layout shift for multi-image posts (#1673) * Fix layout shift for multi-image posts * Add a comment for the hack * Use margins instead of gap * Move alt label --- src/view/com/util/images/Gallery.tsx | 17 ++- src/view/com/util/images/ImageLayoutGrid.tsx | 114 +++++++++---------- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx index 679f71c99b..094b0c56cc 100644 --- a/src/view/com/util/images/Gallery.tsx +++ b/src/view/com/util/images/Gallery.tsx @@ -23,19 +23,19 @@ export const GalleryItem: FC = ({ onLongPress, }) => { const image = images[index] - return ( - + onPress(index) : undefined} onPressIn={onPressIn ? () => onPressIn(index) : undefined} onLongPress={onLongPress ? () => onLongPress(index) : undefined} + style={styles.fullWidth} accessibilityRole="button" accessibilityLabel={image.alt || 'Image'} accessibilityHint=""> = ({ } const styles = StyleSheet.create({ + fullWidth: { + flex: 1, + }, + image: { + flex: 1, + borderRadius: 4, + }, altContainer: { backgroundColor: 'rgba(0, 0, 0, 0.75)', borderRadius: 6, paddingHorizontal: 6, paddingVertical: 3, position: 'absolute', - left: 6, - bottom: 6, + left: 8, + bottom: 8, }, alt: { color: 'white', diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx index 4c09013043..2e352d0864 100644 --- a/src/view/com/util/images/ImageLayoutGrid.tsx +++ b/src/view/com/util/images/ImageLayoutGrid.tsx @@ -1,13 +1,5 @@ -import React, {useMemo, useState} from 'react' -import { - LayoutChangeEvent, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native' -import {ImageStyle} from 'expo-image' -import {Dimensions} from 'lib/media/types' +import React from 'react' +import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import {AppBskyEmbedImages} from '@atproto/api' import {GalleryItem} from './Gallery' @@ -20,21 +12,11 @@ interface ImageLayoutGridProps { } export function ImageLayoutGrid({style, ...props}: ImageLayoutGridProps) { - const [containerInfo, setContainerInfo] = useState() - - const onLayout = (evt: LayoutChangeEvent) => { - const {width, height} = evt.nativeEvent.layout - setContainerInfo({ - width, - height, - }) - } - return ( - - {containerInfo ? ( - - ) : undefined} + + + + ) } @@ -44,70 +26,80 @@ interface ImageLayoutGridInnerProps { onPress?: (index: number) => void onLongPress?: (index: number) => void onPressIn?: (index: number) => void - containerInfo: Dimensions } -function ImageLayoutGridInner({ - containerInfo, - ...props -}: ImageLayoutGridInnerProps) { +function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { const count = props.images.length - const size1 = useMemo(() => { - if (count === 3) { - const size = (containerInfo.width - 10) / 3 - return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} - } else { - const size = (containerInfo.width - 5) / 2 - return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} - } - }, [count, containerInfo]) - const size2 = React.useMemo(() => { - if (count === 3) { - const size = ((containerInfo.width - 10) / 3) * 2 + 5 - return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} - } else { - const size = (containerInfo.width - 5) / 2 - return {width: size, height: size, resizeMode: 'cover', borderRadius: 4} - } - }, [count, containerInfo]) switch (count) { case 2: return ( - - + + + + + + ) + case 3: return ( - - - - + + + + + + + + + + ) + case 4: return ( - - - + + + + + + + - - - + + + + + + + ) + default: return null } } +// This is used to compute margins (rather than flexbox gap) due to Yoga bugs: +// https://github.com/facebook/yoga/issues/1418 +const IMAGE_GAP = 5 + const styles = StyleSheet.create({ - flexRow: {flexDirection: 'row', gap: 5}, - flexColumn: {flexDirection: 'column', gap: 5}, + container: { + marginHorizontal: -IMAGE_GAP / 2, + marginVertical: -IMAGE_GAP / 2, + }, + flexRow: {flexDirection: 'row'}, + smallItem: {flex: 1, aspectRatio: 1}, + image: { + margin: IMAGE_GAP / 2, + }, }) From 997918547c7b9eaeecb0cb65e9360796e6777eb9 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 12 Oct 2023 21:02:17 +0100 Subject: [PATCH 5/9] Make shell hide/show animation smoother (#1683) * Make shell hide/show animation smoother * Also animate "load latest" --- src/lib/hooks/useMinimalShellMode.tsx | 7 ++++--- src/view/com/pager/FeedsTabBarMobile.tsx | 5 +++-- src/view/com/util/load-latest/LoadLatestBtn.tsx | 13 ++++++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/lib/hooks/useMinimalShellMode.tsx b/src/lib/hooks/useMinimalShellMode.tsx index e28a0e884f..2a0a4e4d06 100644 --- a/src/lib/hooks/useMinimalShellMode.tsx +++ b/src/lib/hooks/useMinimalShellMode.tsx @@ -7,21 +7,22 @@ export function useMinimalShellMode() { const store = useStores() const minimalShellInterp = useAnimatedValue(0) const footerMinimalShellTransform = { - transform: [{translateY: Animated.multiply(minimalShellInterp, 100)}], + opacity: Animated.subtract(1, minimalShellInterp), + transform: [{translateY: Animated.multiply(minimalShellInterp, 50)}], } React.useEffect(() => { if (store.shell.minimalShellMode) { Animated.timing(minimalShellInterp, { toValue: 1, - duration: 100, + duration: 150, useNativeDriver: true, isInteraction: false, }).start() } else { Animated.timing(minimalShellInterp, { toValue: 0, - duration: 100, + duration: 150, useNativeDriver: true, isInteraction: false, }).start() diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx index e39e2dd68d..ad1a69cf67 100644 --- a/src/view/com/pager/FeedsTabBarMobile.tsx +++ b/src/view/com/pager/FeedsTabBarMobile.tsx @@ -24,13 +24,14 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl( React.useEffect(() => { Animated.timing(interp, { toValue: store.shell.minimalShellMode ? 1 : 0, - duration: 100, + duration: 150, useNativeDriver: true, isInteraction: false, }).start() }, [interp, store.shell.minimalShellMode]) const transform = { - transform: [{translateY: Animated.multiply(interp, -100)}], + opacity: Animated.subtract(1, interp), + transform: [{translateY: Animated.multiply(interp, -50)}], } const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3) diff --git a/src/view/com/util/load-latest/LoadLatestBtn.tsx b/src/view/com/util/load-latest/LoadLatestBtn.tsx index f5d12ce2c1..095ebea448 100644 --- a/src/view/com/util/load-latest/LoadLatestBtn.tsx +++ b/src/view/com/util/load-latest/LoadLatestBtn.tsx @@ -10,6 +10,10 @@ import {colors} from 'lib/styles' import {HITSLOP_20} from 'lib/constants' import {isWeb} from 'platform/detection' import {clamp} from 'lib/numbers' +import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated' + +const AnimatedTouchableOpacity = + Animated.createAnimatedComponent(TouchableOpacity) export const LoadLatestBtn = observer(function LoadLatestBtnImpl({ onPress, @@ -30,15 +34,18 @@ export const LoadLatestBtn = observer(function LoadLatestBtnImpl({ ? 50 : (minMode || isDesktop ? 16 : 60) + (isWeb ? 20 : clamp(safeAreaInsets.bottom, 15, 60)) + const animatedStyle = useAnimatedStyle(() => ({ + bottom: withTiming(bottom, {duration: 150}), + })) return ( - {showIndicator && } - + ) }) From 4431cfe2d2971577e9c0a4fa4ed83f0c52fb7540 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 12 Oct 2023 21:15:19 +0100 Subject: [PATCH 6/9] Fix crash when scrolling down on the web (#1684) --- src/view/com/util/load-latest/LoadLatestBtn.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/view/com/util/load-latest/LoadLatestBtn.tsx b/src/view/com/util/load-latest/LoadLatestBtn.tsx index 095ebea448..57c3baa5b2 100644 --- a/src/view/com/util/load-latest/LoadLatestBtn.tsx +++ b/src/view/com/util/load-latest/LoadLatestBtn.tsx @@ -74,14 +74,12 @@ const styles = StyleSheet.create({ loadLatestTablet: { // @ts-ignore web only left: '50vw', - // @ts-ignore web only -prf - transform: 'translateX(-282px)', + transform: [{translateX: -282}], }, loadLatestDesktop: { // @ts-ignore web only left: '50vw', - // @ts-ignore web only -prf - transform: 'translateX(-382px)', + transform: [{translateX: -382}], }, indicator: { position: 'absolute', From 2a1edab6d4a8e8b4197b10868153a525f918509c Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 13 Oct 2023 15:24:28 +0100 Subject: [PATCH 7/9] Don't re-render bars when showing/hiding them (#1691) * Don't re-render bars when showing/hiding them * Fix more cases * Use autorun instead of reaction to fix first render --- src/lib/hooks/useMinimalShellMode.tsx | 35 +++++++++++++----------- src/view/com/pager/FeedsTabBarMobile.tsx | 17 +++++++----- src/view/com/util/ViewHeader.tsx | 35 +++++++++++++----------- src/view/com/util/fab/FABInner.tsx | 17 +++++++----- 4 files changed, 58 insertions(+), 46 deletions(-) diff --git a/src/lib/hooks/useMinimalShellMode.tsx b/src/lib/hooks/useMinimalShellMode.tsx index 2a0a4e4d06..68f405dc4d 100644 --- a/src/lib/hooks/useMinimalShellMode.tsx +++ b/src/lib/hooks/useMinimalShellMode.tsx @@ -1,4 +1,5 @@ import React from 'react' +import {autorun} from 'mobx' import {useStores} from 'state/index' import {Animated} from 'react-native' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' @@ -12,22 +13,24 @@ export function useMinimalShellMode() { } React.useEffect(() => { - if (store.shell.minimalShellMode) { - Animated.timing(minimalShellInterp, { - toValue: 1, - duration: 150, - useNativeDriver: true, - isInteraction: false, - }).start() - } else { - Animated.timing(minimalShellInterp, { - toValue: 0, - duration: 150, - useNativeDriver: true, - isInteraction: false, - }).start() - } - }, [minimalShellInterp, store.shell.minimalShellMode]) + return autorun(() => { + if (store.shell.minimalShellMode) { + Animated.timing(minimalShellInterp, { + toValue: 1, + duration: 150, + useNativeDriver: true, + isInteraction: false, + }).start() + } else { + Animated.timing(minimalShellInterp, { + toValue: 0, + duration: 150, + useNativeDriver: true, + isInteraction: false, + }).start() + } + }) + }, [minimalShellInterp, store]) return {footerMinimalShellTransform} } diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx index ad1a69cf67..6bdba11a18 100644 --- a/src/view/com/pager/FeedsTabBarMobile.tsx +++ b/src/view/com/pager/FeedsTabBarMobile.tsx @@ -1,6 +1,7 @@ import React, {useMemo} from 'react' import {Animated, StyleSheet, TouchableOpacity, View} from 'react-native' import {observer} from 'mobx-react-lite' +import {autorun} from 'mobx' import {TabBar} from 'view/com/pager/TabBar' import {RenderTabBarFnProps} from 'view/com/pager/Pager' import {useStores} from 'state/index' @@ -22,13 +23,15 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl( const interp = useAnimatedValue(0) React.useEffect(() => { - Animated.timing(interp, { - toValue: store.shell.minimalShellMode ? 1 : 0, - duration: 150, - useNativeDriver: true, - isInteraction: false, - }).start() - }, [interp, store.shell.minimalShellMode]) + return autorun(() => { + Animated.timing(interp, { + toValue: store.shell.minimalShellMode ? 1 : 0, + duration: 150, + useNativeDriver: true, + isInteraction: false, + }).start() + }) + }, [interp, store]) const transform = { opacity: Animated.subtract(1, interp), transform: [{translateY: Animated.multiply(interp, -50)}], diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx index 1640287082..3a34777ab9 100644 --- a/src/view/com/util/ViewHeader.tsx +++ b/src/view/com/util/ViewHeader.tsx @@ -1,5 +1,6 @@ import React from 'react' import {observer} from 'mobx-react-lite' +import {autorun} from 'mobx' import {Animated, StyleSheet, TouchableOpacity, View} from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {useNavigation} from '@react-navigation/native' @@ -154,22 +155,24 @@ const Container = observer(function ContainerImpl({ const interp = useAnimatedValue(0) React.useEffect(() => { - if (store.shell.minimalShellMode) { - Animated.timing(interp, { - toValue: 1, - duration: 100, - useNativeDriver: true, - isInteraction: false, - }).start() - } else { - Animated.timing(interp, { - toValue: 0, - duration: 100, - useNativeDriver: true, - isInteraction: false, - }).start() - } - }, [interp, store.shell.minimalShellMode]) + return autorun(() => { + if (store.shell.minimalShellMode) { + Animated.timing(interp, { + toValue: 1, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + } else { + Animated.timing(interp, { + toValue: 0, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + } + }) + }, [interp, store]) const transform = { transform: [{translateY: Animated.multiply(interp, -100)}], } diff --git a/src/view/com/util/fab/FABInner.tsx b/src/view/com/util/fab/FABInner.tsx index 6c96eef2c1..97eeba358a 100644 --- a/src/view/com/util/fab/FABInner.tsx +++ b/src/view/com/util/fab/FABInner.tsx @@ -1,5 +1,6 @@ import React, {ComponentProps} from 'react' import {observer} from 'mobx-react-lite' +import {autorun} from 'mobx' import {Animated, StyleSheet, TouchableWithoutFeedback} from 'react-native' import LinearGradient from 'react-native-linear-gradient' import {gradients} from 'lib/styles' @@ -25,13 +26,15 @@ export const FABInner = observer(function FABInnerImpl({ const store = useStores() const interp = useAnimatedValue(0) React.useEffect(() => { - Animated.timing(interp, { - toValue: store.shell.minimalShellMode ? 0 : 1, - duration: 100, - useNativeDriver: true, - isInteraction: false, - }).start() - }, [interp, store.shell.minimalShellMode]) + return autorun(() => { + Animated.timing(interp, { + toValue: store.shell.minimalShellMode ? 0 : 1, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + }) + }, [interp, store]) const transform = isTablet ? undefined : { From eba9f8a1662e78ff79b7d179efa8dec261d61d87 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 13 Oct 2023 15:25:16 +0100 Subject: [PATCH 8/9] Fix profile layout shift (#1690) --- src/view/com/util/ViewSelector.tsx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/view/com/util/ViewSelector.tsx b/src/view/com/util/ViewSelector.tsx index 6c0e4c6cc4..935d93033b 100644 --- a/src/view/com/util/ViewSelector.tsx +++ b/src/view/com/util/ViewSelector.tsx @@ -144,8 +144,6 @@ export function Selector({ items: string[] onSelect?: (index: number) => void }) { - const [height, setHeight] = useState(0) - const pal = usePalette('default') const borderColor = useColorSchemeStyle( {borderColor: colors.black}, @@ -160,22 +158,13 @@ export function Selector({ - { - const {height: layoutHeight} = e.nativeEvent.layout - setHeight(layoutHeight || 60) - }}> + showsHorizontalScrollIndicator={false}> + {items.map((item, i) => { const selected = i === selectedIndex return ( From d5ccbd76d5ffc9280f0109138f608b94bee0aadf Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 13 Oct 2023 15:29:50 +0100 Subject: [PATCH 9/9] Disable events on hidden bars (#1686) --- src/view/com/pager/FeedsTabBarMobile.tsx | 12 +++++++++++- src/view/shell/bottom-bar/BottomBar.tsx | 1 + src/view/shell/bottom-bar/BottomBarStyles.tsx | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx index 6bdba11a18..7924666e5c 100644 --- a/src/view/com/pager/FeedsTabBarMobile.tsx +++ b/src/view/com/pager/FeedsTabBarMobile.tsx @@ -49,7 +49,14 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl( ) return ( - +