diff --git a/apps/mobile/android/app/src/main/assets/plaineditor.html b/apps/mobile/android/app/src/main/assets/plaineditor.html index 1a42a4fc2..fb1d9d434 100644 --- a/apps/mobile/android/app/src/main/assets/plaineditor.html +++ b/apps/mobile/android/app/src/main/assets/plaineditor.html @@ -6,6 +6,7 @@ Note Diff Preview + -
+
diff --git a/apps/mobile/android/app/src/main/res/drawable/widget_preview.png b/apps/mobile/android/app/src/main/res/drawable/widget_preview.png new file mode 100644 index 000000000..44c9af89a Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable/widget_preview.png differ diff --git a/apps/mobile/android/app/src/main/res/layout/note_widget.xml b/apps/mobile/android/app/src/main/res/layout/note_widget.xml index 0354870a5..70376610b 100644 --- a/apps/mobile/android/app/src/main/res/layout/note_widget.xml +++ b/apps/mobile/android/app/src/main/res/layout/note_widget.xml @@ -6,6 +6,7 @@ android:padding="@dimen/widget_margin" android:theme="@style/ThemeOverlay.Notesnook.AppWidgetContainer"> + \ 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 index 1a42a4fc2..fb1d9d434 100644 --- a/apps/mobile/html/Web.bundle/site/plaineditor.html +++ b/apps/mobile/html/Web.bundle/site/plaineditor.html @@ -6,6 +6,7 @@ Note Diff Preview + -
+
diff --git a/apps/mobile/share/index.js b/apps/mobile/share/index.js index a5b045a2a..bdd477d4b 100644 --- a/apps/mobile/share/index.js +++ b/apps/mobile/share/index.js @@ -1,6 +1,7 @@ +import Clipboard from '@react-native-clipboard/clipboard'; import absolutify from 'absolutify'; -import {getLinkPreview} from 'link-preview-js'; -import React, {useEffect, useRef, useState} from 'react'; +import { getLinkPreview } from 'link-preview-js'; +import React, { useEffect, useRef, useState } from 'react'; import { ActivityIndicator, Appearance, @@ -9,24 +10,25 @@ import { Platform, SafeAreaView, Text, - TextInput, TouchableOpacity, useWindowDimensions, View } from 'react-native'; -import Animated, {Easing, timing, useValue} from 'react-native-reanimated'; +import Animated, { Easing, timing, useValue } from 'react-native-reanimated'; +import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import WebView from 'react-native-webview'; import ShareExtension from 'rn-extensions-share'; -import sanitize from 'sanitize-html'; import validator from 'validator'; -import {getElevation, showTooltip, TOOLTIP_POSITIONS} from '../src/utils'; -import {COLOR_SCHEME_DARK, COLOR_SCHEME_LIGHT} from '../src/utils/Colors'; -import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; -import Clipboard from '@react-native-clipboard/clipboard'; -import {db} from '../src/utils/database'; -import {SIZE} from '../src/utils/SizeUtils'; +import { + eSendEvent, + eSubscribeEvent, + eUnSubscribeEvent +} from '../src/services/EventManager'; +import { getElevation } from '../src/utils'; +import { COLOR_SCHEME_DARK, COLOR_SCHEME_LIGHT } from '../src/utils/Colors'; +import { db } from '../src/utils/database'; import Storage from '../src/utils/storage'; -import {sleep} from '../src/utils/TimeUtils'; +import { sleep } from '../src/utils/TimeUtils'; const AnimatedKAV = Animated.createAnimatedComponent(KeyboardAvoidingView); const AnimatedSAV = Animated.createAnimatedComponent(SafeAreaView); @@ -34,19 +36,10 @@ async function sanitizeHtml(site) { try { let html = await fetch(site); html = await html.text(); - let siteHtml = sanitize(html, { - allowedTags: sanitize.defaults.allowedTags.concat([ - 'img', - 'style', - 'head', - 'link' - ]), - allowedClasses: true, - allowVulnerableTags: true, - allowedAttributes: false, - allowProtocolRelative: true, - allowedSchemes: false - }); + let siteHtml = html.replace( + /(?:<(script|button|input|textarea|style|link)(?:\s[^>]*)?>)\s*((?:(?!<\1)[\s\S])*)\s*(?:<\/\1>)/g, + '' + ); return absolutify(siteHtml, site); } catch (e) { return ''; @@ -62,6 +55,30 @@ function makeHtmlFromPlainText(text) { return `

${text}

`; } +function getBaseUrl(site) { + var url = site.split('/').slice(0, 3).join('/'); + return url; +} + +async function absolutifyImgs(html, site) { + let parser = global.HTMLParser; + global.HTMLParser.body.innerHTML = html; + + let images = parser.querySelectorAll('img'); + for (var i = 0; i < images.length; i++) { + let img = images[i]; + let url = getBaseUrl(site); + if (!img.src.startsWith('http')) { + if (img.src.startsWith('//')) { + img.src = img.src.replace('//', 'https://'); + } else { + img.src = url + img.src; + } + } + } + return parser.body.innerHTML; +} + let defaultNote = { title: null, id: null, @@ -71,7 +88,6 @@ let defaultNote = { } }; -let editorContentValue = null; const modes = { 1: { @@ -101,13 +117,10 @@ const NotesnookShare = () => { const [loadingIntent, setLoadingIntent] = useState(true); const [loading, setLoading] = useState(false); const [floating, setFloating] = useState(false); - const [quickNote, setQuickNote] = useState(false); const [rawData, setRawData] = useState({ type: null, value: null }); - const textInputRef = useRef(); - const titleInputRef = useRef(); const {width, height} = useWindowDimensions(); const webviewRef = useRef(); const opacity = useValue(0); @@ -117,7 +130,6 @@ const NotesnookShare = () => { }; const prevAnimation = useRef(null); const [mode, setMode] = useState(1); - const [keyboard,setKeyboard] = useState(false); const animate = (opacityV, translateV) => { prevAnimation.current = translateV; @@ -134,86 +146,79 @@ const NotesnookShare = () => { }).start(); }; - const onKeyboardDidShow = (event) => { + const onKeyboardDidShow = event => { let kHeight = event.endCoordinates.height; - console.log('called') - translate.setValue(-kHeight/1.8); - } + //translate.setValue(-150); + }; const onKeyboardDidHide = () => { translate.setValue(0); - } + }; useEffect(() => { - let keyboardWillChangeFrame = Keyboard.addListener('keyboardWillChangeFrame', onKeyboardWillChangeFrame); - let keyboardDidShow = Keyboard.addListener("keyboardDidShow", onKeyboardDidShow); - let keyboardDidHide = Keyboard.addListener("keyboardDidHide", onKeyboardDidHide); + let keyboardWillChangeFrame = Keyboard.addListener( + 'keyboardWillChangeFrame', + onKeyboardWillChangeFrame + ); + let keyboardDidShow = Keyboard.addListener( + 'keyboardDidShow', + onKeyboardDidShow + ); + let keyboardDidHide = Keyboard.addListener( + 'keyboardDidHide', + onKeyboardDidHide + ); return () => { keyboardWillChangeFrame?.remove(); keyboardDidShow?.remove(); keyboardDidHide?.remove(); }; - },[]); + }, []); const onKeyboardWillChangeFrame = event => { - console.log('keyboad change frame',event) setFloating(event.endCoordinates.width !== width); }; - const showLinkPreview = async link => { - let _note = {...defaultNote}; - _note.title = 'Web link share'; - _note.content.data = !note.content.data - ? makeHtmlFromUrl(link) - : note.content.data + '\n' + makeHtmlFromUrl(link); + const showLinkPreview = async (note, link) => { + let _note = note; + _note.content.data = makeHtmlFromUrl(link); try { let preview = await getLinkPreview(link); _note.title = preview.siteName || preview.title; } catch (e) { console.log(e); } - setNote(_note); + return note; }; const loadData = async () => { try { - setNote(() => { - defaultNote.content.data = null; - return defaultNote; - }); const data = await ShareExtension.data(); - console.log(data); if (!data || data.length === 0) { setRawData({ value: '' }); + setNote({...defaultNote}); setLoadingIntent(false); - setQuickNote(true); return; } + let note = defaultNote; for (item of data) { if (item.type === 'text') { setRawData(item); if (validator.isURL(item.value)) { - await showLinkPreview(item.value); + note = await showLinkPreview(note, item.value); } else { - setNote(note => { - note.title = 'Note Share'; - note.content.data = note.content.data - ? note.content.data + '\n' + makeHtmlFromPlainText(item.value) - : makeHtmlFromPlainText(item.value); - - return note; - }); + note.content.data = makeHtmlFromPlainText(item.value); } } } + setNote({...note}); } catch (e) {} setLoadingIntent(false); }; useEffect(() => { - setNote(defaultNote); loadData(); sleep(50).then(() => { animate(1, 0); @@ -244,15 +249,20 @@ const NotesnookShare = () => { } const onPress = async () => { - titleInputRef.current?.blur(); - textInputRef.current?.blur(); + content = await getContent(); + if (!content || content === '') { + return; + } setLoading(true); - let add = async () => { - let _note = {...note}; - _note.content.data = - _note.content.data + makeHtmlFromPlainText(editorContentValue); - await db.notes.add(note); + let _note = { + ...note, + content: { + data: content, + type: 'tiny' + } + }; + await db.notes.add(_note); }; if (db && db.notes) { await add(); @@ -276,6 +286,35 @@ const NotesnookShare = () => { } }; + const getContent = () => { + return new Promise(resolve => { + let oncontent = value => { + eUnSubscribeEvent('share_content_event', oncontent); + resolve(value); + }; + eSubscribeEvent('share_content_event', oncontent); + webviewRef.current?.injectJavaScript(`(function() { + let html = document.querySelector(".htmldiff_div").innerHTML; + if (!html) { + html = ''; + } + reactNativeEventHandler('tiny', html); + })();`); + }); + }; + + const onMessage = event => { + if (!event) return; + let data = JSON.parse(event.nativeEvent.data); + if (data.type === 'tiny') { + eSendEvent('share_content_event', data.value); + } + }; + + useEffect(() => { + onLoad(); + }, [note]); + return ( { enabled={!floating && Platform.OS === 'ios'} onLayout={event => { if (prevAnimation.current === 0) return; - console.log('setting value here'); translate.setValue(event.nativeEvent.layout.height + 30); }} style={{ @@ -321,325 +359,180 @@ const NotesnookShare = () => { ] }} behavior="padding"> - {quickNote ? null : ( + - )} - - {loadingIntent ? ( - - - - - Parsing Data... - - - ) : ( - <> + position: 'absolute', + zIndex: 999, + ...getElevation(10), + right: 24, + bottom: -35 + }} + /> + - {quickNote ? null : ( - - - setNote(_note => { - _note.title = v; - return _note; - }) - } - onSubmitEditing={() => { - textInputRef.current?.focus(); - }} - blurOnSubmit={false} - placeholder="Note title" - /> - - )} - - {quickNote ? null : ( - - {[ - { - title: 'Plain text', - onPress: () => { - let html = validator.isURL(rawData.value) - ? makeHtmlFromUrl(rawData.value) - : makeHtmlFromPlainText(rawData.value); - setNote(note => { - note.content.data = html; - return note; - }); - onLoad(); - } - }, - ...[ - rawData?.value && validator.isURL(rawData.value) - ? { - title: 'Clip webpage', - onPress: async () => { - let html = await sanitizeHtml(rawData.value); - setNote(note => { - note.content.data = html; - return note; - }); - onLoad(); - } - } - : null - ] - ].map( - (item, index) => - item && ( -