Merge pull request #73 from streetwriters/feat-version-history

Feat version history
This commit is contained in:
Ammar Ahmed
2021-12-22 10:13:47 +05:00
committed by GitHub
12 changed files with 277 additions and 18 deletions

View File

@@ -79,6 +79,7 @@
.htmldiff_div {
padding: 12px !important;
padding-top: 0px !important;
overflow-x: hidden;
overflow-y: scroll;
min-height: 150px;

View File

@@ -79,6 +79,7 @@
.htmldiff_div {
padding: 12px !important;
padding-top: 0px !important;
overflow-x: hidden;
overflow-y: scroll;
min-height: 150px;

View File

@@ -1,5 +1,4 @@
import Clipboard from '@react-native-clipboard/clipboard';
import React, {useEffect, useState} from 'react';
import {
Dimensions,
@@ -29,6 +28,7 @@ import {
eSubscribeEvent,
eUnSubscribeEvent,
openVault,
presentSheet,
ToastEvent
} from '../../services/EventManager';
import Navigation from '../../services/Navigation';
@@ -58,6 +58,7 @@ import {SIZE} from '../../utils/SizeUtils';
import {sleep, timeConverter} from '../../utils/TimeUtils';
import {Button} from '../Button';
import {presentDialog} from '../Dialog/functions';
import NoteHistory from '../NoteHistory';
import {PressableButton} from '../PressableButton';
import Heading from '../Typography/Heading';
import Paragraph from '../Typography/Paragraph';
@@ -657,6 +658,20 @@ export const ActionSheetComponent = ({
positiveType: 'errorShade'
});
}
},
{
name: 'History',
title: 'History',
icon: 'history',
func: async () => {
close();
await sleep(300);
presentSheet({
noProgress: true,
noIcon: true,
component: ref => <NoteHistory ref={ref} note={note} />
});
}
}
];

View File

