import React, {createRef, useEffect, useState} from 'react'; import {Modal, SafeAreaView, TouchableOpacity, View, Text} from 'react-native'; import Animated, {Easing} from 'react-native-reanimated'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import WebView from 'react-native-webview'; import {useTracked} from '../../provider'; import {Actions} from '../../provider/Actions'; import {DDS} from '../../services/DeviceDetection'; import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent, sendNoteEditedEvent, } from '../../services/EventManager'; import {dHeight} from '../../utils'; import {db} from '../../utils/DB'; import { eApplyChanges, eShowMergeDialog, refreshNotesPage, } from '../../utils/Events'; import {normalize, SIZE} from '../../utils/SizeUtils'; import {Button} from '../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 {updateEvent} from '../DialogManager/recievers'; import Paragraph from '../Typography/Paragraph'; import KeepAwake from '@sayem314/react-native-keep-awake'; import {timeConverter} from '../../utils/TimeUtils'; import tiny from '../../views/Editor/tiny/tiny'; import diff from '../../utils/differ'; import {openLinkInBrowser} from '../../utils/functions'; const {Value, timing} = Animated; const firstWebViewHeight = new Value(dHeight * 0.5 - 50); const secondWebViewHeight = new Value(dHeight * 0.5 - 50); const primaryWebView = createRef(); const secondaryWebView = createRef(); let note = null; function openEditorAnimation( heightToAnimate, extendedHeight = null, siblingStatus, insets, ) { let openConfig = { duration: 300, toValue: !siblingStatus ? dHeight - (100 + insets.top) : dHeight * 0.5 - (50 + insets.top / 2), easing: Easing.inOut(Easing.ease), }; let extendConfig = { duration: 300, toValue: dHeight * 0.5 - (50 + insets.top / 2), easing: Easing.inOut(Easing.ease), }; if (extendedHeight) { timing(extendedHeight, extendConfig).start(); } timing(heightToAnimate, openConfig).start(); } function closeEditorAnimation(heightToAnimate, heightToExtend = null, insets) { let closeConfig = { duration: 300, toValue: 0, easing: Easing.inOut(Easing.ease), }; let extendConfig = { duration: 300, toValue: dHeight - (100 + insets.top), easing: Easing.inOut(Easing.ease), }; if (heightToExtend) { timing(heightToExtend, extendConfig).start(); } timing(heightToAnimate, closeConfig).start(); } let primaryData = null; let secondaryData = null; const MergeEditor = () => { const [state, dispatch] = useTracked(); const {colors} = state; const [visible, setVisible] = useState(false); 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 insets = useSafeAreaInsets(); const onPrimaryWebViewLoad = () => { console.log(primaryData.data.length, secondaryData.data.length); let htmlDiff = { before: primaryData.data, }; if (secondaryData.data) { htmlDiff = diff.diff_dual_pane(primaryData.data, secondaryData.data); } postMessage(primaryWebView, 'html', htmlDiff.before); let theme = {...colors}; theme.factor = normalize(1); tiny.call(primaryWebView, tiny.updateTheme(JSON.stringify(theme)), true); }; const onSecondaryWebViewLoad = () => { let htmlDiff = { before: primaryData.data, }; if (secondaryData.data) { htmlDiff = diff.diff_dual_pane(primaryData.data, secondaryData.data); } postMessage(secondaryWebView, 'html', htmlDiff.after); let theme = {...colors}; theme.factor = normalize(1); tiny.call(secondaryWebView, tiny.updateTheme(JSON.stringify(theme)), true); }; function postMessage(webview, type, value = null) { let message = { type, value, }; console.log(message,webview.current); webview.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; } }; const onMessageFromPrimaryWebView = (evt) => { if (evt.nativeEvent.data !== '') { let data = JSON.parse(evt.nativeEvent.data); if (data.type === 'tiny') { primaryData = { type: 'tiny', data: data.value, }; } } }; const onMessageFromSecondaryWebView = (evt) => { if (evt.nativeEvent.data === '') { let data = JSON.parse(evt.nativeEvent.data); if (data.type === 'tiny') { secondaryData = { type: 'tiny', data: data.value, }; } } }; const applyChanges = async () => { if (keepContentFrom === 'primary') { await db.notes.add({ content: { data: primaryData.data ? diff.clean(primaryData.data) : primaryData.data, resolved: true, type: primaryData.type, }, id: note.id, conflicted: false, }); } else if (keepContentFrom === 'secondary') { await db.notes.add({ content: { data: secondaryData.data ? diff.clean(secondaryData.data) : secondaryData.data, type: secondaryData.type, resolved: true, }, id: note.id, conflicted: false, }); } if (copyToSave === 'primary') { await db.notes.add({ content: { data: primaryData.data ? diff.clean(primaryData.data) : primaryData.data, type: primaryData.type, }, id: null, }); } else if (copyToSave === 'secondary') { await db.notes.add({ content: { data: secondaryData.data ? diff.clean(secondaryData.data) : secondaryData.data, type: secondaryData.type, }, id: null, }); } eSendEvent(refreshNotesPage); sendNoteEditedEvent({ id: note.id, forced: true, }); updateEvent({type: Actions.NOTES}); updateEvent({type: Actions.FAVORITES}); close(); }; const show = async (item) => { note = item; let noteData = await db.content.raw(note.contentId); switch (noteData.type) { case 'tiny': primaryData = noteData; secondaryData = noteData.conflicted; } setVisible(true); firstWebViewHeight.setValue(dHeight / 2 - (50 + insets.top / 2)); secondWebViewHeight.setValue(dHeight / 2 - (50 + insets.top / 2)); openEditorAnimation(firstWebViewHeight, secondWebViewHeight, true, insets); }; useEffect(() => { eSubscribeEvent(eApplyChanges, applyChanges); eSubscribeEvent(eShowMergeDialog, show); return () => { eUnSubscribeEvent(eApplyChanges, applyChanges); eUnSubscribeEvent(eShowMergeDialog, show); }; }, []); const onPressKeepFromPrimaryWebView = () => { if (keepContentFrom == 'primary') { setKeepContentFrom(null); openEditorAnimation( firstWebViewHeight, secondWebViewHeight, false, insets, ); } else { setKeepContentFrom('primary'); closeEditorAnimation(firstWebViewHeight, secondWebViewHeight, insets); } }; const onPressSaveCopyFromPrimaryWebView = () => { setCopyToSave('primary'); setDialogVisible(true); }; const onPressKeepFromSecondaryWebView = () => { if (keepContentFrom == 'secondary') { setKeepContentFrom(null); openEditorAnimation( secondWebViewHeight, firstWebViewHeight, false, insets, ); } else { setKeepContentFrom('secondary'); closeEditorAnimation(secondWebViewHeight, firstWebViewHeight, insets); } }; const onPressSaveCopyFromSecondaryWebView = () => { setCopyToSave('secondary'); setDialogVisible(true); }; const onPressDiscardFromPrimaryWebView = () => { setDiscardedContent('primary'); setDialogVisible(true); }; const onPressDiscardFromSecondaryWebView = () => { setDiscardedContent('secondary'); setDialogVisible(true); }; const close = () => { setVisible(false); setPrimary(true); setSecondary(true); setCopyToSave(null); setDiscardedContent(null); setKeepContentFrom(null); setDialogVisible(false); primaryData = null; secondaryData = null; primaryText = null; secondaryText = null; note = null; openEditorAnimation(firstWebViewHeight, secondWebViewHeight, true, insets); }; 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 = './site/plaineditor.html?${params}'; link.click(); }`; return !visible ? null : ( { close() }} visible={true}> {dialogVisible && ( setDialogVisible(false)} onPressPositive={applyChanges} /> )} { if (keepContentFrom === 'primary') return; if (!primary) { openEditorAnimation( firstWebViewHeight, secondary && keepContentFrom !== 'secondary' ? secondWebViewHeight : null, secondary && keepContentFrom !== 'secondary', insets, ); setPrimary(true); } else { closeEditorAnimation( firstWebViewHeight, secondary && keepContentFrom !== 'secondary' ? secondWebViewHeight : null, insets, ); setPrimary(false); } }} style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }}> (This Device) {' '} Saved on {timeConverter(primaryData.dateEdited)} {keepContentFrom === 'secondary' ? (