Files
notesnook/apps/mobile/src/components/mergeconflicts/index.js

322 lines
9.3 KiB
JavaScript
Raw Normal View History

2021-07-08 09:34:21 +05:00
import KeepAwake from '@sayem314/react-native-keep-awake';
2022-06-30 09:45:28 +05:00
import React, { useEffect, useRef, useState } from 'react';
import { Modal, SafeAreaView, Text, View } from 'react-native';
import Animated from 'react-native-reanimated';
2022-01-22 12:57:05 +05:00
import { useSafeAreaInsets } from 'react-native-safe-area-context';
2022-06-30 09:45:28 +05:00
import Editor from '../../screens/editor';
2022-05-20 14:49:38 +05:00
import { editorController } from '../../screens/editor/tiptap/utils';
2022-02-28 23:25:18 +05:00
import { DDS } from '../../services/device-detection';
2022-06-30 09:45:28 +05:00
import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent } from '../../services/event-manager';
2022-02-28 23:25:18 +05:00
import Navigation from '../../services/navigation';
import Sync from '../../services/sync';
2022-05-20 14:49:38 +05:00
import { useThemeStore } from '../../stores/use-theme-store';
2022-01-22 12:57:05 +05:00
import { dHeight } from '../../utils';
import { db } from '../../utils/database';
2022-06-30 09:45:28 +05:00
import { eOnLoadNote, eShowMergeDialog } from '../../utils/events';
import { SIZE } from '../../utils/size';
2022-02-28 13:48:59 +05:00
import { timeConverter } from '../../utils/time';
import BaseDialog from '../dialog/base-dialog';
import DialogButtons from '../dialog/dialog-buttons';
import DialogContainer from '../dialog/dialog-container';
import DialogHeader from '../dialog/dialog-header';
2022-05-20 14:49:38 +05:00
import { Button } from '../ui/button';
import { IconButton } from '../ui/icon-button';
2022-02-28 13:48:59 +05:00
import Seperator from '../ui/seperator';
import Paragraph from '../ui/typography/paragraph';
2022-05-20 14:49:38 +05:00
2022-02-28 15:32:55 +05:00
const MergeConflicts = () => {
2022-02-28 23:25:18 +05:00
const colors = useThemeStore(state => state.colors);
2020-03-26 15:42:37 +05:00
const [visible, setVisible] = useState(false);
2022-06-30 09:45:28 +05:00
const [keep, setKeep] = useState(null);
const [copy, setCopy] = useState(null);
const [dialogVisible, setDialogVisible] = useState(false);
2020-11-29 12:21:36 +05:00
const insets = useSafeAreaInsets();
2022-06-30 09:45:28 +05:00
const content = useRef({});
const isKeepingConflicted = !keep?.conflicted;
const isKeeping = !!keep;
2020-03-26 13:39:04 +05:00
2020-03-26 15:42:37 +05:00
const applyChanges = async () => {
2022-06-30 09:45:28 +05:00
let _content = keep;
let note = db.notes.note(_content.noteId).data;
await db.notes.add({
id: note.id,
2021-12-31 11:05:42 +05:00
conflicted: false,
2022-06-30 09:45:28 +05:00
dateEdited: _content.dateEdited
2021-12-31 11:05:42 +05:00
});
await db.content.add({
id: note.contentId,
2022-06-30 09:45:28 +05:00
data: _content.data,
type: _content.type,
dateResolved: content.current.conflicted.dateModified,
2021-12-31 11:05:42 +05:00
sessionId: Date.now(),
conflicted: false
});
2021-12-31 11:05:42 +05:00
2022-06-30 09:45:28 +05:00
if (copy) {
2020-03-26 15:42:37 +05:00
await db.notes.add({
2022-06-30 09:45:28 +05:00
title: note.title + ' (Copy)',
2020-03-26 15:42:37 +05:00
content: {
2022-06-30 09:45:28 +05:00
data: copy.data,
type: copy.type
}
2020-03-26 15:42:37 +05:00
});
}
2022-04-24 05:59:14 +05:00
Navigation.queueRoutesForUpdate(
'Notes',
'Favorites',
'ColoredNotes',
'TaggedNotes',
'TopicNotes'
);
if (editorController.current?.note?.id === note.id) {
2022-07-09 17:08:08 +05:00
// reload the note in editor
eSendEvent(eOnLoadNote, { ...editorController.current?.note, forced: true });
}
2020-03-26 15:42:37 +05:00
close();
await Sync.run();
2020-03-26 15:42:37 +05:00
};
2021-07-08 09:34:21 +05:00
const show = async item => {
2022-06-30 09:45:28 +05:00
let noteContent = await db.content.raw(item.contentId);
2022-07-07 19:09:47 +05:00
content.current = { ...noteContent };
if (!noteContent.conflicted) {
content.current.conflicted = { ...noteContent };
2021-07-08 09:34:21 +05:00
}
2020-04-11 11:11:42 +05:00
setVisible(true);
2020-03-26 13:39:04 +05:00
};
useEffect(() => {
2020-03-26 15:42:37 +05:00
eSubscribeEvent(eShowMergeDialog, show);
2020-03-26 13:39:04 +05:00
return () => {
2020-03-26 15:42:37 +05:00
eUnSubscribeEvent(eShowMergeDialog, show);
2020-03-26 13:39:04 +05:00
};
2020-09-09 11:10:35 +05:00
}, []);
2020-03-26 13:39:04 +05:00
2020-03-26 15:42:37 +05:00
const close = () => {
setVisible(false);
2022-06-30 09:45:28 +05:00
setCopy(null);
setKeep(null);
2020-12-27 13:51:09 +05:00
setDialogVisible(false);
};
2022-06-30 09:45:28 +05:00
const ConfigBar = ({ isDiscarded, keeping, back, isCurrent, contentToKeep }) => {
return (
<View
style={{
width: '100%',
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 12,
paddingLeft: 6
}}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 1
}}
>
{back && <IconButton onPress={close} color={colors.pri} name="arrow-left" />}
<Paragraph style={{ flexWrap: 'wrap' }} color={colors.icon} size={SIZE.xs}>
<Text style={{ color: isCurrent ? colors.accent : colors.red, fontWeight: 'bold' }}>
{isCurrent ? '(This Device)' : '(Incoming)'}
</Text>
{'\n'}
{timeConverter(contentToKeep.dateEdited)}
</Paragraph>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end'
}}
>
{isDiscarded ? (
<Button
onPress={() => {
setCopy(contentToKeep);
setDialogVisible(true);
}}
title="Save a copy"
type="grayBg"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
fontSize={SIZE.xs}
/>
) : null}
<View style={{ width: 10 }} />
{isDiscarded ? (
<Button
title="Discard"
type="accent"
accentColor="red"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
fontSize={SIZE.xs}
accentText="light"
color={colors.errorText}
onPress={() => {
setDialogVisible(true);
}}
/>
) : null}
{isDiscarded ? null : (
<>
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60,
marginLeft: 10
}}
type="accent"
fontSize={SIZE.xs}
title={keeping && !isDiscarded ? 'Undo' : 'Keep'}
onPress={() => {
setKeep(keeping && !isDiscarded ? null : contentToKeep);
}}
/>
</>
)}
</View>
</View>
);
2020-03-26 15:42:37 +05:00
};
2020-11-23 12:32:33 +05:00
return !visible ? null : (
2020-11-29 12:21:36 +05:00
<Modal
statusBarTranslucent
transparent={false}
animationType="slide"
2021-07-08 09:34:21 +05:00
onRequestClose={() => {
close();
}}
2022-01-12 21:41:44 +05:00
supportedOrientations={[
'portrait',
'portrait-upside-down',
'landscape',
'landscape-left',
'landscape-right'
]}
2022-01-22 12:57:05 +05:00
visible={true}
>
2020-09-09 11:10:35 +05:00
<SafeAreaView
2020-03-26 13:39:04 +05:00
style={{
2021-01-20 10:54:06 +05:00
backgroundColor: colors.bg,
paddingTop: insets.top
2022-01-22 12:57:05 +05:00
}}
>
2021-01-03 12:40:59 +05:00
<KeepAwake />
{dialogVisible && (
<BaseDialog visible={true}>
<DialogContainer>
<DialogHeader
title="Apply Changes"
paragraph="Apply selected changes to note?"
padding={12}
/>
2021-12-31 11:05:42 +05:00
<Seperator />
<DialogButtons
positiveTitle="Apply"
negativeTitle="Cancel"
onPressNegative={() => setDialogVisible(false)}
onPressPositive={applyChanges}
/>
</DialogContainer>
</BaseDialog>
)}
2020-03-26 13:39:04 +05:00
<View
style={{
2020-09-09 11:10:35 +05:00
height: '100%',
2020-03-26 13:39:04 +05:00
width: '100%',
backgroundColor: DDS.isLargeTablet() ? 'rgba(0,0,0,0.3)' : null
2022-01-22 12:57:05 +05:00
}}
>
2022-06-30 09:45:28 +05:00
<ConfigBar
back={true}
isCurrent={true}
isDiscarded={isKeeping && isKeepingConflicted}
keeping={isKeeping}
contentToKeep={content.current}
/>
2020-03-26 13:39:04 +05:00
2020-09-09 11:10:35 +05:00
<Animated.View
2020-03-26 13:39:04 +05:00
style={{
height: dHeight / 2 - (50 + insets.top / 2),
2021-02-09 12:01:19 +05:00
backgroundColor: colors.bg,
borderBottomWidth: 1,
borderBottomColor: colors.nav
2022-01-22 12:57:05 +05:00
}}
>
2022-06-30 09:45:28 +05:00
<Editor
noHeader
noToolbar
readonly
editorId=":conflictPrimary"
onLoad={() => {
const note = db.notes.note(content.current?.noteId)?.data;
if (!note) return;
eSendEvent(eOnLoadNote + ':conflictPrimary', {
...note,
content: {
...content.current,
isPreview: true
}
});
2021-07-08 09:34:21 +05:00
}}
2020-09-09 11:10:35 +05:00
/>
</Animated.View>
2020-03-26 13:39:04 +05:00
2022-06-30 09:45:28 +05:00
<ConfigBar
back={false}
isCurrent={false}
isDiscarded={isKeeping && !isKeepingConflicted}
keeping={isKeeping}
contentToKeep={content.current.conflicted}
/>
2020-03-26 13:39:04 +05:00
2020-09-09 11:10:35 +05:00
<Animated.View
2020-03-26 13:39:04 +05:00
style={{
height: dHeight / 2 - (50 + insets.top / 2),
backgroundColor: colors.bg,
borderRadius: 10
2022-01-22 12:57:05 +05:00
}}
>
2022-06-30 09:45:28 +05:00
<Editor
noHeader
noToolbar
readonly
editorId=":conflictSecondary"
onLoad={() => {
const note = db.notes.note(content.current?.noteId)?.data;
if (!note) return;
eSendEvent(eOnLoadNote + ':conflictSecondary', {
...note,
content: { ...content.current.conflicted, isPreview: true }
2022-06-30 09:45:28 +05:00
});
2021-07-08 09:34:21 +05:00
}}
2020-09-09 11:10:35 +05:00
/>
</Animated.View>
</View>
</SafeAreaView>
2020-03-26 13:39:04 +05:00
</Modal>
);
};
2022-02-28 15:32:55 +05:00
export default MergeConflicts;