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

577 lines
18 KiB
JavaScript
Raw Normal View History

2021-07-08 09:34:21 +05:00
import KeepAwake from '@sayem314/react-native-keep-awake';
2022-01-22 12:57:05 +05:00
import { EV, EVENTS } from 'notes-core/common';
import React, { createRef, useEffect, useState } from 'react';
import { Modal, Platform, 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';
2020-03-26 13:39:04 +05:00
import WebView from 'react-native-webview';
2022-02-28 23:25:18 +05:00
import { useThemeStore } from '../../stores/theme';
import { DDS } from '../../services/device-detection';
2020-03-26 15:50:10 +05:00
import {
2020-09-09 11:10:35 +05:00
eSendEvent,
2020-03-26 15:50:10 +05:00
eSubscribeEvent,
2022-01-22 12:57:05 +05:00
eUnSubscribeEvent,
ToastEvent
2022-02-28 23:25:18 +05:00
} from '../../services/event-manager';
import Navigation from '../../services/navigation';
import Sync from '../../services/sync';
2022-01-22 12:57:05 +05:00
import { dHeight } from '../../utils';
import { db } from '../../utils/database';
2022-02-28 13:48:59 +05:00
import { eApplyChanges, eShowMergeDialog, refreshNotesPage } from '../../utils/events';
2022-01-22 12:57:05 +05:00
import { openLinkInBrowser } from '../../utils/functions';
2022-02-28 13:48:59 +05:00
import { normalize, SIZE } from '../../utils/size';
import { timeConverter } from '../../utils/time';
2022-02-28 15:32:55 +05:00
import { getNote, sourceUri, updateNoteInEditor } from '../../screens/editor/Functions';
2022-02-28 13:48:59 +05:00
import { IconButton } from '../ui/icon-button';
import { Button } from '../ui/button';
import BaseDialog from '../dialog/base-dialog';
import DialogButtons from '../dialog/dialog-buttons';
import DialogContainer from '../dialog/dialog-container';
import DialogHeader from '../dialog/dialog-header';
import Seperator from '../ui/seperator';
import Paragraph from '../ui/typography/paragraph';
2020-03-26 13:39:04 +05:00
const primaryWebView = createRef();
const secondaryWebView = createRef();
2020-03-26 15:42:37 +05:00
let note = null;
2020-11-29 12:21:36 +05:00
let primaryData = null;
let secondaryData = null;
2021-02-09 12:01:19 +05:00
2022-01-22 12:57:05 +05:00
function onMediaLoaded({ hash, src }) {
2021-10-18 15:01:22 +05:00
console.log('on media download complete');
let inject = `
(function(){
const elements = document.querySelectorAll("img[data-hash=${hash}]");
if (!elements || !elements.length) return;
for (let element of elements) element.setAttribute("src", "${src}");
})();`;
primaryWebView.current?.injectJavaScript(inject);
secondaryWebView.current?.injectJavaScript(inject);
}
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);
2020-03-26 13:39:04 +05:00
const [primary, setPrimary] = useState(true);
const [secondary, setSecondary] = useState(true);
const [keepContentFrom, setKeepContentFrom] = useState(null);
const [copyToSave, setCopyToSave] = useState(null);
const [disardedContent, setDiscardedContent] = useState(null);
const [dialogVisible, setDialogVisible] = useState(false);
const [loadingAttachments, setLoadingAttachments] = useState(false);
2020-11-29 12:21:36 +05:00
const insets = useSafeAreaInsets();
2020-03-26 13:39:04 +05:00
const onPrimaryWebViewLoad = async () => {
2022-01-22 12:57:05 +05:00
let content = await db.content.insertPlaceholders(primaryData, 'placeholder.svg');
postMessage(primaryWebView, 'htmldiff', content.data);
2022-01-22 12:57:05 +05:00
let theme = { ...colors };
2021-02-09 12:01:19 +05:00
theme.factor = normalize(1);
primaryWebView.current?.injectJavaScript(`
(function() {
let v = ${JSON.stringify(theme)}
if (pageTheme) {
pageTheme.colors = v;
}
setTheme();
})();
`);
2020-03-26 13:39:04 +05:00
};
const onSecondaryWebViewLoad = async () => {
if (!secondaryData) return;
2022-01-22 12:57:05 +05:00
let content = await db.content.insertPlaceholders(secondaryData, 'placeholder.svg');
postMessage(secondaryWebView, 'htmldiff', content?.data);
2022-01-22 12:57:05 +05:00
let theme = { ...colors };
2021-02-09 12:01:19 +05:00
theme.factor = normalize(1);
secondaryWebView.current?.injectJavaScript(`
(function() {
let v = ${JSON.stringify(theme)}
if (pageTheme) {
pageTheme.colors = v;
}
setTheme();
})();
`);
2020-03-26 13:39:04 +05:00
};
function postMessage(webview, type, value = null) {
let message = {
2021-07-08 09:34:21 +05:00
type: type,
value
};
webview.current?.postMessage(JSON.stringify(message));
}
2021-07-08 09:34:21 +05:00
const _onShouldStartLoadWithRequest = request => {
2021-02-10 10:09:48 +05:00
if (request.url.includes('http')) {
openLinkInBrowser(request.url, colors)
2021-07-08 09:34:21 +05:00
.catch(e =>
2021-02-20 15:03:02 +05:00
ToastEvent.show({
title: 'Failed to open link',
message: e.message,
type: 'success',
context: 'local'
})
2021-02-20 15:03:02 +05:00
)
2021-07-08 09:34:21 +05:00
.then(r => {
2021-02-20 15:03:02 +05:00
console.log('closed');
});
2021-02-10 10:09:48 +05:00
2020-03-26 13:39:04 +05:00
return false;
} else {
return true;
}
};
2020-03-26 15:42:37 +05:00
const applyChanges = async () => {
let content = keepContentFrom === 'primary' ? primaryData : secondaryData;
let keepCopy =
2022-01-22 12:57:05 +05:00
copyToSave === 'primary' ? primaryData : copyToSave === 'secondary' ? secondaryData : null;
await db.notes.add({
id: note.id,
2021-12-31 11:05:42 +05:00
conflicted: false,
dateEdited: content.dateEdited
});
await db.content.add({
id: note.contentId,
data: content.data,
type: content.type,
dateResolved: secondaryData.dateModified,
sessionId: Date.now(),
conflicted: false
});
2021-12-31 11:05:42 +05:00
if (keepCopy) {
2020-03-26 15:42:37 +05:00
await db.notes.add({
content: {
data: keepCopy.data,
type: keepCopy.type
2020-03-26 15:42:37 +05:00
},
id: null
2020-03-26 15:42:37 +05:00
});
}
2020-03-26 15:50:10 +05:00
eSendEvent(refreshNotesPage);
2021-06-02 17:34:39 +05:00
Navigation.setRoutesToUpdate([
Navigation.routeNames.NotesPage,
Navigation.routeNames.Favorites,
Navigation.routeNames.Notes
2021-06-02 17:34:39 +05:00
]);
if (getNote()?.id === note.id) {
updateNoteInEditor();
}
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 => {
2020-03-26 15:42:37 +05:00
note = item;
let content = await db.content.raw(note.contentId);
2021-10-18 15:01:22 +05:00
switch (content.type) {
2021-07-08 09:34:21 +05:00
case 'tiny':
primaryData = content;
secondaryData = content.conflicted;
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(() => {
eSubscribeEvent(eApplyChanges, applyChanges);
2020-03-26 15:42:37 +05:00
eSubscribeEvent(eShowMergeDialog, show);
2020-03-26 13:39:04 +05:00
return () => {
eUnSubscribeEvent(eApplyChanges, applyChanges);
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
const onPressKeepFromPrimaryWebView = () => {
if (keepContentFrom == 'primary') {
setKeepContentFrom(null);
} else {
setKeepContentFrom('primary');
}
};
const onPressSaveCopyFromPrimaryWebView = () => {
setCopyToSave('primary');
setDialogVisible(true);
2020-03-26 13:39:04 +05:00
};
const onPressKeepFromSecondaryWebView = () => {
if (keepContentFrom == 'secondary') {
setKeepContentFrom(null);
} else {
setKeepContentFrom('secondary');
}
};
const onPressSaveCopyFromSecondaryWebView = () => {
setCopyToSave('secondary');
setDialogVisible(true);
2020-03-26 13:39:04 +05:00
};
const onPressDiscardFromPrimaryWebView = () => {
setDiscardedContent('primary');
setDialogVisible(true);
2020-03-26 13:39:04 +05:00
};
const onPressDiscardFromSecondaryWebView = () => {
setDiscardedContent('secondary');
setDialogVisible(true);
2020-03-26 13:39:04 +05:00
};
2020-03-26 15:42:37 +05:00
const close = () => {
2021-10-18 15:01:22 +05:00
db.fs.cancel(primaryData?.noteId);
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
2020-03-26 15:42:37 +05:00
setVisible(false);
setPrimary(true);
setSecondary(true);
setCopyToSave(null);
setDiscardedContent(null);
setKeepContentFrom(null);
2020-12-27 13:51:09 +05:00
setDialogVisible(false);
2020-11-29 12:21:36 +05:00
primaryData = null;
secondaryData = null;
2020-03-26 15:42:37 +05:00
note = null;
};
const onLoadImages = async () => {
try {
setLoadingAttachments(true);
2021-10-18 15:01:22 +05:00
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
await db.content.downloadMedia(primaryData.data.noteId, primaryData);
await db.content.downloadMedia(primaryData.data.noteId, secondaryData);
2021-10-18 15:01:22 +05:00
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
setLoadingAttachments(false);
} catch (e) {
setLoadingAttachments(false);
eUnSubscribeEvent(EVENTS.mediaAttachmentDownloaded, onMediaLoaded);
}
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
}}
>
2020-03-26 13:39:04 +05:00
<View
style={{
2020-09-09 11:10:35 +05:00
width: '100%',
height: 50,
2020-03-26 13:39:04 +05:00
flexDirection: 'row',
justifyContent: 'space-between',
2020-09-09 11:10:35 +05:00
alignItems: 'center',
paddingHorizontal: 12,
paddingLeft: 6
2022-01-22 12:57:05 +05:00
}}
>
2020-09-09 11:10:35 +05:00
<View
2020-03-26 13:39:04 +05:00
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 1
2022-01-22 12:57:05 +05:00
}}
>
2022-02-28 13:48:59 +05:00
<IconButton onPress={close} color={colors.pri} name="arrow-left" />
2022-01-22 12:57:05 +05:00
<Paragraph style={{ flexWrap: 'wrap' }} color={colors.icon} size={SIZE.xs}>
<Text style={{ color: colors.accent, fontWeight: 'bold' }}>(This Device)</Text>
{'\n'}
{timeConverter(primaryData?.dateEdited)}
</Paragraph>
2020-09-09 11:10:35 +05:00
</View>
2020-03-26 13:39:04 +05:00
2020-09-09 11:10:35 +05:00
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end'
2022-01-22 12:57:05 +05:00
}}
>
2020-09-09 11:10:35 +05:00
{keepContentFrom === 'secondary' ? (
<Button
onPress={onPressSaveCopyFromPrimaryWebView}
title="Save a copy"
type="grayBg"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
2021-12-16 10:20:34 +05:00
fontSize={SIZE.xs}
2020-09-09 11:10:35 +05:00
/>
) : null}
2022-01-22 12:57:05 +05:00
<View style={{ width: 10 }} />
2020-09-09 11:10:35 +05:00
{keepContentFrom === 'secondary' ? (
<Button
title="Discard"
2021-01-05 10:20:16 +05:00
type="accent"
accentColor="red"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12
}}
2021-12-16 10:20:34 +05:00
fontSize={SIZE.xs}
2021-01-05 10:20:16 +05:00
accentText="light"
2020-09-09 11:10:35 +05:00
color={colors.errorText}
onPress={onPressDiscardFromPrimaryWebView}
/>
) : null}
{keepContentFrom === 'secondary' ? null : (
2021-01-13 16:32:35 +05:00
<>
<Button
type="grayBg"
title="Load images"
onPress={onLoadImages}
height={30}
loading={loadingAttachments}
fontSize={SIZE.xs}
icon="download"
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
/>
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60,
marginLeft: 10
}}
type="accent"
2021-12-16 10:20:34 +05:00
fontSize={SIZE.xs}
2021-01-13 16:32:35 +05:00
title={keepContentFrom === 'primary' ? 'Undo' : 'Keep'}
onPress={onPressKeepFromPrimaryWebView}
/>
</>
2020-09-09 11:10:35 +05:00
)}
</View>
2020-03-26 13:39:04 +05:00
</View>
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
}}
>
2020-09-09 11:10:35 +05:00
<WebView
onLoad={onPrimaryWebViewLoad}
ref={primaryWebView}
style={{
width: '100%',
height: '100%',
backgroundColor: 'transparent'
2020-09-09 11:10:35 +05:00
}}
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
domStorageEnabled={true}
2021-05-20 11:05:03 +05:00
scrollEnabled={true}
2020-09-09 11:10:35 +05:00
bounces={false}
allowFileAccess={true}
scalesPageToFit={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
javaScriptEnabled={true}
cacheEnabled={true}
2021-07-08 09:34:21 +05:00
source={{
uri: sourceUri + 'plaineditor.html'
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
<View
style={{
2020-09-09 11:10:35 +05:00
width: '100%',
height: 50,
2020-03-26 13:39:04 +05:00
flexDirection: 'row',
justifyContent: 'space-between',
2020-09-09 11:10:35 +05:00
alignItems: 'center',
paddingHorizontal: 12
2022-01-22 12:57:05 +05:00
}}
>
2020-09-09 11:10:35 +05:00
<View
2020-03-26 13:39:04 +05:00
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flexShrink: 1
2022-01-22 12:57:05 +05:00
}}
>
<Paragraph style={{ flexWrap: 'wrap' }} color={colors.icon} size={SIZE.xs}>
<Text style={{ color: 'red', fontWeight: 'bold' }}>(Incoming)</Text>
{'\n'}
{timeConverter(secondaryData?.dateEdited)}
</Paragraph>
2020-09-09 11:10:35 +05:00
</View>
2020-03-26 13:39:04 +05:00
2020-09-09 11:10:35 +05:00
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end'
2022-01-22 12:57:05 +05:00
}}
>
2020-09-09 11:10:35 +05:00
{keepContentFrom === 'primary' ? (
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
type="accent"
2021-12-16 10:20:34 +05:00
fontSize={SIZE.xs}
2020-09-09 11:10:35 +05:00
onPress={onPressSaveCopyFromSecondaryWebView}
title="Save a copy"
2020-09-09 11:10:35 +05:00
/>
) : null}
2022-01-22 12:57:05 +05:00
<View style={{ width: 10 }} />
2020-09-09 11:10:35 +05:00
{keepContentFrom === 'primary' ? (
<Button
title="Discard"
2021-01-05 10:20:16 +05:00
type="accent"
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
2021-12-16 10:20:34 +05:00
fontSize={SIZE.xs}
2021-01-05 10:20:16 +05:00
accentColor="red"
accentText="light"
2020-09-09 11:10:35 +05:00
onPress={onPressDiscardFromSecondaryWebView}
/>
) : null}
2021-01-13 16:32:35 +05:00
2020-09-09 11:10:35 +05:00
{keepContentFrom === 'primary' ? null : (
2021-01-13 16:32:35 +05:00
<>
<Button
type="grayBg"
title="Load images"
height={30}
loading={loadingAttachments}
fontSize={SIZE.xs}
icon="download"
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60
}}
/>
<Button
height={30}
style={{
borderRadius: 100,
paddingHorizontal: 12,
minWidth: 60,
marginLeft: 10
}}
type="accent"
2021-12-16 10:20:34 +05:00
fontSize={SIZE.xs}
2021-01-13 16:32:35 +05:00
title={keepContentFrom === 'secondary' ? 'Undo' : 'Keep'}
onPress={onPressKeepFromSecondaryWebView}
/>
</>
2020-09-09 11:10:35 +05:00
)}
</View>
2020-03-26 13:39:04 +05:00
</View>
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
}}
>
2020-09-09 11:10:35 +05:00
<WebView
onLoad={onSecondaryWebViewLoad}
ref={secondaryWebView}
style={{
width: '100%',
height: '100%',
backgroundColor: 'transparent'
2020-09-09 11:10:35 +05:00
}}
onShouldStartLoadWithRequest={_onShouldStartLoadWithRequest}
cacheMode="LOAD_DEFAULT"
domStorageEnabled={true}
2021-05-20 11:05:03 +05:00
scrollEnabled={true}
2020-09-09 11:10:35 +05:00
bounces={false}
allowFileAccess={true}
scalesPageToFit={true}
allowingReadAccessToURL={Platform.OS === 'android' ? true : null}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
originWhitelist={['*']}
javaScriptEnabled={true}
cacheEnabled={true}
2021-07-08 09:34:21 +05:00
source={{
uri: sourceUri + 'plaineditor.html'
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;