From f18b9b32b0d296c8d19dc06956699f95c0af9be2 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 21 Nov 2023 10:57:34 -0600 Subject: [PATCH] PWI Base (#1964) * Base work for public view * Make default moderation settings more restrictive * Fix type * Handle showing sign-in on authed actions * Fix hoc logic * Simplify prefs logic * Remove duplicate method * Add todo * Clean up RepostButton.web * Fix x button color * Add todo * Retain existing label prefs for now, use separate logged out settings * Clean up useAuthedMethod, rename to useRequireAuth * Add todos * Move dismiss logic to withAuthRequired * Ooops add web * Block public view in prod * Add todo * Fix bad import --- src/state/queries/preferences/const.ts | 24 + src/state/queries/preferences/index.ts | 129 +-- src/state/queries/preferences/moderation.ts | 18 + src/state/queries/preferences/types.ts | 5 +- src/state/session/index.tsx | 17 + src/state/shell/index.tsx | 31 +- src/state/shell/logged-out.tsx | 37 + src/view/com/auth/LoggedOut.tsx | 3 +- src/view/com/auth/SplashScreen.tsx | 32 +- src/view/com/auth/SplashScreen.web.tsx | 110 ++- src/view/com/auth/withAuthRequired.tsx | 17 +- src/view/com/util/post-ctrls/PostCtrls.tsx | 10 +- src/view/com/util/post-ctrls/RepostButton.tsx | 6 +- .../com/util/post-ctrls/RepostButton.web.tsx | 58 +- src/view/screens/Feeds.tsx | 743 +++++++++--------- src/view/screens/Home.tsx | 63 +- src/view/screens/PostLikedBy.tsx | 37 +- src/view/screens/PostRepostedBy.tsx | 37 +- src/view/screens/PostThread.tsx | 147 ++-- src/view/screens/Profile.tsx | 131 +-- src/view/screens/ProfileFeed.tsx | 3 + src/view/screens/ProfileFeedLikedBy.tsx | 37 +- src/view/screens/ProfileFollowers.tsx | 35 +- src/view/screens/ProfileFollows.tsx | 35 +- src/view/screens/SavedFeeds.tsx | 16 +- 25 files changed, 1026 insertions(+), 755 deletions(-) create mode 100644 src/state/shell/logged-out.tsx diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts index 5db137e5..b7f9206e 100644 --- a/src/state/queries/preferences/const.ts +++ b/src/state/queries/preferences/const.ts @@ -2,6 +2,7 @@ import { UsePreferencesQueryResponse, ThreadViewPreferences, } from '#/state/queries/preferences/types' +import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' export const DEFAULT_HOME_FEED_PREFS: UsePreferencesQueryResponse['feedViewPrefs'] = { @@ -25,3 +26,26 @@ export const DEFAULT_PROD_FEEDS = { pinned: [DEFAULT_PROD_FEED_PREFIX('whats-hot')], saved: [DEFAULT_PROD_FEED_PREFIX('whats-hot')], } + +export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = { + birthDate: new Date('2022-11-17'), // TODO(pwi) + adultContentEnabled: false, + feeds: { + saved: [], + pinned: [], + unpinned: [], + }, + // labels are undefined until set by user + contentLabels: { + nsfw: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.nsfw, + nudity: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.nudity, + suggestive: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.suggestive, + gore: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.gore, + hate: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.hate, + spam: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.spam, + impersonation: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES.impersonation, + }, + feedViewPrefs: DEFAULT_HOME_FEED_PREFS, + threadViewPrefs: DEFAULT_THREAD_VIEW_PREFS, + userAge: 13, // TODO(pwi) +} diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index e7fa3b15..afdec267 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -15,6 +15,7 @@ import {temp__migrateLabelPref} from '#/state/queries/preferences/util' import { DEFAULT_HOME_FEED_PREFS, DEFAULT_THREAD_VIEW_PREFS, + DEFAULT_LOGGED_OUT_PREFERENCES, } from '#/state/queries/preferences/const' import {getModerationOpts} from '#/state/queries/preferences/moderation' import {STALE} from '#/state/queries' @@ -23,63 +24,67 @@ export * from '#/state/queries/preferences/types' export * from '#/state/queries/preferences/moderation' export * from '#/state/queries/preferences/const' -export const usePreferencesQueryKey = ['getPreferences'] +export const preferencesQueryKey = ['getPreferences'] export function usePreferencesQuery() { - const {hasSession} = useSession() return useQuery({ - enabled: hasSession, staleTime: STALE.MINUTES.ONE, - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, queryFn: async () => { - const res = await getAgent().getPreferences() - const preferences: UsePreferencesQueryResponse = { - ...res, - feeds: { - saved: res.feeds?.saved || [], - pinned: res.feeds?.pinned || [], - unpinned: - res.feeds.saved?.filter(f => { - return !res.feeds.pinned?.includes(f) - }) || [], - }, - // labels are undefined until set by user - contentLabels: { - nsfw: temp__migrateLabelPref( - res.contentLabels?.nsfw || DEFAULT_LABEL_PREFERENCES.nsfw, - ), - nudity: temp__migrateLabelPref( - res.contentLabels?.nudity || DEFAULT_LABEL_PREFERENCES.nudity, - ), - suggestive: temp__migrateLabelPref( - res.contentLabels?.suggestive || - DEFAULT_LABEL_PREFERENCES.suggestive, - ), - gore: temp__migrateLabelPref( - res.contentLabels?.gore || DEFAULT_LABEL_PREFERENCES.gore, - ), - hate: temp__migrateLabelPref( - res.contentLabels?.hate || DEFAULT_LABEL_PREFERENCES.hate, - ), - spam: temp__migrateLabelPref( - res.contentLabels?.spam || DEFAULT_LABEL_PREFERENCES.spam, - ), - impersonation: temp__migrateLabelPref( - res.contentLabels?.impersonation || - DEFAULT_LABEL_PREFERENCES.impersonation, - ), - }, - feedViewPrefs: { - ...DEFAULT_HOME_FEED_PREFS, - ...(res.feedViewPrefs.home || {}), - }, - threadViewPrefs: { - ...DEFAULT_THREAD_VIEW_PREFS, - ...(res.threadViewPrefs ?? {}), - }, - userAge: res.birthDate ? getAge(res.birthDate) : undefined, + const agent = getAgent() + + if (agent.session?.did === undefined) { + return DEFAULT_LOGGED_OUT_PREFERENCES + } else { + const res = await agent.getPreferences() + const preferences: UsePreferencesQueryResponse = { + ...res, + feeds: { + saved: res.feeds?.saved || [], + pinned: res.feeds?.pinned || [], + unpinned: + res.feeds.saved?.filter(f => { + return !res.feeds.pinned?.includes(f) + }) || [], + }, + // labels are undefined until set by user + contentLabels: { + nsfw: temp__migrateLabelPref( + res.contentLabels?.nsfw || DEFAULT_LABEL_PREFERENCES.nsfw, + ), + nudity: temp__migrateLabelPref( + res.contentLabels?.nudity || DEFAULT_LABEL_PREFERENCES.nudity, + ), + suggestive: temp__migrateLabelPref( + res.contentLabels?.suggestive || + DEFAULT_LABEL_PREFERENCES.suggestive, + ), + gore: temp__migrateLabelPref( + res.contentLabels?.gore || DEFAULT_LABEL_PREFERENCES.gore, + ), + hate: temp__migrateLabelPref( + res.contentLabels?.hate || DEFAULT_LABEL_PREFERENCES.hate, + ), + spam: temp__migrateLabelPref( + res.contentLabels?.spam || DEFAULT_LABEL_PREFERENCES.spam, + ), + impersonation: temp__migrateLabelPref( + res.contentLabels?.impersonation || + DEFAULT_LABEL_PREFERENCES.impersonation, + ), + }, + feedViewPrefs: { + ...DEFAULT_HOME_FEED_PREFS, + ...(res.feedViewPrefs.home || {}), + }, + threadViewPrefs: { + ...DEFAULT_THREAD_VIEW_PREFS, + ...(res.threadViewPrefs ?? {}), + }, + userAge: res.birthDate ? getAge(res.birthDate) : undefined, + } + return preferences } - return preferences }, }) } @@ -107,7 +112,7 @@ export function useClearPreferencesMutation() { await getAgent().app.bsky.actor.putPreferences({preferences: []}) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -125,7 +130,7 @@ export function usePreferencesSetContentLabelMutation() { await getAgent().setContentLabelPref(labelGroup, visibility) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -139,7 +144,7 @@ export function usePreferencesSetAdultContentMutation() { await getAgent().setAdultContentEnabled(enabled) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -153,7 +158,7 @@ export function usePreferencesSetBirthDateMutation() { await getAgent().setPersonalDetails({birthDate}) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -167,7 +172,7 @@ export function useSetFeedViewPreferencesMutation() { await getAgent().setFeedViewPrefs('home', prefs) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -181,7 +186,7 @@ export function useSetThreadViewPreferencesMutation() { await getAgent().setThreadViewPrefs(prefs) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -199,7 +204,7 @@ export function useSetSaveFeedsMutation() { await getAgent().setSavedFeeds(saved, pinned) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -214,7 +219,7 @@ export function useSaveFeedMutation() { track('CustomFeed:Save') // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -229,7 +234,7 @@ export function useRemoveFeedMutation() { track('CustomFeed:Unsave') // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -244,7 +249,7 @@ export function usePinFeedMutation() { track('CustomFeed:Pin', {uri}) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) @@ -259,7 +264,7 @@ export function useUnpinFeedMutation() { track('CustomFeed:Unpin', {uri}) // triggers a refetch await queryClient.invalidateQueries({ - queryKey: usePreferencesQueryKey, + queryKey: preferencesQueryKey, }) }, }) diff --git a/src/state/queries/preferences/moderation.ts b/src/state/queries/preferences/moderation.ts index a26380a3..cdae5293 100644 --- a/src/state/queries/preferences/moderation.ts +++ b/src/state/queries/preferences/moderation.ts @@ -34,6 +34,24 @@ export const DEFAULT_LABEL_PREFERENCES: Record< impersonation: 'hide', } +/** + * More strict than our default settings for logged in users. + * + * TODO(pwi) + */ +export const DEFAULT_LOGGED_OUT_LABEL_PREFERENCES: Record< + ConfigurableLabelGroup, + LabelPreference +> = { + nsfw: 'hide', + nudity: 'hide', + suggestive: 'hide', + gore: 'hide', + hate: 'hide', + spam: 'hide', + impersonation: 'hide', +} + export const ILLEGAL_LABEL_GROUP: LabelGroupConfig = { id: 'illegal', title: 'Illegal Content', diff --git a/src/state/queries/preferences/types.ts b/src/state/queries/preferences/types.ts index ff742067..5fca8d55 100644 --- a/src/state/queries/preferences/types.ts +++ b/src/state/queries/preferences/types.ts @@ -43,7 +43,10 @@ export type UsePreferencesQueryResponse = Omit< } } -export type ThreadViewPreferences = Omit & { +export type ThreadViewPreferences = Pick< + BskyThreadViewPreference, + 'prioritizeFollowedUsers' +> & { sort: 'oldest' | 'newest' | 'most-likes' | 'random' | string lab_treeViewEnabled?: boolean } diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 97bd6e97..d7541295 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -8,6 +8,7 @@ import * as persisted from '#/state/persisted' import {PUBLIC_BSKY_AGENT} from '#/state/queries' import {IS_PROD} from '#/lib/constants' import {emitSessionLoaded, emitSessionDropped} from '../events' +import {useLoggedOutViewControls} from '#/state/shell/logged-out' let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT @@ -515,3 +516,19 @@ export function useSession() { export function useSessionApi() { return React.useContext(ApiContext) } + +export function useRequireAuth() { + const {hasSession} = useSession() + const {setShowLoggedOut} = useLoggedOutViewControls() + + return React.useCallback( + (fn: () => void) => { + if (hasSession) { + fn() + } else { + setShowLoggedOut(true) + } + }, + [hasSession, setShowLoggedOut], + ) +} diff --git a/src/state/shell/index.tsx b/src/state/shell/index.tsx index 53f05055..897a6602 100644 --- a/src/state/shell/index.tsx +++ b/src/state/shell/index.tsx @@ -7,6 +7,7 @@ import {Provider as ColorModeProvider} from './color-mode' import {Provider as OnboardingProvider} from './onboarding' import {Provider as ComposerProvider} from './composer' import {Provider as TickEveryMinuteProvider} from './tick-every-minute' +import {Provider as LoggedOutViewProvider} from './logged-out' export {useIsDrawerOpen, useSetDrawerOpen} from './drawer-open' export { @@ -22,19 +23,23 @@ export {useTickEveryMinute} from './tick-every-minute' export function Provider({children}: React.PropsWithChildren<{}>) { return ( - - - - - - - {children} - - - - - - + + + + + + + + + {children} + + + + + + + + ) } diff --git a/src/state/shell/logged-out.tsx b/src/state/shell/logged-out.tsx new file mode 100644 index 00000000..19eaee76 --- /dev/null +++ b/src/state/shell/logged-out.tsx @@ -0,0 +1,37 @@ +import React from 'react' + +type StateContext = { + showLoggedOut: boolean +} + +const StateContext = React.createContext({ + showLoggedOut: false, +}) +const ControlsContext = React.createContext<{ + setShowLoggedOut: (show: boolean) => void +}>({ + setShowLoggedOut: () => {}, +}) + +export function Provider({children}: React.PropsWithChildren<{}>) { + const [showLoggedOut, setShowLoggedOut] = React.useState(false) + + const state = React.useMemo(() => ({showLoggedOut}), [showLoggedOut]) + const controls = React.useMemo(() => ({setShowLoggedOut}), [setShowLoggedOut]) + + return ( + + + {children} + + + ) +} + +export function useLoggedOutView() { + return React.useContext(StateContext) +} + +export function useLoggedOutViewControls() { + return React.useContext(ControlsContext) +} diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx index 3505e86a..daafa60a 100644 --- a/src/view/com/auth/LoggedOut.tsx +++ b/src/view/com/auth/LoggedOut.tsx @@ -15,7 +15,7 @@ enum ScreenState { S_CreateAccount, } -export function LoggedOut() { +export function LoggedOut({onDismiss}: {onDismiss?: () => void}) { const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() const {screen} = useAnalytics() @@ -31,6 +31,7 @@ export function LoggedOut() { if (screenState === ScreenState.S_LoginOrCreateAccount) { return ( setScreenState(ScreenState.S_Login)} onPressCreateAccount={() => setScreenState(ScreenState.S_CreateAccount)} /> diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx index 05e72a2e..2c968aef 100644 --- a/src/view/com/auth/SplashScreen.tsx +++ b/src/view/com/auth/SplashScreen.tsx @@ -1,5 +1,12 @@ import React from 'react' -import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native' +import { + SafeAreaView, + StyleSheet, + TouchableOpacity, + Pressable, + View, +} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Text} from 'view/com/util/text/Text' import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {s, colors} from 'lib/styles' @@ -9,9 +16,11 @@ import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' export const SplashScreen = ({ + onDismiss, onPressSignin, onPressCreateAccount, }: { + onDismiss?: () => void onPressSignin: () => void onPressCreateAccount: () => void }) => { @@ -20,6 +29,27 @@ export const SplashScreen = ({ return ( + {onDismiss && ( + + + + )} + diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx index f10dc4f9..08cf701d 100644 --- a/src/view/com/auth/SplashScreen.web.tsx +++ b/src/view/com/auth/SplashScreen.web.tsx @@ -1,5 +1,6 @@ import React from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' +import {StyleSheet, TouchableOpacity, View, Pressable} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Text} from 'view/com/util/text/Text' import {TextLink} from '../util/Link' import {ErrorBoundary} from 'view/com/util/ErrorBoundary' @@ -11,9 +12,11 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {Trans} from '@lingui/macro' export const SplashScreen = ({ + onDismiss, onPressSignin, onPressCreateAccount, }: { + onDismiss?: () => void onPressSignin: () => void onPressCreateAccount: () => void }) => { @@ -23,47 +26,70 @@ export const SplashScreen = ({ const isMobileWeb = isWeb && isTabletOrMobile return ( - - - - - Bluesky - - - See what's next - - - - - Create a new account - - - - - Sign In - - - - - -