diff --git a/apps/mobile/android/app/src/main/assets/plaineditor.html b/apps/mobile/android/app/src/main/assets/plaineditor.html
new file mode 100644
index 000000000..01e4424bd
--- /dev/null
+++ b/apps/mobile/android/app/src/main/assets/plaineditor.html
@@ -0,0 +1,15203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/mobile/html/Web.bundle/site/plaineditor.html b/apps/mobile/html/Web.bundle/site/plaineditor.html
new file mode 100644
index 000000000..211c2834b
--- /dev/null
+++ b/apps/mobile/html/Web.bundle/site/plaineditor.html
@@ -0,0 +1,15204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/mobile/src/components/Dialog/index.js b/apps/mobile/src/components/Dialog/index.js
index 817eb14fc..3a0dc2cad 100644
--- a/apps/mobile/src/components/Dialog/index.js
+++ b/apps/mobile/src/components/Dialog/index.js
@@ -8,6 +8,7 @@ import {
eClearEditor,
eCloseFullscreenEditor,
eOnNewTopicAdded,
+ eApplyChanges,
} from '../../services/events';
import NavigationService from '../../services/NavigationService';
import {db, DDS, getElevation, history, ToastEvent} from '../../utils/utils';
@@ -118,6 +119,11 @@ export class Dialog extends Component {
this.hide();
break;
}
+ case dialogActions.ACTION_APPLY_CHANGES: {
+ eSendEvent(eApplyChanges);
+ this.hide();
+ break;
+ }
}
};
diff --git a/apps/mobile/src/components/DialogManager/dialogActions.js b/apps/mobile/src/components/DialogManager/dialogActions.js
index 1e921a3e5..b02bbd054 100644
--- a/apps/mobile/src/components/DialogManager/dialogActions.js
+++ b/apps/mobile/src/components/DialogManager/dialogActions.js
@@ -5,4 +5,5 @@ export const dialogActions = {
ACTION_EXIT_FULLSCREEN: 514,
ACTION_TRASH: 515,
ACTION_PERMANANT_DELETE: 516,
+ ACTION_APPLY_CHANGES: 517,
};
diff --git a/apps/mobile/src/components/DialogManager/index.js b/apps/mobile/src/components/DialogManager/index.js
index 71ee6bc5a..0ed7e1e7f 100644
--- a/apps/mobile/src/components/DialogManager/index.js
+++ b/apps/mobile/src/components/DialogManager/index.js
@@ -31,6 +31,7 @@ import {hexToRGBA, DDS} from '../../utils/utils';
import {Platform} from 'react-native';
import {TEMPLATE_DELETE, TEMPLATE_PERMANANT_DELETE} from './templates';
import {moveNoteEvent} from './recievers';
+import MergeEditor from '../MergeEditor';
export class DialogManager extends Component {
constructor(props) {
@@ -343,6 +344,8 @@ export class DialogManager extends Component {
/>
(this.loginDialog = ref)} />
+
+
>
);
}
diff --git a/apps/mobile/src/components/DialogManager/templates.js b/apps/mobile/src/components/DialogManager/templates.js
index c18b35e13..7e1f696a2 100644
--- a/apps/mobile/src/components/DialogManager/templates.js
+++ b/apps/mobile/src/components/DialogManager/templates.js
@@ -32,6 +32,15 @@ export const TEMPLATE_TRASH = type => {
};
};
+export const TEMPLATE_APPLY_CHANGES = {
+ title: `Apply Changes`,
+ paragraph: `Apply selected changes to note?`,
+ positiveText: 'Apply',
+ negativeText: 'Cancel',
+ action: dialogActions.ACTION_APPLY_CHANGES,
+ icon: 'check',
+};
+
export const TEMPLATE_EXIT_FULLSCREEN = () => {
return {
title: `Exit fullscreen editor?`,
diff --git a/apps/mobile/src/components/MergeEditor/index.js b/apps/mobile/src/components/MergeEditor/index.js
new file mode 100644
index 000000000..83e71c331
--- /dev/null
+++ b/apps/mobile/src/components/MergeEditor/index.js
@@ -0,0 +1,592 @@
+import React, {useState, createRef, useEffect} from 'react';
+import {Modal, View, TouchableOpacity, Text} from 'react-native';
+import WebView from 'react-native-webview';
+import {h, getElevation} from '../../utils/utils';
+import {useTracked} from '../../provider';
+import {SIZE, WEIGHT, normalize} from '../../common/common';
+import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
+import Animated, {Easing} from 'react-native-reanimated';
+import {
+ eSubscribeEvent,
+ eUnSubscribeEvent,
+ eSendEvent,
+} from '../../services/eventManager';
+import {eApplyChanges, eOpenSimpleDialog} from '../../services/events';
+import {TEMPLATE_APPLY_CHANGES} from '../DialogManager/templates';
+import {simpleDialogEvent} from '../DialogManager/recievers';
+
+const {Value, timing} = Animated;
+const firstWebViewHeight = new Value(h * 0.5 - 50);
+const secondWebViewHeight = new Value(h * 0.5 - 50);
+
+const primaryWebView = createRef();
+const secondaryWebView = createRef();
+
+export function openEditorAnimation(
+ heightToAnimate,
+ extendedHeight = null,
+ siblingStatus,
+) {
+ let openConfig = {
+ duration: 300,
+ toValue: !siblingStatus ? h - 100 : h * 0.5 - 50,
+ easing: Easing.inOut(Easing.ease),
+ };
+
+ let extendConfig = {
+ duration: 300,
+ toValue: h * 0.5 - 50,
+ easing: Easing.inOut(Easing.ease),
+ };
+
+ if (extendedHeight) {
+ timing(extendedHeight, extendConfig).start();
+ }
+ timing(heightToAnimate, openConfig).start();
+}
+
+export function closeEditorAnimation(heightToAnimate, heightToExtend = null) {
+ let closeConfig = {
+ duration: 300,
+ toValue: 0,
+ easing: Easing.inOut(Easing.ease),
+ };
+
+ let extendConfig = {
+ duration: 300,
+ toValue: h - 100,
+ easing: Easing.inOut(Easing.ease),
+ };
+ if (heightToExtend) {
+ timing(heightToExtend, extendConfig).start();
+ }
+
+ timing(heightToAnimate, closeConfig).start();
+}
+
+let primaryDelta = null;
+let primaryText = 'Hello world, I am primary text conflict';
+
+let secondaryDelta = null;
+let secondaryText = 'Hello world, I am secondary text confilict';
+
+const MergeEditor = () => {
+ const [state, dispatch] = useTracked();
+ const {colors} = state;
+ const [visible, setVisible] = useState(true);
+ 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 postMessageToPrimaryWebView = message =>
+ primaryWebView.current?.postMessage(JSON.stringify(message));
+
+ const postMessageToSecondaryWebView = message =>
+ secondaryWebView.current?.postMessage(JSON.stringify(message));
+
+ const onPrimaryWebViewLoad = () => {
+ postMessageToPrimaryWebView({
+ type: 'text',
+ value: primaryText,
+ });
+ let c = {...colors};
+ c.factor = normalize(1);
+ postMessageToPrimaryWebView({
+ type: 'theme',
+ value: c,
+ });
+ };
+
+ const onSecondaryWebViewLoad = () => {
+ postMessageToSecondaryWebView({
+ type: 'text',
+ value: secondaryText,
+ });
+ let c = {...colors};
+ c.factor = normalize(1);
+ postMessageToSecondaryWebView({
+ type: 'theme',
+ value: c,
+ });
+ };
+
+ const _onShouldStartLoadWithRequest = request => {
+ if (request.url.includes('https')) {
+ Linking.openURL(request.url);
+ return false;
+ } else {
+ return true;
+ }
+ };
+
+ const onMessageFromPrimaryWebView = evt => {
+ if (evt.nativeEvent.data === 'loaded') {
+ } else if (
+ evt.nativeEvent.data !== '' &&
+ evt.nativeEvent.data !== 'loaded'
+ ) {
+ let data = JSON.parse(evt.nativeEvent.data);
+ primaryDelta = data.delta;
+ primaryText = data.text;
+ }
+ };
+
+ const onMessageFromSecondaryWebView = evt => {
+ if (evt.nativeEvent.data === 'loaded') {
+ } else if (
+ evt.nativeEvent.data !== '' &&
+ evt.nativeEvent.data !== 'loaded'
+ ) {
+ let data = JSON.parse(evt.nativeEvent.data);
+ secondaryDelta = data.delta;
+ secondaryText = data.text;
+ //Handle Here
+ }
+ };
+
+ const applyChanges = () => {
+ alert('hello');
+ };
+
+ useEffect(() => {
+ eSubscribeEvent(eApplyChanges, applyChanges);
+
+ return () => {
+ eUnSubscribeEvent(eApplyChanges, applyChanges);
+ };
+ });
+
+ const onPressKeepFromPrimaryWebView = () => {
+ if (keepContentFrom == 'primary') {
+ setKeepContentFrom(null);
+ openEditorAnimation(firstWebViewHeight, secondWebViewHeight);
+ } else {
+ setKeepContentFrom('primary');
+ closeEditorAnimation(firstWebViewHeight, secondWebViewHeight);
+ }
+ };
+
+ const onPressSaveCopyFromPrimaryWebView = () => {
+ setCopyToSave('primary');
+ simpleDialogEvent(TEMPLATE_APPLY_CHANGES);
+ };
+
+ const onPressKeepFromSecondaryWebView = () => {
+ if (keepContentFrom == 'secondary') {
+ setKeepContentFrom(null);
+ openEditorAnimation(secondWebViewHeight, firstWebViewHeight);
+ } else {
+ setKeepContentFrom('secondary');
+ closeEditorAnimation(secondWebViewHeight, firstWebViewHeight);
+ }
+ };
+
+ const onPressSaveCopyFromSecondaryWebView = () => {
+ setCopyToSave('secondary');
+ simpleDialogEvent(TEMPLATE_APPLY_CHANGES);
+ };
+
+ const onPressDiscardFromPrimaryWebView = () => {
+ setDiscardedContent('primary');
+ simpleDialogEvent(TEMPLATE_APPLY_CHANGES);
+ };
+
+ const onPressDiscardFromSecondaryWebView = () => {
+ setDiscardedContent('secondary');
+ simpleDialogEvent(TEMPLATE_APPLY_CHANGES);
+ };
+
+ const params = 'platform=' + Platform.OS;
+ const sourceUri =
+ (Platform.OS === 'android' ? 'file:///android_asset/' : '') +
+ 'Web.bundle/loader.html';
+ const injectedJS = `if (!window.location.search) {
+ var link = document.getElementById('progress-bar');
+ link.href = './site2/plaineditor.html?${params}';
+ link.click();
+ }`;
+
+ return (
+
+
+
+
+
+ {
+ if (keepContentFrom === 'primary') return;
+ if (!primary) {
+ openEditorAnimation(
+ firstWebViewHeight,
+ secondary && keepContentFrom !== 'secondary'
+ ? secondWebViewHeight
+ : null,
+ secondary && keepContentFrom !== 'secondary',
+ );
+ setPrimary(true);
+ } else {
+ closeEditorAnimation(
+ firstWebViewHeight,
+ secondary && keepContentFrom !== 'secondary'
+ ? secondWebViewHeight
+ : null,
+ );
+ setPrimary(false);
+ }
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ }}>
+
+ Saved on 10/10/20 {'\n'}
+ 12:30pm on Tablet
+
+
+
+
+
+
+ {keepContentFrom === 'secondary' ? (
+
+
+ Save as a copy
+
+
+ ) : null}
+
+ {keepContentFrom === 'secondary' ? (
+
+
+ Discard
+
+
+ ) : null}
+
+ {keepContentFrom === 'secondary' ? null : (
+
+
+ {keepContentFrom === 'primary' ? 'Undo' : 'Keep'}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ {
+ if (keepContentFrom === 'secondary') return;
+ if (!secondary) {
+ openEditorAnimation(
+ secondWebViewHeight,
+ primary && keepContentFrom !== 'primary'
+ ? firstWebViewHeight
+ : null,
+ primary && keepContentFrom !== 'primary',
+ );
+ setSecondary(true);
+ } else {
+ closeEditorAnimation(
+ secondWebViewHeight,
+ primary && keepContentFrom !== 'primary'
+ ? firstWebViewHeight
+ : null,
+ );
+ setSecondary(false);
+ }
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ }}>
+
+ Saved on 10/10/20 {'\n'}
+ 12:30pm on Tablet
+
+
+
+
+
+
+ {keepContentFrom === 'primary' ? (
+
+
+ Save as a copy
+
+
+ ) : null}
+
+ {keepContentFrom === 'primary' ? (
+
+
+ Discard
+
+
+ ) : null}
+
+ {keepContentFrom === 'primary' ? null : (
+
+
+ {keepContentFrom === 'secondary' ? 'Undo' : 'Keep'}
+
+
+ )}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default MergeEditor;
diff --git a/apps/mobile/src/services/events.js b/apps/mobile/src/services/events.js
index 2523e1793..84327ada9 100644
--- a/apps/mobile/src/services/events.js
+++ b/apps/mobile/src/services/events.js
@@ -67,3 +67,5 @@ export const refreshNotesPage = '532';
export const eClearSearch = '533';
export const eClearEditor = '534';
+
+export const eApplyChanges = '535';
diff --git a/apps/mobile/src/views/Editor/index.js b/apps/mobile/src/views/Editor/index.js
index b95de42b1..1e2e43e83 100755
--- a/apps/mobile/src/views/Editor/index.js
+++ b/apps/mobile/src/views/Editor/index.js
@@ -348,7 +348,7 @@ const Editor = ({noMenu}) => {
c.factor = normalize(1);
post({
type: 'theme',
- value: colors,
+ value: c,
});
};
@@ -357,10 +357,6 @@ const Editor = ({noMenu}) => {
id = note.id;
setDateEdited(note.dateEdited);
content = {...note.content};
- if (!note.locked) {
- content.delta = await db.notes.note(id).delta();
- }
-
saveCounter = 0;
if (title !== null || title === '') {
@@ -383,10 +379,10 @@ const Editor = ({noMenu}) => {
post({
type: 'clearEditor',
});
- } else if (content.delta) {
+ } else if (note.content.delta) {
let delta;
- if (typeof content.delta !== 'string') {
- delta = content.delta;
+ if (typeof note.content.delta !== 'string') {
+ delta = note.content.delta;
} else {
delta = await db.notes.note(id).delta();
}