Skip to content
This repository has been archived by the owner on Jun 15, 2022. It is now read-only.

Commit

Permalink
feature: note session history
Browse files Browse the repository at this point in the history
  • Loading branch information
radko93 committed Sep 2, 2020
1 parent 1968f41 commit 2fe274e
Show file tree
Hide file tree
Showing 14 changed files with 498 additions and 3 deletions.
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Expand Up @@ -243,6 +243,8 @@ PODS:
- React
- react-native-safe-area-context (3.1.6):
- React
- react-native-segmented-control (2.1.1):
- React
- react-native-sodium (0.4.0):
- React
- react-native-version-info (1.0.1):
Expand Down Expand Up @@ -370,6 +372,7 @@ DEPENDENCIES:
- react-native-fingerprint-scanner (from `../node_modules/react-native-fingerprint-scanner`)
- react-native-mail (from `../node_modules/react-native-mail`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- "react-native-segmented-control (from `../node_modules/@react-native-community/segmented-control`)"
- react-native-sodium (from `../node_modules/react-native-sodium`)
- react-native-version-info (from `../node_modules/react-native-version-info`)
- react-native-webview (from `../node_modules/react-native-webview`)
Expand Down Expand Up @@ -457,6 +460,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-mail"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-segmented-control:
:path: "../node_modules/@react-native-community/segmented-control"
react-native-sodium:
:path: "../node_modules/react-native-sodium"
react-native-version-info:
Expand Down Expand Up @@ -547,6 +552,7 @@ SPEC CHECKSUMS:
react-native-fingerprint-scanner: f0d8190ceaf0b9e1893e3379d78724375b8f6ea7
react-native-mail: a864fb211feaa5845c6c478a3266de725afdce89
react-native-safe-area-context: 4fb3cdeb4a405ec19f18aca80ef2144381ffc761
react-native-segmented-control: 05d93c1c7576b53189720012a3e8a3c23608d849
react-native-sodium: ef43e28fdf8d866e68ed06890c32f8d86a570cc7
react-native-version-info: f95938832bdb2ce94c3d045c2e44fabcc88f796a
react-native-webview: 0e49a2e85f42b20ea5579928a8b254a07fa901dc
Expand Down
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -22,6 +22,7 @@
"@expo/react-native-action-sheet": "^3.8.0",
"@react-native-community/async-storage": "1.12.0",
"@react-native-community/masked-view": "^0.1.10",
"@react-native-community/segmented-control": "^2.1.1",
"@react-navigation/native": "^5.7.3",
"@react-navigation/stack": "^5.9.0",
"base64-arraybuffer": "^0.2.0",
Expand All @@ -47,6 +48,7 @@
"react-native-search-box": "standardnotes/react-native-search-box#f61a2b5",
"react-native-sodium": "standardnotes/react-native-sodium#f68a35b",
"react-native-store-review": "^0.1.5",
"react-native-tab-view": "^2.15.1",
"react-native-vector-icons": "^7.0.0",
"react-native-version-info": "^1.0.1",
"react-native-webview": "^10.8.2",
Expand Down
23 changes: 22 additions & 1 deletion src/App.tsx
Expand Up @@ -25,6 +25,7 @@ import { AuthenticatePrivileges } from '@Screens/Authenticate/AuthenticatePrivil
import { Compose } from '@Screens/Compose/Compose';
import { PasscodeInputModal } from '@Screens/InputModal/PasscodeInputModal';
import { TagInputModal } from '@Screens/InputModal/TagInputModal';
import { NoteHistory } from '@Screens/NoteHistory/NoteHistory';
import { Root } from '@Screens/Root';
import { ManagePrivileges } from '@Screens/Settings/ManagePrivileges';
import { Settings } from '@Screens/Settings/Settings';
Expand All @@ -33,7 +34,7 @@ import { NoteSideMenu } from '@Screens/SideMenu/NoteSideMenu';
import { ICON_CHECKMARK, ICON_CLOSE, ICON_MENU } from '@Style/icons';
import { StyleKit, StyleKitContext } from '@Style/StyleKit';
import { StyleKitTheme } from '@Style/Themes/styled-components';
import { getDefaultDrawerWidth } from '@Style/Util/getDefaultDraerWidth';
import { getDefaultDrawerWidth } from '@Style/Util/getDefaultDrawerWidth';
import React, {
useCallback,
useContext,
Expand Down Expand Up @@ -63,6 +64,7 @@ import {
SCREEN_INPUT_MODAL_TAG,
SCREEN_MANAGE_PRIVILEGES,
SCREEN_NOTES,
SCREEN_NOTE_HISTORY,
SCREEN_SETTINGS,
} from './screens/screens';

Expand All @@ -75,6 +77,9 @@ type HeaderTitleParams = {
type AppStackNavigatorParamList = {
[SCREEN_NOTES]: HeaderTitleParams;
[SCREEN_COMPOSE]: HeaderTitleParams | undefined;
[SCREEN_NOTE_HISTORY]:
| (HeaderTitleParams & { noteUuid: string })
| (undefined & { noteUuid: string });
};

type ModalStackNavigatorParamList = {
Expand Down Expand Up @@ -294,6 +299,22 @@ const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) => {
})}
component={Compose}
/>
<AppStack.Screen
name={SCREEN_NOTE_HISTORY}
options={({ route }) => ({
title: 'Note history',
headerTitle: ({ children }) => {
return (
<HeaderTitleView
title={route.params?.title ?? (children || '')}
subtitle={route.params?.subTitle}
subtitleColor={route.params?.subTitleColor}
/>
);
},
})}
component={NoteHistory}
/>
</AppStack.Navigator>
</DrawerLayout>
</DrawerLayout>
Expand Down
17 changes: 17 additions & 0 deletions src/screens/NoteHistory/NoteHistory.styled.ts
@@ -0,0 +1,17 @@
import styled from 'styled-components/native';

export const Container = styled.View``;

export const DateText = styled.Text`
font-size: 15px;
margin-top: 4px;
opacity: 0.8;
line-height: 21px;
`;

export const IosTabBarContainer = styled.View`
padding-top: 10px;
padding-bottom: 5px;
padding-left: 12px;
padding-right: 12px;
`;
145 changes: 145 additions & 0 deletions src/screens/NoteHistory/NoteHistory.tsx
@@ -0,0 +1,145 @@
import SegmentedControl from '@react-native-community/segmented-control';
import { AppStackNavigationProp } from '@Root/App';
import { ApplicationContext } from '@Root/ApplicationContext';
import { SCREEN_NOTE_HISTORY } from '@Screens/screens';
import { StyleKitContext } from '@Style/StyleKit';
import React, { useCallback, useContext, useState } from 'react';
import { Dimensions, Platform } from 'react-native';
import {
NavigationState,
Route,
SceneRendererProps,
TabBar,
TabView,
} from 'react-native-tab-view';
import { ButtonType, ContentType, PayloadSource, SNNote } from 'snjs';
import { PayloadContent } from 'snjs/dist/@types/protocol/payloads/generator';
import { ThemeContext } from 'styled-components/native';
import { IosTabBarContainer } from './NoteHistory.styled';
import { RemoteHistory } from './RemoteHistory';
import { SessionHistory } from './SessionHistory';

const initialLayout = { width: Dimensions.get('window').width };

type Props = AppStackNavigationProp<typeof SCREEN_NOTE_HISTORY>;
export const NoteHistory = (props: Props) => {
// Context
const application = useContext(ApplicationContext);
const theme = useContext(ThemeContext);
const styleKit = useContext(StyleKitContext);

// State
const [note] = useState<SNNote>(
() => application?.findItem(props.route.params.noteUuid) as SNNote
);
const [routes] = React.useState([
{ key: 'session', title: 'Session' },
{ key: 'remote', title: 'Remote' },
]);
const [index, setIndex] = useState(0);

const restore = useCallback(
async (asCopy: boolean, revisionUuid: string, content: PayloadContent) => {
const run = async () => {
if (asCopy) {
const contentCopy = Object.assign({}, content);
if (contentCopy.title) {
contentCopy.title += ' (copy)';
}
await application?.createManagedItem(
ContentType.Note,
contentCopy,
true
);
props.navigation.popToTop();
} else {
await application?.changeAndSaveItem(
revisionUuid,
mutator => {
mutator.setContent(content);
},
true,
PayloadSource.RemoteActionRetrieved
);
props.navigation.goBack();
}
};

if (!asCopy) {
const confirmed = await application?.alertService?.confirm(
"Are you sure you want to replace the current note's contents with what you see in this preview?",
'Restore note',
'Restore',
ButtonType.Info
);
if (confirmed) {
run();
}
} else {
run();
}
},
[application, props.navigation]
);

const renderScene = ({
route,
}: {
route: { key: string; title: string };
}) => {
switch (route.key) {
case 'session':
return <SessionHistory restoreNote={restore} note={note} />;
case 'remote':
return <RemoteHistory restoreNote={restore} note={note} />;
default:
return null;
}
};

const renderTabBar = (
tabBarProps: SceneRendererProps & {
navigationState: NavigationState<Route>;
}
) => {
return Platform.OS === 'ios' &&
parseInt(Platform.Version as string, 10) >= 13 ? (
<IosTabBarContainer>
<SegmentedControl
backgroundColor={theme.stylekitContrastBackgroundColor}
appearance={styleKit?.keyboardColorForActiveTheme()}
fontStyle={{
color: theme.stylekitForegroundColor,
}}
values={routes.map(route => route.title)}
selectedIndex={tabBarProps.navigationState.index}
onChange={event => {
setIndex(event.nativeEvent.selectedSegmentIndex);
}}
/>
</IosTabBarContainer>
) : (
<TabBar
{...tabBarProps}
indicatorStyle={{ backgroundColor: theme.stylekitInfoColor }}
inactiveColor={theme.stylekitBorderColor}
activeColor={theme.stylekitInfoColor}
style={{
backgroundColor: theme.stylekitBackgroundColor,
shadowColor: theme.stylekitShadowColor,
}}
labelStyle={{ color: theme.stylekitInfoColor }}
/>
);
};

return (
<TabView
renderTabBar={renderTabBar}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
/>
);
};
64 changes: 64 additions & 0 deletions src/screens/NoteHistory/NoteHistoryCell.tsx
@@ -0,0 +1,64 @@
import {
Props as TableCellProps,
SectionedTableCellTouchableHighlight,
} from '@Components/SectionedTableCell';
import React from 'react';
import styled, { css } from 'styled-components/native';

type Props = {
testID?: string;
disabled?: boolean;
onPress: () => void;
first?: boolean;
last?: boolean;
title: string;
subTitle?: string;
};

const Container = styled(SectionedTableCellTouchableHighlight).attrs(props => ({
underlayColor: props.theme.stylekitBorderColor,
}))<TableCellProps>`
padding-top: ${12}px;
justify-content: center;
`;
const ButtonContainer = styled.View``;

type ButtonLabelProps = Pick<Props, 'disabled'>;
const ButtonLabel = styled.Text<ButtonLabelProps>`
color: ${props => {
let color = props.theme.stylekitForegroundColor;
if (props.disabled) {
color = 'gray';
}
return color;
}};
font-weight: bold;
font-size: ${props => props.theme.mainTextFontSize}px;
${({ disabled }) =>
disabled &&
css`
opacity: 0.6;
`}
`;
export const SubTitleText = styled.Text`
font-size: 14px;
margin-top: 4px;
color: ${({ theme }) => theme.stylekitForegroundColor};
opacity: 0.8;
line-height: 21px;
`;

export const NoteHistoryCell: React.FC<Props> = props => (
<Container
first={props.first}
last={props.last}
testID={props.testID}
disabled={props.disabled}
onPress={props.onPress}
>
<ButtonContainer>
<ButtonLabel disabled={props.disabled}>{props.title}</ButtonLabel>
{props.subTitle && <SubTitleText>{props.subTitle}</SubTitleText>}
</ButtonContainer>
</Container>
);

0 comments on commit 2fe274e

Please sign in to comment.