mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-21 14:09:34 +01:00
feat: fully implement new note widget on android
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>Note Diff Preview</title>
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
<link rel="stylesheet" href="./fonts.css">
|
||||
<link id="dark_sheet" disabled rel="stylesheet" href="./dist/skins/notesnook-dark/content.min.css">
|
||||
<link id="light_sheet" disabled rel="stylesheet" href="./dist/skins/notesnook/content.min.css">
|
||||
<style>
|
||||
@@ -80,17 +81,41 @@
|
||||
padding: 12px !important;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
min-height: 150px;
|
||||
font-family: "Open Sans";
|
||||
}
|
||||
|
||||
[contenteditable] {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
|
||||
[contenteditable=true]:empty:before {
|
||||
content: attr(placeholder);
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
color: gray;
|
||||
/* For Firefox */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="htmldiff_div">
|
||||
<div placeholder="Write something..." contenteditable="true" class="htmldiff_div">
|
||||
|
||||
</div>
|
||||
<script src="./listeners.js"></script>
|
||||
<script src="./constants.js"></script>
|
||||
<script>
|
||||
function reactNativeEventHandler(type, value) {
|
||||
if (window.ReactNativeWebView) {
|
||||
window.ReactNativeWebView.postMessage(
|
||||
JSON.stringify({
|
||||
type: type,
|
||||
value: value
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
attachMessageListener()
|
||||
</script>
|
||||
</body>
|
||||
|
||||
BIN
apps/mobile/android/app/src/main/res/drawable/widget_preview.png
Normal file
BIN
apps/mobile/android/app/src/main/res/drawable/widget_preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -6,6 +6,7 @@
|
||||
android:padding="@dimen/widget_margin"
|
||||
android:theme="@style/ThemeOverlay.Notesnook.AppWidgetContainer">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/widget_button"
|
||||
@@ -14,7 +15,7 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/layout_bg"
|
||||
android:paddingHorizontal="10dp"
|
||||
|
||||
android:elevation="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialKeyguardLayout="@layout/note_widget"
|
||||
android:initialLayout="@layout/note_widget"
|
||||
android:minWidth="110dp"
|
||||
android:minHeight="40dp"
|
||||
android:previewImage="@drawable/example_appwidget_preview"
|
||||
android:minWidth="300dp"
|
||||
android:minHeight="50dp"
|
||||
android:previewImage="@drawable/widget_preview"
|
||||
android:resizeMode="horizontal"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:widgetCategory="home_screen"></appwidget-provider>
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>Note Diff Preview</title>
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
<link rel="stylesheet" href="./fonts.css">
|
||||
<link id="dark_sheet" disabled rel="stylesheet" href="./dist/skins/notesnook-dark/content.min.css">
|
||||
<link id="light_sheet" disabled rel="stylesheet" href="./dist/skins/notesnook/content.min.css">
|
||||
<style>
|
||||
@@ -80,17 +81,41 @@
|
||||
padding: 12px !important;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
min-height: 150px;
|
||||
font-family: "Open Sans";
|
||||
}
|
||||
|
||||
[contenteditable] {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
|
||||
[contenteditable=true]:empty:before {
|
||||
content: attr(placeholder);
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
color: gray;
|
||||
/* For Firefox */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="htmldiff_div">
|
||||
<div placeholder="Write something..." contenteditable="true" class="htmldiff_div">
|
||||
|
||||
</div>
|
||||
<script src="./listeners.js"></script>
|
||||
<script src="./constants.js"></script>
|
||||
<script>
|
||||
function reactNativeEventHandler(type, value) {
|
||||
if (window.ReactNativeWebView) {
|
||||
window.ReactNativeWebView.postMessage(
|
||||
JSON.stringify({
|
||||
type: type,
|
||||
value: value
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
attachMessageListener()
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -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 `<p style="overflow-wrap:anywhere;white-space:pre-wrap" >${text}</p>`;
|
||||
}
|
||||
|
||||
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 (
|
||||
<AnimatedSAV
|
||||
style={{
|
||||
@@ -307,7 +346,6 @@ const NotesnookShare = () => {
|
||||
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 : (
|
||||
<View
|
||||
style={{
|
||||
maxHeight: '100%',
|
||||
paddingHorizontal: 12
|
||||
}}>
|
||||
<View
|
||||
style={{
|
||||
width: 50,
|
||||
height: 6,
|
||||
borderRadius: 100,
|
||||
backgroundColor: colors.nav,
|
||||
alignSelf: 'center',
|
||||
position: 'absolute',
|
||||
marginTop: 15
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{loadingIntent ? (
|
||||
<View
|
||||
style={{
|
||||
height: 150,
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
width: '100%'
|
||||
}}>
|
||||
<ActivityIndicator color={colors.accent} />
|
||||
|
||||
<Text
|
||||
<Button
|
||||
color={colors.accent}
|
||||
onPress={onPress}
|
||||
loading={loading || loadingIntent}
|
||||
icon="check"
|
||||
iconSize={25}
|
||||
type="action"
|
||||
loading={loading}
|
||||
style={{
|
||||
color: colors.pri,
|
||||
fontSize: SIZE.md,
|
||||
marginTop: 5
|
||||
}}>
|
||||
Parsing Data...
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
position: 'absolute',
|
||||
zIndex: 999,
|
||||
...getElevation(10),
|
||||
right: 24,
|
||||
bottom: -35
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
maxHeight: '100%'
|
||||
marginTop: 10,
|
||||
minHeight: 100,
|
||||
borderRadius: 10,
|
||||
...getElevation(5),
|
||||
backgroundColor: colors.bg
|
||||
}}>
|
||||
{quickNote ? null : (
|
||||
<View
|
||||
style={{
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.nav,
|
||||
paddingHorizontal: 12
|
||||
}}>
|
||||
<TextInput
|
||||
ref={titleInputRef}
|
||||
style={{
|
||||
fontSize: 25,
|
||||
fontFamily:
|
||||
Platform.OS === 'android' ? 'Roboto-Medium' : null,
|
||||
fontWeight: Platform.OS === 'ios' ? '600' : null,
|
||||
color: colors.pri,
|
||||
flexGrow: 1,
|
||||
maxWidth: '100%'
|
||||
}}
|
||||
placeholderTextColor={colors.icon}
|
||||
defaultValue={note?.title}
|
||||
onChangeText={v =>
|
||||
setNote(_note => {
|
||||
_note.title = v;
|
||||
return _note;
|
||||
})
|
||||
}
|
||||
onSubmitEditing={() => {
|
||||
textInputRef.current?.focus();
|
||||
}}
|
||||
blurOnSubmit={false}
|
||||
placeholder="Note title"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{quickNote ? null : (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.nav
|
||||
}}>
|
||||
{[
|
||||
{
|
||||
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 && (
|
||||
<Button
|
||||
title={item.title}
|
||||
color={colors.nav}
|
||||
textStyle={{
|
||||
color: colors.icon,
|
||||
fontWeight: 'normal',
|
||||
fontSize: 14,
|
||||
fontFamily: null
|
||||
}}
|
||||
onPress={item.onPress}
|
||||
style={{
|
||||
borderWidth: 0.5,
|
||||
borderRadius: 100,
|
||||
borderColor: colors.icon,
|
||||
height: 30,
|
||||
marginRight: 10,
|
||||
marginLeft: index === 0 ? 12 : 0,
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{quickNote ? null : (
|
||||
<View
|
||||
style={{
|
||||
height: height * 0.25,
|
||||
width: '100%'
|
||||
}}>
|
||||
<WebView
|
||||
onLoad={onLoad}
|
||||
ref={webviewRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: 'transparent'
|
||||
}}
|
||||
cacheMode="LOAD_DEFAULT"
|
||||
domStorageEnabled={true}
|
||||
scrollEnabled={true}
|
||||
bounces={false}
|
||||
allowFileAccess={true}
|
||||
scalesPageToFit={true}
|
||||
allowingReadAccessToURL={
|
||||
Platform.OS === 'android' ? true : null
|
||||
}
|
||||
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
||||
allowFileAccessFromFileURLs={true}
|
||||
allowUniversalAccessFromFileURLs={true}
|
||||
originWhitelist={['*']}
|
||||
javaScriptEnabled={true}
|
||||
cacheEnabled={true}
|
||||
source={
|
||||
Platform.OS === 'ios'
|
||||
? {uri: sourceUri}
|
||||
: {
|
||||
uri: 'file:///android_asset/plaineditor.html',
|
||||
baseUrl: 'file:///android_asset/'
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
paddingHorizontal: 12
|
||||
height: height * 0.25,
|
||||
paddingBottom: 15
|
||||
}}>
|
||||
<WebView
|
||||
onLoad={onLoad}
|
||||
ref={webviewRef}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: 'transparent'
|
||||
}}
|
||||
cacheMode="LOAD_DEFAULT"
|
||||
domStorageEnabled={true}
|
||||
scrollEnabled={true}
|
||||
bounces={false}
|
||||
allowFileAccess={true}
|
||||
scalesPageToFit={true}
|
||||
allowingReadAccessToURL={
|
||||
Platform.OS === 'android' ? true : null
|
||||
}
|
||||
onMessage={onMessage}
|
||||
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
||||
allowFileAccessFromFileURLs={true}
|
||||
allowUniversalAccessFromFileURLs={true}
|
||||
originWhitelist={['*']}
|
||||
javaScriptEnabled={true}
|
||||
cacheEnabled={true}
|
||||
source={
|
||||
Platform.OS === 'ios'
|
||||
? {uri: sourceUri}
|
||||
: {
|
||||
uri: 'file:///android_asset/plaineditor.html',
|
||||
baseUrl: 'file:///android_asset/'
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 12,
|
||||
paddingRight: 80,
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Button
|
||||
color={colors.accent}
|
||||
color={colors.shade}
|
||||
onPress={onPress}
|
||||
loading={loading}
|
||||
icon="check"
|
||||
iconSize={25}
|
||||
type="action"
|
||||
icon={modes[mode].icon}
|
||||
onPress={async () => {
|
||||
let _mode = modes[mode];
|
||||
if (
|
||||
_mode.type === 'text' &&
|
||||
validator.isURL(rawData.value)
|
||||
) {
|
||||
let html = await sanitizeHtml(rawData.value);
|
||||
html = await absolutifyImgs(html, rawData.value);
|
||||
|
||||
setNote(note => {
|
||||
note.content.data = html;
|
||||
return {...note};
|
||||
});
|
||||
setMode(2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mode.type === 'clip') {
|
||||
let html = validator.isURL(rawData.value)
|
||||
? makeHtmlFromUrl(rawData.value)
|
||||
: makeHtmlFromPlainText(rawData.value);
|
||||
setNote(note => {
|
||||
note.content.data = html;
|
||||
return {...note};
|
||||
});
|
||||
setMode(1);
|
||||
return;
|
||||
}
|
||||
}}
|
||||
title={modes[mode].title}
|
||||
iconSize={18}
|
||||
iconColor={colors.accent}
|
||||
textStyle={{
|
||||
fontSize: 12,
|
||||
color: colors.accent,
|
||||
marginLeft: 5
|
||||
}}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
zIndex: 999,
|
||||
...getElevation(10),
|
||||
right: 24,
|
||||
bottom: -35
|
||||
marginRight: 10,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 12,
|
||||
marginTop: -2.5
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10,
|
||||
minHeight: 100,
|
||||
borderRadius: 10,
|
||||
...getElevation(5),
|
||||
backgroundColor: colors.bg
|
||||
}}>
|
||||
<TextInput
|
||||
ref={textInputRef}
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: colors.pri,
|
||||
fontFamily: 'OpenSans-Regular',
|
||||
padding: 12,
|
||||
width: '100%'
|
||||
{Clipboard.hasString() ? (
|
||||
<Button
|
||||
color={colors.nav}
|
||||
onPress={onPress}
|
||||
icon="clipboard"
|
||||
onPress={async () => {
|
||||
let text = await Clipboard.getString();
|
||||
if (text) {
|
||||
let content = await getContent();
|
||||
setNote(note => {
|
||||
note.content.data =
|
||||
content + '\n' + makeHtmlFromPlainText(text);
|
||||
return {...note};
|
||||
});
|
||||
}
|
||||
}}
|
||||
iconSize={18}
|
||||
iconColor={colors.icon}
|
||||
title="Paste"
|
||||
textStyle={{
|
||||
fontSize: 12,
|
||||
color: colors.icon,
|
||||
marginLeft: 5
|
||||
}}
|
||||
placeholderTextColor={colors.icon}
|
||||
onChangeText={v => (editorContentValue = v)}
|
||||
multiline={true}
|
||||
numberOfLines={quickNote ? 5 : 3}
|
||||
textAlignVertical="top"
|
||||
value={editorContentValue}
|
||||
blurOnSubmit={false}
|
||||
placeholder={
|
||||
quickNote
|
||||
? 'Write something...'
|
||||
: 'Add some additional notes here'
|
||||
}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 12,
|
||||
paddingRight: 80
|
||||
}}>
|
||||
<Button
|
||||
color={colors.shade}
|
||||
onPress={onPress}
|
||||
loading={loading}
|
||||
icon={modes[mode].icon}
|
||||
onPress={async () => {
|
||||
let _mode = modes[mode];
|
||||
if (_mode.type === "text") {
|
||||
setMode(2);
|
||||
return;
|
||||
}
|
||||
if (_mode.type === "clip") {
|
||||
setMode(1);
|
||||
return
|
||||
}
|
||||
|
||||
if (_mode.type == "link") {
|
||||
setMode(3)
|
||||
}
|
||||
}}
|
||||
title={modes[mode].title}
|
||||
iconSize={18}
|
||||
iconColor={colors.accent}
|
||||
textStyle={{
|
||||
fontSize: 12,
|
||||
color: colors.accent,
|
||||
marginLeft: 5
|
||||
}}
|
||||
style={{
|
||||
marginRight: 10,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 12,
|
||||
marginTop: -2.5
|
||||
}}
|
||||
/>
|
||||
|
||||
{Clipboard.hasString() ? (
|
||||
<Button
|
||||
color={colors.nav}
|
||||
onPress={onPress}
|
||||
loading={loading}
|
||||
icon="clipboard"
|
||||
onPress={async () => {
|
||||
let text = await Clipboard.getString();
|
||||
if (text) {
|
||||
textInputRef.current?.setNativeProps({
|
||||
text: text
|
||||
});
|
||||
editorContentValue = text;
|
||||
}
|
||||
}}
|
||||
iconSize={18}
|
||||
iconColor={colors.icon}
|
||||
title="Paste"
|
||||
textStyle={{
|
||||
fontSize: 12,
|
||||
color: colors.icon,
|
||||
marginLeft: 5
|
||||
}}
|
||||
style={{
|
||||
marginRight: 15,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 6,
|
||||
marginTop: -2.5
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
marginRight: 15,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 6,
|
||||
marginTop: -2.5
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</AnimatedKAV>
|
||||
</AnimatedSAV>
|
||||
);
|
||||
@@ -699,7 +592,7 @@ const Button = ({
|
||||
]}>
|
||||
{loading && <ActivityIndicator color="white" />}
|
||||
|
||||
{icon && (
|
||||
{icon && !loading && (
|
||||
<Icon name={icon} size={iconSize} color={iconColor || 'white'} />
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user