Skip to content
This repository was archived by the owner on Oct 4, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions packages/common/src/services/remote-config/feature-flags.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Environment } from '../env'

/* FeatureFlags must be lowercase snake case */
export enum FeatureFlags {
SOLANA_LISTEN_ENABLED = 'solana_listen_enabled',
Expand Down Expand Up @@ -25,13 +27,27 @@ export enum FeatureFlags {
BUY_AUDIO_COINBASE_ENABLED = 'buy_audio_coinbase_enabled',
BUY_AUDIO_STRIPE_ENABLED = 'buy_audio_stripe_enabled',
OFFLINE_MODE_ENABLED = 'offline_mode_enabled',
AUTO_SUBSCRIBE_ON_FOLLOW = 'auto_subscribe_on_follow'
AUTO_SUBSCRIBE_ON_FOLLOW = 'auto_subscribe_on_follow',
MOBILE_NAV_OVERHAUL = 'mobile_nav_overhaul'
}

type FlagDefaults = Record<FeatureFlags, boolean>

export const environmentFlagDefaults: Record<

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this but I'm trying to think if it's really necessary. Can't we just flip the flag on for staging in optimizely?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this gives us granularity between dev and staging though

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah im also not sure if this is necessary... i think it's potentially helpful for that first paint where optimizely is still coming back? but yeah...

Environment,
Partial<FlagDefaults>
> = {
development: {},
staging: {
[FeatureFlags.MOBILE_NAV_OVERHAUL]: true
},
production: {}
}

/**
* If optimizely errors, these default values are used.
*/
export const flagDefaults: { [key in FeatureFlags]: boolean } = {
export const flagDefaults: FlagDefaults = {
[FeatureFlags.SOLANA_LISTEN_ENABLED]: false,
[FeatureFlags.PLAYLIST_UPDATES_ENABLED]: false,
[FeatureFlags.SHARE_SOUND_TO_TIKTOK]: false,
Expand All @@ -57,5 +73,6 @@ export const flagDefaults: { [key in FeatureFlags]: boolean } = {
[FeatureFlags.BUY_AUDIO_COINBASE_ENABLED]: false,
[FeatureFlags.BUY_AUDIO_STRIPE_ENABLED]: false,
[FeatureFlags.OFFLINE_MODE_ENABLED]: false,
[FeatureFlags.AUTO_SUBSCRIBE_ON_FOLLOW]: false
[FeatureFlags.AUTO_SUBSCRIBE_ON_FOLLOW]: false,
[FeatureFlags.MOBILE_NAV_OVERHAUL]: false
}
19 changes: 14 additions & 5 deletions packages/common/src/services/remote-config/remote-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import optimizely from '@optimizely/optimizely-sdk'
import { ID } from 'models'
import { Nullable } from 'utils'

import { Environment } from '../env'

import {
remoteConfigIntDefaults,
remoteConfigStringDefaults,
remoteConfigDoubleDefaults,
remoteConfigBooleanDefaults
} from './defaults'
import { FeatureFlags, flagDefaults } from './feature-flags'
import {
environmentFlagDefaults,
FeatureFlags,
flagDefaults
} from './feature-flags'
import {
IntKeys,
StringKeys,
Expand All @@ -36,6 +42,7 @@ export type RemoteConfigOptions<Client> = {
getFeatureFlagSessionId: () => Promise<Nullable<number>>
setFeatureFlagSessionId: (id: number) => Promise<void>
setLogLevel: () => void
environment: Environment
}

export const remoteConfig = <
Expand All @@ -52,7 +59,8 @@ export const remoteConfig = <
createOptimizelyClient,
getFeatureFlagSessionId,
setFeatureFlagSessionId,
setLogLevel
setLogLevel,
environment
}: RemoteConfigOptions<Client>) => {
const state: State = {
didInitialize: false,
Expand Down Expand Up @@ -200,10 +208,11 @@ export const remoteConfig = <
* Gets whether a given feature flag is enabled.
*/
function getFeatureEnabled(flag: FeatureFlags) {
// If the client is not ready yet, return early with `null`
if (!client || !state.id) return null
const defaultVal =
environmentFlagDefaults[environment][flag] ?? flagDefaults[flag]

const defaultVal = flagDefaults[flag]
// If the client is not ready yet, return early with `null`
if (!client || !state.id) return defaultVal

const id = state.id

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useMemo } from 'react'

import IconExploreLight from 'app/assets/animations/iconExploreLight.json'
import { colorize } from 'app/utils/colorizeLottie'
import { useThemeColors } from 'app/utils/theme'

import type { BaseBottomTabBarButtonProps } from './BottomTabBarButton'
import { BottomTabBarButton } from './BottomTabBarButton'

type NotificationsButtonProps = BaseBottomTabBarButtonProps

export const NotificationsButton = (props: NotificationsButtonProps) => {
const { primary, neutral } = useThemeColors()

const IconExplore = useMemo(
() =>
colorize(IconExploreLight, {
// icon_Explore Outlines.Group 1.Fill 1
'layers.0.shapes.0.it.4.c.k.0.s': neutral,
// icon_Explore Outlines.Group 1.Fill 1
'layers.0.shapes.0.it.4.c.k.1.s': primary,
// icon_Explore Outlines.Group 2.Fill 1
'layers.0.shapes.1.it.0.c.k.0.s': primary,
// icon_Explore Outlines.Group 2.Fill 1
'layers.0.shapes.1.it.0.c.k.1.s': neutral
}),
[primary, neutral]
)

return (
<BottomTabBarButton
name='notifications'
iconJSON={IconExplore}
{...props}
/>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ExploreButton } from './ExploreButton'
import { FavoritesButton } from './FavoritesButton'
import { FeedButton } from './FeedButton'
import { NotificationsButton } from './NotificationsButton'
import { ProfileButton } from './ProfileButton'
import { TrendingButton } from './TrendingButton'

Expand All @@ -9,11 +10,13 @@ export const bottomTabBarButtons = {
trending: TrendingButton,
explore: ExploreButton,
favorites: FavoritesButton,
profile: ProfileButton
profile: ProfileButton,
notifications: NotificationsButton
}

export * from './ExploreButton'
export * from './FavoritesButton'
export * from './FeedButton'
export * from './ProfileButton'
export * from './TrendingButton'
export * from './NotificationsButton'
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ import Drawer, {
} from 'app/components/drawer'
import { Scrubber } from 'app/components/scrubber'
import { useDrawer } from 'app/hooks/useDrawer'
import { AppDrawerContext } from 'app/screens/app-drawer-screen'
import { AppTabNavigationContext } from 'app/screens/app-screen'
import { NotificationsDrawerNavigationContext } from 'app/screens/notifications-screen/NotificationsDrawerNavigationContext'
import { getAndroidNavigationBarHeight } from 'app/store/mobileUi/selectors'
import { makeStyles } from 'app/styles'

Expand Down Expand Up @@ -111,7 +111,7 @@ export const NowPlayingDrawer = memo(function NowPlayngDrawer(
const isPlaying = useSelector(getPlaying)
const [isPlayBarShowing, setIsPlayBarShowing] = useState(false)

const { drawerNavigation } = useContext(NotificationsDrawerNavigationContext)
const { drawerNavigation } = useContext(AppDrawerContext)

// When audio starts playing, open the playbar to the initial offset
useEffect(() => {
Expand Down
6 changes: 2 additions & 4 deletions packages/mobile/src/components/user/ProfilePicture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ export const ProfilePicture = (props: ProfilePictureProps) => {
uri={profilePicture}
styles={{
...stylesProp,
...{
root: {
...styles.profilePhoto
}
root: {
...styles.profilePhoto
}
}}
{...other}
Expand Down
28 changes: 28 additions & 0 deletions packages/mobile/src/screens/account-screen/AccountDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useCallback } from 'react'

import { accountSelectors } from '@audius/common'
import type { DrawerContentComponentProps } from '@react-navigation/drawer'
import { DrawerContentScrollView } from '@react-navigation/drawer'
import { useSelector } from 'react-redux'

import { Text } from 'app/components/core'

import { useAppDrawerNavigation } from '../app-drawer-screen'
const { getAccountUser } = accountSelectors

type AccountDrawerProps = DrawerContentComponentProps

export const AccountDrawer = (props: AccountDrawerProps) => {
const accountUser = useSelector(getAccountUser)
const navigation = useAppDrawerNavigation()

const handlePressAccount = useCallback(() => {
navigation.navigate('Profile', { handle: 'accountUser' })
}, [navigation])

return (
<DrawerContentScrollView {...props}>
<Text onPress={handlePressAccount}>{accountUser?.name}</Text>
</DrawerContentScrollView>
)
}
1 change: 1 addition & 0 deletions packages/mobile/src/screens/account-screen/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './AccountDrawer'
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,25 @@ import { useMemo, createContext } from 'react'

// eslint-disable-next-line import/no-unresolved
import type { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types'
import type {
DrawerNavigationState,
NavigationProp,
ParamListBase
} from '@react-navigation/native'
import type { NavigationProp } from '@react-navigation/native'

type ContextType = {
type AppDrawerContextType = {
drawerHelpers: DrawerNavigationHelpers
drawerNavigation?: NavigationProp<any>
gesturesDisabled?: boolean
setGesturesDisabled?: (gestureDisabled: boolean) => void
state?: DrawerNavigationState<ParamListBase>
}

type NotificationsDrawerNavigationContextValue =
| ContextType
| Record<string, never>
type AppDrawerContextValue = AppDrawerContextType | Record<string, never>

export const NotificationsDrawerNavigationContext =
createContext<NotificationsDrawerNavigationContextValue>({})
export const AppDrawerContext = createContext<AppDrawerContextValue>({})

type ProviderProps = ContextType & {
type AppDrawerContextProviderProps = AppDrawerContextType & {
children: ReactNode
}

export const NotificationsDrawerNavigationContextProvider = (
props: ProviderProps
export const AppDrawerContextProvider = (
props: AppDrawerContextProviderProps
) => {
const {
children,
Expand All @@ -38,7 +30,8 @@ export const NotificationsDrawerNavigationContextProvider = (
gesturesDisabled,
setGesturesDisabled
} = props
const other = useMemo(

const context = useMemo(
() => ({
drawerHelpers,
drawerNavigation,
Expand All @@ -48,8 +41,8 @@ export const NotificationsDrawerNavigationContextProvider = (
[drawerHelpers, drawerNavigation, gesturesDisabled, setGesturesDisabled]
)
return (
<NotificationsDrawerNavigationContext.Provider value={other}>
<AppDrawerContext.Provider value={context}>
{children}
</NotificationsDrawerNavigationContext.Provider>
</AppDrawerContext.Provider>
)
}
82 changes: 82 additions & 0 deletions packages/mobile/src/screens/app-drawer-screen/AppDrawerScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useMemo, useState } from 'react'

import { FeatureFlags } from '@audius/common'
import { createDrawerNavigator } from '@react-navigation/drawer'
// eslint-disable-next-line import/no-unresolved
import type { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types'
import { useNavigation } from '@react-navigation/native'
import { Dimensions } from 'react-native'

import { useFeatureFlag } from 'app/hooks/useRemoteConfig'
import { NotificationsDrawer } from 'app/screens/notifications-screen'

import { AccountDrawer } from '../account-screen'
import { AppScreen } from '../app-screen'

import { AppDrawerContextProvider } from './AppDrawerContext'

const SCREEN_WIDTH = Dimensions.get('window').width

const baseDrawerScreenOptions = {
drawerType: 'slide' as const,
headerShown: false,
swipeEdgeWidth: SCREEN_WIDTH
}

const Drawer = createDrawerNavigator()

type AppTabScreenProps = {
navigation: DrawerNavigationHelpers
}

/**
* The app stack after signing up or signing in
*/
const AppStack = (props: AppTabScreenProps) => {
const { navigation: drawerHelpers } = props
const drawerNavigation = useNavigation()
return (
<AppDrawerContextProvider
drawerNavigation={drawerNavigation}
drawerHelpers={drawerHelpers}
>
<AppScreen />
</AppDrawerContextProvider>
)
}

export const AppDrawerScreen = () => {
const [disableGestures, setDisableGestures] = useState(false)
const { isEnabled } = useFeatureFlag(FeatureFlags.MOBILE_NAV_OVERHAUL)
const DrawerComponent = isEnabled ? AccountDrawer : NotificationsDrawer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice


const drawerScreenOptions = useMemo(
() => ({
...baseDrawerScreenOptions,
drawerStyle: {
width: isEnabled ? '75%' : '100%'
},
gestureHandlerProps: {
enabled: !disableGestures
}
}),
[isEnabled, disableGestures]
)

return (
<Drawer.Navigator
// legacy implementation uses reanimated-v1
useLegacyImplementation={true}
screenOptions={drawerScreenOptions}
drawerContent={(props) => (
<DrawerComponent
disableGestures={disableGestures}
setDisableGestures={setDisableGestures}
{...props}
/>
)}
>
<Drawer.Screen name='App' component={AppStack} />
</Drawer.Navigator>
)
}
3 changes: 3 additions & 0 deletions packages/mobile/src/screens/app-drawer-screen/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './AppDrawerScreen'
export * from './AppDrawerContext'
export * from './useAppDrawerNavigation'
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { useNavigation } from 'app/hooks/useNavigation'
import type { AppTabScreenParamList } from '../app-screen'
import type { ProfileTabScreenParamList } from '../app-screen/ProfileTabScreen'

import { NotificationsDrawerNavigationContext } from './NotificationsDrawerNavigationContext'
import { AppDrawerContext } from '.'

export const useDrawerNavigation = () => {
const { drawerHelpers } = useContext(NotificationsDrawerNavigationContext)
export const useAppDrawerNavigation = () => {
const { drawerHelpers } = useContext(AppDrawerContext)
return useNavigation<AppTabScreenParamList & ProfileTabScreenParamList>({
customNativeNavigation: drawerHelpers
})
Expand Down
Loading