@@ -42,7 +42,10 @@ const GeneralSheet = ({context}) => {
actionSheetRef.current?.setModalVisible(true);
};
const close = () => {
const close = ctx => {
if ((ctx && !context) || (ctx && ctx !== context)) {
return;
}
actionSheetRef.current?.setModalVisible(false);
};

View File

@@ -0,0 +1,97 @@
import React, {useCallback, useEffect, useState} from 'react';
import {View} from 'react-native';
import {FlatList} from 'react-native-gesture-handler';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {useTracked} from '../../provider';
import {presentSheet} from '../../services/EventManager';
import {db} from '../../utils/database';
import {timeConverter} from '../../utils/TimeUtils';
import DialogHeader from '../Dialog/dialog-header';
import GeneralSheet from '../GeneralSheet';
import {PressableButton} from '../PressableButton';
import Seperator from '../Seperator';
import Paragraph from '../Typography/Paragraph';
import NotePreview from './preview';
export default function NoteHistory({note, ref}) {
const [history, setHistory] = useState([]);
const [loading, setLoading] = useState(true);
const [state] = useTracked();
const {colors} = state;
useEffect(() => {
(async () => {
console.log(note.id);
console.log(await db.noteHistory.get(note.id));
setHistory([...(await db.noteHistory.get(note.id))].reverse());
setLoading(false);
})();
}, []);
async function preview(item) {
let content = await db.noteHistory.content(item.sessionContentId);
presentSheet({
component: <NotePreview session={item} content={content} />,
context: 'note_history',
noProgress: true,
noIcon: true
});
}
const renderItem = useCallback(
({item, index}) => (
<PressableButton
type="grayBg"
onPress={() => preview(item)}
customStyle={{
justifyContent: 'center',
alignItems: 'flex-start',
paddingHorizontal: 12,
height: 45,
marginBottom: 10
}}>
<Paragraph>{timeConverter(item.dateEdited)}</Paragraph>
</PressableButton>
),
[]
);
return (
<View>
<GeneralSheet context="note_history" />
<DialogHeader
title="Note history"
paragraph="Revert back to an older version of this note"
padding={12}
/>
<Seperator />
<FlatList
onMomentumScrollEnd={() => {
ref?.current?.handleChildScrollEnd();
}}
style={{
paddingHorizontal: 12
}}
keyExtractor={item => item.id}
data={history}
ListEmptyComponent={
<View
style={{
width: '100%',
justifyContent: 'center',
alignItems: 'center',
height: 200
}}>
<Icon name="history" size={60} color={colors.icon} />
<Paragraph color={colors.icon}>No note history found.</Paragraph>
</View>
}
renderItem={renderItem}
/>
</View>
);
}

View File

@@ -0,0 +1,119 @@
import React, {useRef} from 'react';
import {View} from 'react-native';
import WebView from 'react-native-webview';
import {useTracked} from '../../provider';
import {eSendEvent, ToastEvent} from '../../services/EventManager';
import Navigation from '../../services/Navigation';
import {dHeight} from '../../utils';
import {db} from '../../utils/database';
import {eCloseProgressDialog} from '../../utils/Events';
import {normalize} from '../../utils/SizeUtils';
import {timeConverter} from '../../utils/TimeUtils';
import {sourceUri} from '../../views/Editor/Functions';
import {Button} from '../Button';
import DialogHeader from '../Dialog/dialog-header';
export default function NotePreview({session, content}) {
const [state] = useTracked();
const {colors} = state;
const webviewRef = useRef();
const onLoad = async () => {
console.log(content);
let preview = await db.content.insertPlaceholders(
content,
'placeholder.svg'
);
console.log(preview, 'preview');
postMessage('htmldiff', preview?.data);
let theme = {...colors};
theme.factor = normalize(1);
postMessage('theme', JSON.stringify(theme));
};
function postMessage(type, value = null) {
let message = {
type: type,
value
};
webviewRef.current?.postMessage(JSON.stringify(message));
}
const _onShouldStartLoadWithRequest = request => {
if (request.url.includes('http')) {
openLinkInBrowser(request.url, colors)
.catch(e =>
ToastEvent.show({
title: 'Failed to open link',
message: e.message,
type: 'success',
context: 'local'
})
)
.then(r => {
console.log('closed');
});
return false;
} else {
return true;
}
};
async function restore() {
await db.noteHistory.restore(session.id);
eSendEvent(eCloseProgressDialog, 'note_history');
eSendEvent(eCloseProgressDialog);
Navigation.setRoutesToUpdate([
Navigation.routeNames.NotesPage,
Navigation.routeNames.Favorites,
Navigation.routeNames.Notes
]);
ToastEvent.show({
heading: 'Note restored successfully',
type: 'success'
});
}
return (
<View
style={{
height: 600,
width: '100%'
}}>
<DialogHeader padding={12} title={timeConverter(session.dateEdited)} />
<WebView
ref={webviewRef}
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
onLoad={onLoad}
style={{
width: '100%',
height: '100%',
backgroundColor: 'transparent'
}}
cacheMode="LOAD_DEFAULT"
domStorageEnabled={true}
scrollEnabled={true}
bounces={false}
allowFileAccess={true}
scalesPageToFit={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
javaScriptEnabled={true}
cacheEnabled={true}
source={{
uri: sourceUri + 'plaineditor.html'
}}
/>
<View
style={{
paddingHorizontal: 12
}}>
<Button onPress={restore} title="Restore" type="accent" width="100%" />
</View>
</View>
);
}

View File

@@ -67,6 +67,7 @@ const showActionSheet = (item, isTrash) => {
'Vault',
'Delete',
'RemoveTopic',
"History",
...android
]
);

View File

@@ -39,7 +39,6 @@ export const SectionHeader = ({item, index, type, color, screen}) => {
: groupBy.slice(0, 1).toUpperCase() + groupBy.slice(1, groupBy.length);
const onUpdate = () => {
console.log(groupOptions);
setGroupOptions({...db.settings?.getGroupOptions(type)});
};

View File

@@ -381,7 +381,6 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
set({announcements: []});
} finally {
let all = await getFiltered(announcements);
console.log("all", all)
set({
announcements: all.filter(a => a.type === 'inline'),
dialogs: all.filter(a => a.type === 'dialog')

View File

@@ -64,14 +64,11 @@ function routeNeedsUpdate(routeName, callback) {
* @param {array} routes
*/
function setRoutesToUpdate(routes) {
console.log(currentScreen, 'current');
if (routes.indexOf(currentScreen) > -1) {
console.log('updating screen', currentScreen);
if (
currentScreen === routeNames.NotesPage ||
currentScreen === routeNames.Notebook
) {
console.log(currentScreen, 'CURRENT');
eSendEvent(
currentScreen === routeNames.NotesPage
? refreshNotesPage

View File

@@ -61,6 +61,7 @@ let waitForContent = false;
let prevNoteContent = null;
let timerForEditor = null;
let sessionId = null;
let historySessionId = null;
export function startClosingSession() {
closingSession = true;
@@ -228,6 +229,7 @@ function clearNote() {
note = null;
title = '';
noteEdited = false;
historySessionId = null;
prevNoteContent = content.data;
isSaving = false;
id = null;
@@ -817,10 +819,36 @@ export async function saveNote(preventUpdate) {
);
tiny.call(EditorWebView, tiny.updateSavingState(!n ? '' : 'Saved'));
}
await updateSessionHistory(id, noteData.content);
} catch (e) {}
isSaving = false;
}
async function updateSessionHistory(id, content) {
let note = db.notes.note(id)?.data;
if (!note) return;
if (!historySessionId) {
historySessionId = `${id}_${note.dateEdited}`;
}
if (!historySessionId.includes(id)) {
historySessionId = null;
return;
}
if (!note.locked) {
console.log('saving session with id: ',historySessionId);
await db.noteHistory.add(id, historySessionId, content);
} else {
let content = await db.content.get(note.contentId);
await db.noteHistory.add(id, historySessionId, {
data: content.data,
type: content.type
});
}
}
export async function onWebViewLoad(premium, colors) {
setTimeout(() => {
if (premium) {

View File

@@ -1,9 +1,9 @@
import React, {useEffect, useState} from 'react';
import {Platform, TouchableOpacity, View} from 'react-native';
import React, { useEffect, useState } from 'react';
import { Platform, TouchableOpacity, View } from 'react-native';
import ToggleSwitch from 'toggle-switch-react-native';
import Paragraph from '../../components/Typography/Paragraph';
import {useTracked} from '../../provider';
import {useSettingStore, useUserStore} from '../../provider/stores';
import { useTracked } from '../../provider';
import { useSettingStore, useUserStore } from '../../provider/stores';
import Backup from '../../services/Backup';
import {
eSendEvent,
@@ -17,14 +17,13 @@ import {
eOpenLoginDialog,
eOpenRestoreDialog
} from '../../utils/Events';
import {openLinkInBrowser} from '../../utils/functions';
import {MMKV} from '../../utils/mmkv';
import {SIZE} from '../../utils/SizeUtils';
import {sleep} from '../../utils/TimeUtils';
import {CustomButton} from './button';
import {verifyUser} from './functions';
import { openLinkInBrowser } from '../../utils/functions';
import { MMKV } from '../../utils/mmkv';
import { SIZE } from '../../utils/SizeUtils';
import { sleep } from '../../utils/TimeUtils';
import { CustomButton } from './button';
import { verifyUser } from './functions';
import SectionHeader from './section-header';
import * as ScopedStorage from 'react-native-scoped-storage';
const SettingsBackupAndRestore = ({isSheet}) => {
const [state] = useTracked();