mirror of
https://github.com/standardnotes/mobile.git
synced 2026-04-19 05:39:20 -04:00
* feat: add chip component
* feat: pass search options from state to snjs search
* feat: add keys to chip items
* feat: enable/disable protections chip depending on protections status
* feat: make selecting protected chip a protected action
* feat: change wording
* feat: add horizontal scrolling
* feat: add view protected note screen
* feat: remove protect note alert
* feat: show protected content when chip is selected
* fix: hide horizontal scrollbar
* fix: fix view protected screen wording
* Revert "feat: show protected content when chip is selected"
This reverts commit 367a7e7a14.
* fix: add search filter to query condition
* fix: apply filters only when searching
* fix: change wording for one search result
* fix: automatically re-focus input after enabling protected search
* fix: improve is focusing search
* feat: improve view protected note screen
* fix: use onCancel to hide chips on Android
* fix: use useFocusEffect instead of focus listener
* fix: add margin to chips for Android
* feat: add space between icon and placeholder on Android
* fix: fix Android search bar focus
* fix: fix wording for 0 search results
* feat: better wording for view protected note text
* feat: add search options container animation
* feat: add chips animation
* chore: update search box
* feat: make search options animation faster
* fix: always show all chips except when on archived or trash tag
* fix: add bg color to search container
* feat: make chips animation faster
* fix: only toggle chip when selected has changed
* fix: remove elevation for search bar container
* chore: upgrade search box
* feat: use native driver for chip animation
* feat: add opacity animation to search options
* feat: adjust animations duration
* feat: make chip animation faster
287 lines
9.4 KiB
TypeScript
287 lines
9.4 KiB
TypeScript
import { HeaderTitleView } from '@Components/HeaderTitleView';
|
|
import { IoniconsHeaderButton } from '@Components/IoniconsHeaderButton';
|
|
import {
|
|
AppStateEventType,
|
|
AppStateType,
|
|
TabletModeChangeData,
|
|
} from '@Lib/application_state';
|
|
import { useHasEditor, useIsLocked } from '@Lib/snjs_helper_hooks';
|
|
import { ScreenStatus } from '@Lib/status_manager';
|
|
import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
|
|
import {
|
|
createStackNavigator,
|
|
StackNavigationProp,
|
|
} from '@react-navigation/stack';
|
|
import { Compose } from '@Screens/Compose/Compose';
|
|
import { Root } from '@Screens/Root';
|
|
import {
|
|
SCREEN_COMPOSE,
|
|
SCREEN_NOTES,
|
|
SCREEN_VIEW_PROTECTED_NOTE,
|
|
} from '@Screens/screens';
|
|
import { MainSideMenu } from '@Screens/SideMenu/MainSideMenu';
|
|
import { NoteSideMenu } from '@Screens/SideMenu/NoteSideMenu';
|
|
import { ViewProtectedNote } from '@Screens/ViewProtectedNote/ViewProtectedNote';
|
|
import { ICON_MENU } from '@Style/icons';
|
|
import { ThemeService } from '@Style/theme_service';
|
|
import { getDefaultDrawerWidth } from '@Style/utils';
|
|
import React, {
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
import { Dimensions, Keyboard, ScaledSize } from 'react-native';
|
|
import DrawerLayout, {
|
|
DrawerState,
|
|
} from 'react-native-gesture-handler/DrawerLayout';
|
|
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
|
|
import { ThemeContext } from 'styled-components';
|
|
import { HeaderTitleParams } from './App';
|
|
import { ApplicationContext } from './ApplicationContext';
|
|
import { ModalStackNavigationProp } from './ModalStack';
|
|
|
|
type AppStackNavigatorParamList = {
|
|
[SCREEN_NOTES]: HeaderTitleParams;
|
|
[SCREEN_COMPOSE]: HeaderTitleParams | undefined;
|
|
[SCREEN_VIEW_PROTECTED_NOTE]: {
|
|
onPressView: () => void;
|
|
};
|
|
};
|
|
|
|
export type AppStackNavigationProp<
|
|
T extends keyof AppStackNavigatorParamList
|
|
> = {
|
|
navigation: CompositeNavigationProp<
|
|
ModalStackNavigationProp<'AppStack'>['navigation'],
|
|
StackNavigationProp<AppStackNavigatorParamList, T>
|
|
>;
|
|
route: RouteProp<AppStackNavigatorParamList, T>;
|
|
};
|
|
|
|
const AppStack = createStackNavigator<AppStackNavigatorParamList>();
|
|
|
|
export const AppStackComponent = (
|
|
props: ModalStackNavigationProp<'AppStack'>
|
|
) => {
|
|
// Context
|
|
const application = useContext(ApplicationContext);
|
|
const theme = useContext(ThemeContext);
|
|
const [isLocked] = useIsLocked();
|
|
const [hasEditor] = useHasEditor();
|
|
|
|
// State
|
|
const [dimensions, setDimensions] = useState(() => Dimensions.get('window'));
|
|
const [isInTabletMode, setIsInTabletMode] = useState(
|
|
() => application?.getAppState().isInTabletMode
|
|
);
|
|
const [notesStatus, setNotesStatus] = useState<ScreenStatus>();
|
|
const [composeStatus, setComposeStatus] = useState<ScreenStatus>();
|
|
const [noteDrawerOpen, setNoteDrawerOpen] = useState(false);
|
|
|
|
// Ref
|
|
const drawerRef = useRef<DrawerLayout>(null);
|
|
const noteDrawerRef = useRef<DrawerLayout>(null);
|
|
|
|
useEffect(() => {
|
|
const removeObserver = application
|
|
?.getAppState()
|
|
.addStateChangeObserver(event => {
|
|
if (event === AppStateType.EditorClosed) {
|
|
noteDrawerRef.current?.closeDrawer();
|
|
if (!isInTabletMode && props.navigation.canGoBack()) {
|
|
props.navigation.popToTop();
|
|
}
|
|
}
|
|
});
|
|
|
|
return removeObserver;
|
|
}, [application, props.navigation, isInTabletMode]);
|
|
|
|
useEffect(() => {
|
|
const removeObserver = application
|
|
?.getStatusManager()
|
|
.addHeaderStatusObserver(messages => {
|
|
setNotesStatus(messages[SCREEN_NOTES]);
|
|
setComposeStatus(messages[SCREEN_COMPOSE]);
|
|
});
|
|
|
|
return removeObserver;
|
|
}, [application, isInTabletMode]);
|
|
|
|
useEffect(() => {
|
|
const updateDimensions = ({ window }: { window: ScaledSize }) => {
|
|
setDimensions(window);
|
|
};
|
|
|
|
Dimensions.addEventListener('change', updateDimensions);
|
|
|
|
return () => Dimensions.removeEventListener('change', updateDimensions);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const remoteTabletModeSubscription = application
|
|
?.getAppState()
|
|
.addStateEventObserver((event, data) => {
|
|
if (event === AppStateEventType.TabletModeChange) {
|
|
const eventData = data as TabletModeChangeData;
|
|
if (eventData.new_isInTabletMode && !eventData.old_isInTabletMode) {
|
|
setIsInTabletMode(true);
|
|
} else if (
|
|
!eventData.new_isInTabletMode &&
|
|
eventData.old_isInTabletMode
|
|
) {
|
|
setIsInTabletMode(false);
|
|
}
|
|
}
|
|
});
|
|
|
|
return remoteTabletModeSubscription;
|
|
}, [application]);
|
|
|
|
const handleDrawerStateChange = useCallback(
|
|
(newState: DrawerState, drawerWillShow: boolean) => {
|
|
if (newState !== 'Idle' && drawerWillShow) {
|
|
application?.getAppState().onDrawerOpen();
|
|
}
|
|
},
|
|
[application]
|
|
);
|
|
|
|
return (
|
|
<DrawerLayout
|
|
ref={drawerRef}
|
|
drawerWidth={getDefaultDrawerWidth(dimensions)}
|
|
drawerPosition={'left'}
|
|
drawerType="slide"
|
|
drawerLockMode={
|
|
hasEditor && !isInTabletMode ? 'locked-closed' : 'unlocked'
|
|
}
|
|
onDrawerStateChanged={handleDrawerStateChange}
|
|
renderNavigationView={() =>
|
|
!isLocked && <MainSideMenu drawerRef={drawerRef.current} />
|
|
}
|
|
>
|
|
<DrawerLayout
|
|
ref={noteDrawerRef}
|
|
drawerWidth={getDefaultDrawerWidth(dimensions)}
|
|
onDrawerStateChanged={handleDrawerStateChange}
|
|
onDrawerOpen={() => setNoteDrawerOpen(true)}
|
|
onDrawerClose={() => setNoteDrawerOpen(false)}
|
|
drawerPosition={'right'}
|
|
drawerType="slide"
|
|
drawerLockMode={hasEditor ? 'unlocked' : 'locked-closed'}
|
|
renderNavigationView={() =>
|
|
hasEditor && (
|
|
<NoteSideMenu
|
|
drawerOpen={noteDrawerOpen}
|
|
drawerRef={noteDrawerRef.current}
|
|
/>
|
|
)
|
|
}
|
|
>
|
|
<AppStack.Navigator
|
|
screenOptions={() => ({
|
|
headerStyle: {
|
|
backgroundColor: theme.stylekitContrastBackgroundColor,
|
|
},
|
|
headerTintColor: theme.stylekitInfoColor,
|
|
headerTitle: ({ children }) => {
|
|
return <HeaderTitleView title={children || ''} />;
|
|
},
|
|
})}
|
|
initialRouteName={SCREEN_NOTES}
|
|
>
|
|
<AppStack.Screen
|
|
name={SCREEN_NOTES}
|
|
options={({ route }) => ({
|
|
title: 'All notes',
|
|
headerTitle: ({ children }) => {
|
|
const screenStatus = isInTabletMode
|
|
? composeStatus || notesStatus
|
|
: notesStatus;
|
|
return (
|
|
<HeaderTitleView
|
|
title={route.params?.title ?? (children || '')}
|
|
subtitle={screenStatus?.status}
|
|
subtitleColor={screenStatus?.color}
|
|
/>
|
|
);
|
|
},
|
|
headerLeft: () => (
|
|
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
|
|
<Item
|
|
testID="drawerButton"
|
|
disabled={false}
|
|
title={''}
|
|
iconName={ThemeService.nameForIcon(ICON_MENU)}
|
|
onPress={() => {
|
|
Keyboard.dismiss();
|
|
drawerRef.current?.openDrawer();
|
|
}}
|
|
/>
|
|
</HeaderButtons>
|
|
),
|
|
headerRight: () =>
|
|
isInTabletMode &&
|
|
hasEditor && (
|
|
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
|
|
<Item
|
|
testID="noteDrawerButton"
|
|
disabled={false}
|
|
title={''}
|
|
iconName={ThemeService.nameForIcon(ICON_MENU)}
|
|
onPress={() => {
|
|
Keyboard.dismiss();
|
|
noteDrawerRef.current?.openDrawer();
|
|
}}
|
|
/>
|
|
</HeaderButtons>
|
|
),
|
|
})}
|
|
component={Root}
|
|
/>
|
|
<AppStack.Screen
|
|
name={SCREEN_COMPOSE}
|
|
options={({ route }) => ({
|
|
headerTitle: ({ children }) => {
|
|
return (
|
|
<HeaderTitleView
|
|
title={route.params?.title ?? (children || '')}
|
|
subtitle={composeStatus?.status}
|
|
subtitleColor={composeStatus?.color}
|
|
/>
|
|
);
|
|
},
|
|
headerRight: () =>
|
|
!isInTabletMode && (
|
|
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
|
|
<Item
|
|
testID="noteDrawerButton"
|
|
disabled={false}
|
|
title={''}
|
|
iconName={ThemeService.nameForIcon(ICON_MENU)}
|
|
onPress={() => {
|
|
Keyboard.dismiss();
|
|
noteDrawerRef.current?.openDrawer();
|
|
}}
|
|
/>
|
|
</HeaderButtons>
|
|
),
|
|
})}
|
|
component={Compose}
|
|
/>
|
|
<AppStack.Screen
|
|
name={SCREEN_VIEW_PROTECTED_NOTE}
|
|
options={() => ({
|
|
title: 'View Protected Note',
|
|
})}
|
|
component={ViewProtectedNote}
|
|
/>
|
|
</AppStack.Navigator>
|
|
</DrawerLayout>
|
|
</DrawerLayout>
|
|
);
|
|
};
|