diff --git a/apps/mobile/App.js b/apps/mobile/App.js index 7aea56c3b..7905d4c2a 100644 --- a/apps/mobile/App.js +++ b/apps/mobile/App.js @@ -39,6 +39,7 @@ import Animated, {Easing} from 'react-native-reanimated'; import {useForceUpdate} from './src/views/ListsEditor'; import FastStorage from 'react-native-fast-storage'; import {defaultState} from './src/provider/defaultState'; +import {EditorPosition} from './src/utils/animations'; export const DDS = new DeviceDetectionService(); export const db = new Storage(StorageInterface); @@ -176,9 +177,9 @@ const App = () => { } else { s = JSON.parse(s); scale.fontScale = s.fontScale; - console.log(scale.fontScale); + updateSize(); - console.log(SIZE.lg); + dispatch({type: ACTIONS.SETTINGS, settings: {...s}}); } dispatch({type: ACTIONS.THEME, colors: newColors}); @@ -367,7 +368,27 @@ const App = () => { /> )} + + (editorRef = ref)} + style={{ + width: '100%', + height: '100%', + position: 'absolute', + zIndex: 10, + backgroundColor: colors.bg, + elevation: 10, + transform: [ + { + translateX: EditorPosition, + }, + ], + }}> + + + + diff --git a/apps/mobile/src/components/ActionSheetComponent/index.js b/apps/mobile/src/components/ActionSheetComponent/index.js index b3b2b9efc..e6390a98a 100644 --- a/apps/mobile/src/components/ActionSheetComponent/index.js +++ b/apps/mobile/src/components/ActionSheetComponent/index.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useState, createRef} from 'react'; import { Dimensions, StatusBar, @@ -32,7 +32,7 @@ import NavigationService from '../../services/NavigationService'; const w = Dimensions.get('window').width; const h = Dimensions.get('window').height; -let tagsInputRef; +const tagsInputRef = createRef(); export const ActionSheetComponent = ({ close = () => {}, item, @@ -42,7 +42,7 @@ export const ActionSheetComponent = ({ columnItems = [], }) => { const [state, dispatch] = useTracked(); - const {colors, tags} = state; + const {colors, tags, currentEditingNote} = state; const [focused, setFocused] = useState(false); const [note, setNote] = useState( item @@ -78,14 +78,16 @@ export const ActionSheetComponent = ({ let backPressCount = 0; const _onSubmit = async () => { - if (!tagToAdd || tagToAdd === '') return; - + if (!tagToAdd || tagToAdd === '' || tagToAdd.trimStart().length == 0) { + ToastEvent.show('Empty Tag', 'success'); + return; + } let tag = tagToAdd; if (tag.includes(' ')) { tag = tag.replace(' ', '_'); } - tagsInputRef.setNativeProps({ + tagsInputRef.current?.setNativeProps({ text: '', }); @@ -116,10 +118,20 @@ export const ActionSheetComponent = ({ setNote({...db.notes.note(note.id).data}); - tagsInputRef.setNativeProps({ + tagsInputRef.current?.setNativeProps({ text: tagInputValue, }); } + } else if (event.nativeEvent.key === ' ') { + _onSubmit(); + tagsInputRef.current?.setNativeProps({ + text: '', + }); + } else if (event.nativeE.key === ',') { + _onSubmit(); + tagsInputRef.current?.setNativeProps({ + text: '', + }); } }; @@ -333,9 +345,14 @@ export const ActionSheetComponent = ({ key={tag} onPress={async () => { let oldProps = {...note}; - - await db.notes.note(note.id).untag(oldProps.tags.indexOf(tag)); - localRefresh(item.type); + try { + await db.notes + .note(note.id) + .untag(oldProps.tags[oldProps.tags.indexOf(tag)]); + localRefresh(oldProps.type); + } catch (e) { + localRefresh(oldProps.type); + } }} style={{ flexDirection: 'row', @@ -422,7 +439,7 @@ export const ActionSheetComponent = ({ }} name={rowItem.icon} size={DDS.isTab ? SIZE.xl : SIZE.lg} - color={colors.accent} + color={rowItem.name === 'Delete' ? colors.errorText : colors.accent} /> + { + tagsInputRef.current?.focus(); + }} + style={{ + position: 'absolute', + width: '100%', + height: '100%', + }}> {note && note.tags ? note.tags.map(_renderTag) : null} (tagsInputRef = ref)} + ref={tagsInputRef} placeholderTextColor={colors.icon} onFocus={() => { setFocused(true); diff --git a/apps/mobile/src/components/AddNotebookDialog/index.js b/apps/mobile/src/components/AddNotebookDialog/index.js index 22033a1e3..ac57f1026 100644 --- a/apps/mobile/src/components/AddNotebookDialog/index.js +++ b/apps/mobile/src/components/AddNotebookDialog/index.js @@ -108,33 +108,42 @@ export class AddNotebookDialog extends React.Component { if (this.currentInputValue) { this.onSubmit(); } - let {topics} = this.state; - let {toEdit} = this.props; - if (!this.title) - return ToastEvent.show('Title is required', 'error', 3000, () => {}, ''); - let id = toEdit && toEdit.id ? toEdit.id : null; + setTimeout(async () => { + let {topics} = this.state; + let {toEdit} = this.props; + if (!this.title) + return ToastEvent.show( + 'Title is required', + 'error', + 3000, + () => {}, + '', + ); - if (id) { - await db.notebooks.add({ - title: this.title, - description: this.description, - id: id, - }); + let id = toEdit && toEdit.id ? toEdit.id : null; - await db.notebooks.notebook(id).topics.add(...topics); - } else { - await db.notebooks.add({ - title: this.title, - description: this.description, - topics, - id: id, - }); - } + if (id) { + await db.notebooks.add({ + title: this.title, + description: this.description, + id: id, + }); - updateEvent({type: ACTIONS.NOTEBOOKS}); - this.close(); - ToastEvent.show('New notebook added', 'success', 3000, () => {}, ''); + await db.notebooks.notebook(id).topics.add(...topics); + } else { + await db.notebooks.add({ + title: this.title, + description: this.description, + topics, + id: id, + }); + } + this.close(); + updateEvent({type: ACTIONS.NOTEBOOKS}); + + ToastEvent.show('New notebook added', 'success', 3000, () => {}, ''); + }, 100); }; onSubmit = () => { @@ -205,6 +214,9 @@ export class AddNotebookDialog extends React.Component { transparent={true} animated animationType="fade" + onShow={() => { + this.titleRef.focus(); + }} onRequestClose={this.close}> { @@ -52,6 +53,9 @@ export class AddTopicDialog extends React.Component { animated animationType="fade" transparent={true} + onShow={() => { + this.titleRef.current?.focus(); + }} onRequestClose={() => { refs = []; this.close(); @@ -102,6 +106,7 @@ export class AddTopicDialog extends React.Component { { + if (i.type === 'note') { + await db.notes.delete(i.id); + ToastEvent.show('Notes moved to trash', 'error', 3000); + updateEvent({type: i.type}); + } else if (i.type === 'topic') { + await db.notebooks.notebook(i.notebookId).topics.delete(i.title); + updateEvent({type: 'notebook'}); + + ToastEvent.show('Topics deleted', 'error', 3000); + } else if (i.type === 'notebook') { + await db.notebooks.delete(i.id); + updateEvent({type: i.type}); + ToastEvent.show('Notebooks moved to trash', 'error', 3000); + } + }); + + updateEvent({type: ACTIONS.CLEAR_SELECTION}); + updateEvent({type: ACTIONS.SELECTION_MODE, enabled: false}); this.setState({ visible: false, }); + if (editing.currentlyEditing) { + if (DDS.isTab) { + eSendEvent(eCloseFullscreenEditor); + eSendEvent(eOnLoadNote, {type: 'new'}); + } else { + NavigationService.goBack(); + } + } break; } case dialogActions.ACTION_EXIT: { @@ -81,7 +94,7 @@ export class Dialog extends Component { break; } case dialogActions.ACTION_TRASH: { - db.trash.restore(item.id); + await db.trash.restore(i.id); ToastEvent.show( item.type.slice(0, 1).toUpperCase() + item.type.slice(1) + @@ -89,6 +102,7 @@ export class Dialog extends Component { 'success', 3000, ); + updateEvent({type: ACTIONS.TRASH}); this.hide(); break; @@ -108,8 +122,10 @@ export class Dialog extends Component { }; show = () => { + console.log(history.selectedItemsList.length, 'length'); this.setState({ visible: true, + selectedItemsLength: history.selectedItemsList.length, }); }; hide = () => { @@ -187,7 +203,11 @@ export class Dialog extends Component { textAlign: 'center', marginTop: 10, }}> - {paragraph} + {this.state.selectedItemsLength > 0 + ? 'Delete ' + + this.state.selectedItemsLength + + ' selected items?' + : paragraph} ) : null} diff --git a/apps/mobile/src/components/Menu/index.js b/apps/mobile/src/components/Menu/index.js index a8476aead..3c742f8fd 100644 --- a/apps/mobile/src/components/Menu/index.js +++ b/apps/mobile/src/components/Menu/index.js @@ -30,6 +30,7 @@ import {eSendEvent} from '../../services/eventManager'; import {eOpenModalMenu, eSendSideMenuOverlayRef} from '../../services/events'; import NavigationService from '../../services/NavigationService'; import {timeSince, getElevation, hexToRGBA} from '../../utils/utils'; +import {inputRef} from '../SearchInput'; export const Menu = ({ close = () => {}, @@ -189,7 +190,7 @@ export const Menu = ({ )} { item.close === false ? null : close(); - + if (item.close) { + inputRef.current?.setNativeProps({ + text: '', + }); + dispatch({type: ACTIONS.SEARCH_RESULTS, results: []}); + } item.func(); }} style={{ diff --git a/apps/mobile/src/components/NoteItem/index.js b/apps/mobile/src/components/NoteItem/index.js index 11b55ec7c..38b0e45a6 100644 --- a/apps/mobile/src/components/NoteItem/index.js +++ b/apps/mobile/src/components/NoteItem/index.js @@ -12,6 +12,7 @@ import { simpleDialogEvent, TEMPLATE_TRASH, } from '../DialogManager'; +import {openEditorAnimation} from '../../utils/animations'; const w = Dimensions.get('window').width; const h = Dimensions.get('window').height; @@ -116,14 +117,15 @@ export default class NoteItem extends React.Component { onPress={() => { if (item.locked) { eSendEvent(eOpenVaultDialog, {unlock: true, i: item}); + return; + } + if (DDS.isTab) { + eSendEvent(eOnLoadNote, item); + } else if (isTrash) { + simpleDialogEvent(TEMPLATE_TRASH(item.type)); } else { - DDS.isTab - ? eSendEvent(eOnLoadNote, item) - : isTrash - ? simpleDialogEvent(TEMPLATE_TRASH(item.type)) - : NavigationService.navigate('Editor', { - note: item, - }); + eSendEvent(eOnLoadNote, item); + openEditorAnimation(); } }} style={{ diff --git a/apps/mobile/src/components/NotebookItem/index.js b/apps/mobile/src/components/NotebookItem/index.js index 39ad7c277..f1a3fa6b5 100644 --- a/apps/mobile/src/components/NotebookItem/index.js +++ b/apps/mobile/src/components/NotebookItem/index.js @@ -193,22 +193,21 @@ export const NotebookItem = ({ )} - {isTopic && item.totalNotes > 0 ? ( - - {item && item.totalNotes && item.totalNotes > 1 - ? item.totalNotes + ' notes' - : item.totalNotes === 1 - ? item.totalNotes + ' note' - : null} - - ) : null} + + + {item && item.totalNotes && item.totalNotes > 1 + ? item.totalNotes + ' notes' + : item.totalNotes === 1 + ? item.totalNotes + ' note' + : '0 notes'} + {hideMore || (item.title === 'General' && item.type === 'topic') ? null : ( @@ -243,7 +242,7 @@ export const NotebookItem = ({ onPress={async () => { let noteIds = []; selectedItemsList.forEach(item => noteIds.push(item.id)); - console.log(noteIds); + db.notes.move( { topic: item.title, diff --git a/apps/mobile/src/components/NotesList/index.js b/apps/mobile/src/components/NotesList/index.js index 60065356f..5a025e29d 100644 --- a/apps/mobile/src/components/NotesList/index.js +++ b/apps/mobile/src/components/NotesList/index.js @@ -18,6 +18,7 @@ import {NotesPlaceHolder} from '../ListPlaceholders'; import NoteItem from '../NoteItem'; import SelectionWrapper from '../SelectionWrapper'; import {db, DDS} from '../../../App'; +import {inputRef} from '../SearchInput'; export const NotesList = ({isGrouped = false}) => { const [state, dispatch] = useTracked(); @@ -155,12 +156,12 @@ export const NotesList = ({isGrouped = false}) => { style={{ marginTop: Platform.OS == 'ios' - ? notes[0] + ? notes[0] && !selectionMode ? DDS.isTab ? 115 : 135 : 135 - 60 - : notes[0] + : notes[0] && !selectionMode ? 155 : 155 - 60, flexDirection: 'row', @@ -178,6 +179,9 @@ export const NotesList = ({isGrouped = false}) => { { + inputRef.current?.setNativeProps({ + text: '', + }); dispatch({ type: ACTIONS.SEARCH_RESULTS, results: [], @@ -194,7 +198,7 @@ export const NotesList = ({isGrouped = false}) => { ); const _listKeyExtractor = (item, index) => item.id.toString(); - console.log(notes); + return isGrouped && searchResults.length === 0 ? ( (sectionListRef = ref)} @@ -269,7 +273,7 @@ const PinnedItems = () => { return ( <> item.id.toString()} renderItem={({item, index}) => item.type === 'note' ? ( @@ -288,6 +292,7 @@ const PinnedItems = () => { borderBottomWidth: 0, marginHorizontal: 0, }} + onLongPress={() => {}} pinned={true} item={item} index={index} diff --git a/apps/mobile/src/components/SearchInput/index.js b/apps/mobile/src/components/SearchInput/index.js index 40a5544dd..b1a49b3f8 100644 --- a/apps/mobile/src/components/SearchInput/index.js +++ b/apps/mobile/src/components/SearchInput/index.js @@ -8,12 +8,14 @@ import {useTracked} from '../../provider'; const {Value, timing, block} = Animated; +export const inputRef = createRef(); + export const Search = props => { const [state, dispatch] = useTracked(); const {colors, searchResults} = state; const [focus, setFocus] = useState(false); - const inputRef = createRef(); + const _marginAnim = new Value(0); const _opacity = new Value(1); const _borderAnim = new Value(1.5); @@ -98,7 +100,7 @@ export const Search = props => { text: '', }); }} - name={searchResults.length > 0 ? 'x' : 'magnify'} + name={searchResults.length > 0 ? 'close' : 'magnify'} color={ focus ? props.headerColor diff --git a/apps/mobile/src/components/SelectionHeader/index.js b/apps/mobile/src/components/SelectionHeader/index.js index 122553a4b..f6b89dc05 100644 --- a/apps/mobile/src/components/SelectionHeader/index.js +++ b/apps/mobile/src/components/SelectionHeader/index.js @@ -14,8 +14,9 @@ import {useTracked} from '../../provider'; import {ACTIONS} from '../../provider/actions'; import {w, ToastEvent} from '../../utils/utils'; import {eSendEvent} from '../../services/eventManager'; -import {eOpenMoveNoteDialog} from '../../services/events'; +import {eOpenMoveNoteDialog, eOpenSimpleDialog} from '../../services/events'; import {db} from '../../../App'; +import {TEMPLATE_DELETE} from '../DialogManager'; export const AnimatedSafeAreaView = Animatable.createAnimatableComponent( SafeAreaView, @@ -105,6 +106,7 @@ export const SelectionHeader = ({navigation}) => { { dispatch({type: ACTIONS.SELECTION_MODE, enabled: false}); + dispatch({type: ACTIONS.CLEAR_SELECTION}); eSendEvent(eOpenMoveNoteDialog); }}> { {currentScreen === 'trash' || currentScreen === 'notebooks' ? null : ( { + let favCount = 0; + let unFavCount = 0; if (selectedItemsList.length > 0) { selectedItemsList.forEach(async item => { + if (item.favorite) { + favCount += 1; + } else { + unFavCount += 1; + } await db.notes.note(item.id).favorite(); + dispatch({type: ACTIONS.NOTES}); + dispatch({type: ACTIONS.FAVORITES}); }); + dispatch({type: ACTIONS.SELECTION_MODE, enabled: false}); - dispatch({type: ACTIONS.NOTES}); + dispatch({type: ACTIONS.CLEAR_SELECTION}); - ToastEvent.show('Notes added to favorites', 'success'); + + ToastEvent.show( + favCount + + ' notes added to favorites &' + + unFavCount + + 'removed', + 'success', + ); } }}> { {currentScreen === 'trash' ? null : ( { + eSendEvent(eOpenSimpleDialog, TEMPLATE_DELETE('item')); + return; if (selectedItemsList.length > 0) { let noteIds = []; selectedItemsList.forEach(item => { diff --git a/apps/mobile/src/components/Toast/index.js b/apps/mobile/src/components/Toast/index.js index 5bc743650..61befb6d5 100644 --- a/apps/mobile/src/components/Toast/index.js +++ b/apps/mobile/src/components/Toast/index.js @@ -65,16 +65,17 @@ export const Toast = () => { return ( { }}> { borderRadius: 5, paddingHorizontal: ph, paddingVertical: pv, + opacity: toast ? 1 : 0, justifyContent: 'center', elevation: 25, flexDirection: 'row', diff --git a/apps/mobile/src/components/VaultDialog/index.js b/apps/mobile/src/components/VaultDialog/index.js index 3fd97cc75..6385364c9 100644 --- a/apps/mobile/src/components/VaultDialog/index.js +++ b/apps/mobile/src/components/VaultDialog/index.js @@ -8,6 +8,7 @@ import {getElevation} from '../../utils/utils'; import NavigationService from '../../services/NavigationService'; import {updateEvent} from '../DialogManager'; +import Share from 'react-native-share'; export class VaultDialog extends Component { constructor(props) { @@ -24,24 +25,19 @@ export class VaultDialog extends Component { }); }; close = (share = false, item = null) => { - this.setState( - { - visible: false, - }, + if (share && item) { + let m = `${item.title}\n \n ${item.content.text}`; + Share.open({ + title: 'Share note to', + failOnCancel: false, + message: m, + }); + updateEvent({type: item.type}); + } - () => { - if (share && note) { - let m = `${item.title}\n \n ${item.content.text}`; - - Share.open({ - title: 'Share note to', - failOnCancel: false, - message: m, - }); - } - updateEvent({type: this.props.note.type}); - }, - ); + this.setState({ + visible: false, + }); }; onPress = async () => { diff --git a/apps/mobile/src/provider/defaultState.js b/apps/mobile/src/provider/defaultState.js index c1fda7da0..2e01c94ba 100644 --- a/apps/mobile/src/provider/defaultState.js +++ b/apps/mobile/src/provider/defaultState.js @@ -8,7 +8,10 @@ export const defaultState = { notebooks: [], trash: [], favorites: [], - pinned: [], + pinned: { + notes: [], + notebooks: [], + }, tags: [], colorNotes: [], user: {}, diff --git a/apps/mobile/src/provider/reducer.js b/apps/mobile/src/provider/reducer.js index 8d4367152..6e8035048 100644 --- a/apps/mobile/src/provider/reducer.js +++ b/apps/mobile/src/provider/reducer.js @@ -1,5 +1,5 @@ import {db} from '../../App'; -import {SideMenuEvent} from '../utils/utils'; +import {SideMenuEvent, history} from '../utils/utils'; import {ACTIONS} from './actions'; export const reducer = (state, action) => { @@ -50,10 +50,15 @@ export const reducer = (state, action) => { }; } case ACTIONS.PINNED: { - let pinned = db.notes.pinned; + let notes = db.notes.pinned; + let notebooks = db.notebooks.pinned; + return { ...state, - pinned: pinned, + pinned: { + notes, + notebooks, + }, }; } case ACTIONS.CURRENT_SCREEN: { @@ -99,13 +104,16 @@ export const reducer = (state, action) => { } else { selectedItems.push(action.item); } + history.selectedItemsList = selectedItems; return { ...state, selectedItemsList: selectedItems, + selectionMode: selectedItems.length > 0 ? true : false, }; } case ACTIONS.CLEAR_SELECTION: { + history.selectedItemsList = []; return { ...state, selectedItemsList: [], diff --git a/apps/mobile/src/services/validation.js b/apps/mobile/src/services/validation.js index de3e5f9dc..95bf33840 100644 --- a/apps/mobile/src/services/validation.js +++ b/apps/mobile/src/services/validation.js @@ -1,26 +1,18 @@ -import {ToastEvent} from '../utils/utils'; - let regex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; export function validateEmail(email) { if (email && email.length > 0) { - ToastEvent.show('Please enter a valid email address'); return regex.test(email); } else { - ToastEvent.show('Please enter email or passoword to login'); return false; } } export function validatePass(password) { if (password && password.length <= 0) { - ToastEvent.show('No password provided'); - return false; } if (password && password.length < 8 && password.length > 0) { - ToastEvent.show('Password too short'); - return false; } else if (password && password.length > 8 && password.length > 0) { return true; @@ -30,10 +22,8 @@ export function validatePass(password) { export function validateUsername(username) { let regex = /^[a-z0-9_-]{3,16}$/gim; if (username && username.length > 0) { - //ToastEvent.show('Please enter a valid email address'); return regex.test(username); } else { - //ToastEvent.show('Please enter email or passoword to login'); return false; } } diff --git a/apps/mobile/src/utils/animations.js b/apps/mobile/src/utils/animations.js index 0b2159f40..2b3ad2671 100644 --- a/apps/mobile/src/utils/animations.js +++ b/apps/mobile/src/utils/animations.js @@ -1,3 +1,28 @@ +import Animated, {Easing} from 'react-native-reanimated'; +import {Dimensions} from 'react-native'; + +const {color, Value, timing} = Animated; + +export const EditorPosition = new Value(Dimensions.get('window').width * 1.5); + +export function openEditorAnimation() { + let openConfigH = { + duration: 300, + toValue: 0, + easing: Easing.inOut(Easing.ease), + }; + timing(EditorPosition, openConfigH).start(); +} + +export function exitEditorAnimation() { + let openConfigH = { + duration: 300, + toValue: Dimensions.get('window').width * 1.5, + easing: Easing.inOut(Easing.ease), + }; + timing(EditorPosition, openConfigH).start(); +} + export const slideRight = { 0: { transform: [{translateX: -4}], diff --git a/apps/mobile/src/utils/storage.js b/apps/mobile/src/utils/storage.js index 31e4399b4..d98f34c21 100644 --- a/apps/mobile/src/utils/storage.js +++ b/apps/mobile/src/utils/storage.js @@ -1,5 +1,6 @@ import {NativeModules} from 'react-native'; import FastStorage from 'react-native-fast-storage'; + var Aes = NativeModules.Aes; async function read(key) { @@ -25,12 +26,16 @@ function encrypt(password, data) { let key; return Aes.pbkdf2('password', 'salt', 5000, 256).then(aes => { key = aes; - console.log(aes); return Aes.randomKey(16).then(iv => { - return Aes.encrypt(data, key, iv).then(cipher => ({ - cipher, - iv, - })); + return Aes.encrypt(data, key, iv).then(cipher => { + return Aes.hmac256(cipher, key).then(hash => { + return { + hash, + cipher, + iv, + }; + }); + }); }); }); } @@ -40,8 +45,13 @@ function decrypt(password, data) { return Aes.pbkdf2(password, 'salt', 5000, 256).then(aes => { key = aes; - return Aes.decrypt(data.cipher, key, data.iv).then(e => { - return e; + return Aes.hmac256(data.cipher, key).then(hash => { + if (hash !== data.hash) { + throw new Error('Wrong password'); + } + return Aes.decrypt(data.cipher, key, data.iv).then(e => { + return e; + }); }); }); } diff --git a/apps/mobile/src/utils/utils.js b/apps/mobile/src/utils/utils.js index b6b39f0ff..d10b6e84e 100755 --- a/apps/mobile/src/utils/utils.js +++ b/apps/mobile/src/utils/utils.js @@ -18,6 +18,15 @@ export const getElevation = elevation => { }; }; +export const editing = { + currentlyEditing: false, + isFullscreen: false, +}; + +export const history = { + selectedItemsList: [], +}; + export function timeSince(date) { var seconds = Math.floor((new Date() - date) / 1000); @@ -49,7 +58,7 @@ export const w = Dimensions.get('window').width; export const h = Dimensions.get('window').height; export const ToastEvent = { - show: (message, type, duration = 1000, func = null, actionText = '') => { + show: (message, type, duration = 3000, func = null, actionText = '') => { eSendEvent(eShowToast, { message, type, @@ -58,7 +67,7 @@ export const ToastEvent = { actionText, }); }, - hide: (message, type, duration = 1000, func = null, actionText = '') => { + hide: (message, type, duration = 3000, func = null, actionText = '') => { eSendEvent(eHideToast, { message, type, diff --git a/apps/mobile/src/views/Editor/index.js b/apps/mobile/src/views/Editor/index.js index 71af92bd3..fec9173f2 100755 --- a/apps/mobile/src/views/Editor/index.js +++ b/apps/mobile/src/views/Editor/index.js @@ -1,6 +1,5 @@ -import React, {useEffect, useState} from 'react'; +import React, {createRef, useEffect, useState} from 'react'; import { - ActivityIndicator, BackHandler, KeyboardAvoidingView, Linking, @@ -9,16 +8,15 @@ import { Text, TouchableOpacity, View, + SafeAreaView, } from 'react-native'; -import * as Animatable from 'react-native-animatable'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import WebView from 'react-native-webview'; import {db, DDS} from '../../../App'; -import {SIZE, WEIGHT, normalize, opacity} from '../../common/common'; +import {normalize, SIZE, WEIGHT} from '../../common/common'; import { ActionSheetEvent, simpleDialogEvent, - TEMPLATE_EXIT, TEMPLATE_EXIT_FULLSCREEN, TEMPLATE_INFO, } from '../../components/DialogManager'; @@ -34,29 +32,39 @@ import { eOnLoadNote, eOpenFullscreenEditor, } from '../../services/events'; -import {SideMenuEvent, timeConverter, ToastEvent} from '../../utils/utils'; -import {AnimatedSafeAreaView} from '../Home'; -import NavigationService from '../../services/NavigationService'; +import {exitEditorAnimation} from '../../utils/animations'; +import { + editing, + SideMenuEvent, + timeConverter, + ToastEvent, +} from '../../utils/utils'; -let EditorWebView; +const EditorWebView = createRef(); let note = {}; let id = null; -let dateEdited = null; var content = null; var title = null; let timer = null; let saveCounter = 0; let tapCount = 0; + const Editor = ({navigation, noMenu}) => { // Global State const [state, dispatch] = useTracked(); const {colors} = state; - const [loading, setLoading] = useState(true); const [fullscreen, setFullscreen] = useState(false); - + const [dateEdited, setDateEdited] = useState(0); // FUNCTIONS - const post = value => EditorWebView.postMessage(value); + const post = value => EditorWebView.current?.postMessage(value); + + useEffect(() => { + let c = {...colors}; + c.factor = normalize(1); + post(JSON.stringify(c)); + }, [colors.bg]); + useEffect(() => { eSubscribeEvent(eOnLoadNote, loadNote); @@ -65,56 +73,44 @@ const Editor = ({navigation, noMenu}) => { }; }, []); - useEffect(() => { - setLoading(true); - setTimeout(() => { - setLoading(false); - }, 3000); - }, [colors.bg]); - - const loadNote = item => { + const loadNote = async item => { if (note && note.id) { - saveNote(true).then(() => { - dispatch({type: ACTIONS.NOTES}); - if (item && item.type === 'new') { - clearEditor(); - } else { - note = item; - if (DDS.isTab) { - dispatch({ - type: ACTIONS.CURRENT_EDITING_NOTE, - id: item.id, - }); - } - - updateEditor(); - } - }); + dispatch({type: ACTIONS.NOTES}); + if (item && item.type === 'new') { + await clearEditor(); + } else { + note = item; + dispatch({ + type: ACTIONS.CURRENT_EDITING_NOTE, + id: item.id, + }); + updateEditor(); + } } else { dispatch({type: ACTIONS.NOTES}); if (item && item.type === 'new') { - clearEditor(); + await clearEditor(); } else { note = item; - if (DDS.isTab) { - dispatch({ - type: ACTIONS.CURRENT_EDITING_NOTE, - id: item.id, - }); - } + dispatch({ + type: ACTIONS.CURRENT_EDITING_NOTE, + id: item.id, + }); updateEditor(); } } }; - const clearEditor = () => { - id = null; + const clearEditor = async () => { + await saveNote(); title = null; content = null; - note = {}; + note = null; + id = null; + tapCount = 0; saveCounter = 0; - EditorWebView.reload(); - + post('clear'); + post(JSON.stringify({type: 'text', value: ''})); post('focusTitle'); }; @@ -131,7 +127,6 @@ const Editor = ({navigation, noMenu}) => { const _onMessage = evt => { if (evt.nativeEvent.data === 'loaded') { - setLoading(false); } else if ( evt.nativeEvent.data !== '' && evt.nativeEvent.data !== 'loaded' @@ -165,8 +160,6 @@ const Editor = ({navigation, noMenu}) => { }; } - console.log(content.delta, 'i am called'); - let rId = await db.notes.add({ title, content: { @@ -189,8 +182,7 @@ const Editor = ({navigation, noMenu}) => { } }, 500); } - - if (DDS.isTab) { + if (id) { dispatch({ type: ACTIONS.CURRENT_EDITING_NOTE, id: id, @@ -248,17 +240,10 @@ const Editor = ({navigation, noMenu}) => { ); } - if (navigation && navigation.state.params && navigation.state.params.note) { - note = navigation.state.params.note; - - updateEditor(); - } else if (note && note.id) { + if (note && note.id) { updateEditor(); } else { post('focusTitle'); - wait(500).then(() => { - setLoading(false); - }); } let c = {...colors}; c.factor = normalize(1); @@ -274,17 +259,14 @@ const Editor = ({navigation, noMenu}) => { }); const updateEditor = async () => { - console.log('before', content, title, id); title = note.title; id = note.id; - dateEdited = note.dateEdited; + setDateEdited(note.dateEdited); content = note.content; if (!note.locked) { content.delta = await db.notes.note(id).delta(); } - console.log('after', content, title, id); - saveCounter = 0; if (title !== null || title === '') { @@ -297,18 +279,11 @@ const Editor = ({navigation, noMenu}) => { } else { post('focusTitle'); post('clear'); - wait(500).then(() => { - setLoading(false); - }); } if (note.content.text === '' && note.content.delta === null) { post('clear'); - wait(500).then(() => { - setLoading(false); - }); } else if (note.content.delta) { let delta; - console.log(note.content.delta, 'HERE'); if (typeof note.content.delta !== 'string') { delta = note.content.delta; } else { @@ -318,9 +293,6 @@ const Editor = ({navigation, noMenu}) => { post(JSON.stringify(delta)); } else { post(JSON.stringify({type: 'text', value: note.content.text})); - wait(2000).then(() => { - setLoading(false); - }); } }; @@ -334,8 +306,92 @@ const Editor = ({navigation, noMenu}) => { link.click(); }`; - const _renderEditor = () => { - return ( + const closeFullscreen = () => { + setFullscreen(false); + }; + + // EFFECTS + + useEffect(() => { + eSubscribeEvent(eCloseFullscreenEditor, closeFullscreen); + + return () => { + eUnSubscribeEvent(eCloseFullscreenEditor, closeFullscreen); + }; + }); + + const _onHardwareBackPress = async () => { + if (tapCount > 0) { + exitEditorAnimation(); + await clearEditor(); + ToastEvent.show('Note Saved!', 'success'); + return true; + } else { + tapCount = 1; + setTimeout(() => { + tapCount = 0; + }, 3000); + ToastEvent.show('Press back again to exit editor', 'success'); + return true; + } + }; + + useEffect(() => { + editing.currentlyEditing = true; + + let handleBack; + if (!noMenu && DDS.isTab) { + handleBack = BackHandler.addEventListener('hardwareBackPress', () => { + simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN()); + editing.isFullscreen = false; + return true; + }); + } else if (!DDS.isTab) { + handleBack = BackHandler.addEventListener( + 'hardwareBackPress', + _onHardwareBackPress, + ); + } else { + if (handleBack) { + handleBack.remove(); + handleBack = null; + } + } + + return () => { + editing.currentlyEditing = false; + if (handleBack) { + handleBack.remove(); + handleBack = null; + } + title = null; + content = null; + id = null; + timer = null; + note = {}; + }; + }, [noMenu]); + + useEffect(() => { + noMenu ? null : SideMenuEvent.disable(); + + return () => { + if (noMenu) return; + DDS.isTab ? SideMenuEvent.open() : null; + SideMenuEvent.enable(); + }; + }); + + return ( + { {noMenu ? null : ( { + onPress={async () => { if (DDS.isTab) { simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN()); } else { - tapCount = 0; - title = null; - content = null; - note = null; - id = null; - - NavigationService.goBack(); + exitEditorAnimation(); + await clearEditor(); + ToastEvent.show('Note Saved!', 'success'); } }} style={{ @@ -401,6 +453,7 @@ const Editor = ({navigation, noMenu}) => { onPress={() => { eSendEvent(eOpenFullscreenEditor); setFullscreen(true); + editing.isFullscreen = true; post( JSON.stringify({ type: 'nomenu', @@ -468,7 +521,7 @@ const Editor = ({navigation, noMenu}) => { (EditorWebView = ref)} + ref={EditorWebView} onError={error => console.log(error)} onLoad={onWebViewLoad} javaScriptEnabled={true} @@ -510,133 +563,7 @@ const Editor = ({navigation, noMenu}) => { onMessage={_onMessage} /> - ); - }; - - const closeFullscreen = () => { - setFullscreen(false); - }; - - // EFFECTS - - useEffect(() => { - eSubscribeEvent(eCloseFullscreenEditor, closeFullscreen); - - return () => { - eUnSubscribeEvent(eCloseFullscreenEditor, closeFullscreen); - }; - }); - - useEffect(() => { - let handleBack; - if (!noMenu && DDS.isTab) { - handleBack = BackHandler.addEventListener('hardwareBackPress', () => { - simpleDialogEvent(TEMPLATE_EXIT_FULLSCREEN()); - return true; - }); - } else if (!DDS.isTab) { - handleBack = BackHandler.addEventListener('hardwareBackPress', () => { - console.log('tapCOunt', tapCount); - if (tapCount > 0) { - tapCount = 0; - title = null; - content = null; - note = null; - id = null; - return false; - } else { - tapCount = 1; - setTimeout(() => { - tapCount = 0; - }, 3000); - ToastEvent.show('Press back again to exit editor', 'success'); - return true; - } - }); - } else { - console.log(' I RUN EVERYTIME'); - if (handleBack) { - handleBack.remove(); - handleBack = null; - } - } - - return () => { - if (handleBack) { - handleBack.remove(); - handleBack = null; - } - title = null; - content = null; - id = null; - timer = null; - note = {}; - }; - }, [noMenu]); - - useEffect(() => { - noMenu ? null : SideMenuEvent.disable(); - - return () => { - if (noMenu) return; - DDS.isTab ? SideMenuEvent.open() : null; - SideMenuEvent.enable(); - }; - }); - - useEffect(() => { - EditorWebView.reload(); - }, [colors]); - - return ( - - - - - - Write with confidence. - - - - - {_renderEditor()} - - + ); }; diff --git a/apps/mobile/src/views/Favorites/index.js b/apps/mobile/src/views/Favorites/index.js index 746d4def2..ae2b3a925 100644 --- a/apps/mobile/src/views/Favorites/index.js +++ b/apps/mobile/src/views/Favorites/index.js @@ -10,19 +10,24 @@ import {useTracked} from '../../provider'; import {ACTIONS} from '../../provider/actions'; import {eSendEvent} from '../../services/eventManager'; import {eScrollEvent} from '../../services/events'; -import {ToastEvent} from '../../utils/utils'; +import {ToastEvent, w} from '../../utils/utils'; +import {useIsFocused} from 'react-navigation-hooks'; export const Favorites = ({navigation}) => { const [state, dispatch] = useTracked(); const {colors, selectionMode, favorites} = state; const [refreshing, setRefreshing] = useState(false); + const isFocused = useIsFocused(); + useEffect(() => { - dispatch({ - type: ACTIONS.CURRENT_SCREEN, - screen: 'favorites', - }); - dispatch({type: ACTIONS.FAVORITES}); - }, []); + if (isFocused) { + dispatch({ + type: ACTIONS.CURRENT_SCREEN, + screen: 'favorites', + }); + dispatch({type: ACTIONS.FAVORITES}); + } + }, [isFocused]); const onScroll = event => { let y = event.nativeEvent.contentOffset.y; diff --git a/apps/mobile/src/views/Folders/index.js b/apps/mobile/src/views/Folders/index.js index f51edd1b0..f8251b734 100644 --- a/apps/mobile/src/views/Folders/index.js +++ b/apps/mobile/src/views/Folders/index.js @@ -34,45 +34,37 @@ export const Folders = ({navigation}) => { const [refreshing, setRefreshing] = useState(false); let isFocused = useIsFocused(); - /// - const handleBackPress = () => { alert('here'); return true; }; + useEffect(() => { + if (isFocused) { + dispatch({type: ACTIONS.PINNED}); + dispatch({type: ACTIONS.NOTEBOOKS}); + dispatch({ + type: ACTIONS.CURRENT_SCREEN, + screen: 'notebooks', + }); + } + }, [isFocused]); + useEffect(() => { eSendEvent(eScrollEvent, 0); - dispatch({type: ACTIONS.NOTEBOOKS}); - - if (isFocused) { - if (isFocused) { - dispatch({ - type: ACTIONS.CURRENT_SCREEN, - screen: 'notebooks', - }); - } - } - let backhandler; - if (isFocused) { - backhandler = BackHandler.addEventListener( - 'hardwareBackPress', - handleBackPress, - ); - } else { - if (backhandler) { - backhandler.remove(); - backhandler = null; - } - } + + backhandler = BackHandler.addEventListener( + 'hardwareBackPress', + handleBackPress, + ); return () => { if (!backhandler) return; backhandler.remove(); backhandler = null; }; - }, [isFocused]); + }, []); const params = navigation.state.params; @@ -134,38 +126,36 @@ export const Folders = ({navigation}) => { ? 155 : 155 - 60, }}> - {pinned && pinned.length > 0 ? ( + {pinned && pinned.notebooks && pinned.notebooks.length > 0 ? ( <> item.id.toString()} renderItem={({item, index}) => item.type === 'notebook' ? ( - - { - dispatch({ - type: ACTIONS.SELECTION_MODE, - enabled: !selectionMode, - }); - dispatch({ - type: ACTIONS.SELECTED_ITEMS, - item: item, - }); - }} - noteToMove={params.note} - item={item} - pinned={true} - index={index} - colors={colors} - /> - + {}} + noteToMove={params.note} + item={item} + pinned={true} + index={index} + colors={colors} + /> ) : null } /> @@ -174,31 +164,29 @@ export const Folders = ({navigation}) => { } ListEmptyComponent={ - pinned && pinned.length > 0 ? null : ( - - - - + + + + - - Notebooks you add will appear here - - - ) + + Notebooks you add will appear here + + } contentContainerStyle={{ width: '100%', diff --git a/apps/mobile/src/views/Notebook/index.js b/apps/mobile/src/views/Notebook/index.js index 127794dec..b107322e8 100644 --- a/apps/mobile/src/views/Notebook/index.js +++ b/apps/mobile/src/views/Notebook/index.js @@ -35,7 +35,6 @@ export const Notebook = ({navigation}) => { allTopics = db.notebooks.notebook(navigation.state.params.notebook.id).data .topics; - console.log(allTopics); notebook = db.notebooks.notebook(navigation.state.params.notebook.id); @@ -67,13 +66,15 @@ export const Notebook = ({navigation}) => { type: ACTIONS.CURRENT_SCREEN, screen: 'notebook', }); + onLoad(); } - + }, [isFocused]); + useEffect(() => { eSubscribeEvent(eMoveNoteDialogNavigateBack, handleBackPress); return () => { eUnSubscribeEvent(eMoveNoteDialogNavigateBack, handleBackPress); }; - }, [isFocused]); + }, []); const onScroll = event => { let y = event.nativeEvent.contentOffset.y; diff --git a/apps/mobile/src/views/Notes/index.js b/apps/mobile/src/views/Notes/index.js index c00787de7..06cea7a3a 100644 --- a/apps/mobile/src/views/Notes/index.js +++ b/apps/mobile/src/views/Notes/index.js @@ -18,6 +18,7 @@ import {ToastEvent} from '../../utils/utils'; import {eSendEvent} from '../../services/eventManager'; import {eScrollEvent} from '../../services/events'; import {NotesPlaceHolder} from '../../components/ListPlaceholders'; +import {useIsFocused} from 'react-navigation-hooks'; export const Notes = ({navigation}) => { const [state, dispatch] = useTracked(); @@ -25,6 +26,7 @@ export const Notes = ({navigation}) => { const allNotes = state.notes; const [notes, setNotes] = useState([]); const [refreshing, setRefreshing] = useState(false); + const isFocused = useIsFocused(); let params = navigation.state ? navigation.state.params : null; useEffect(() => { @@ -34,6 +36,15 @@ export const Notes = ({navigation}) => { }; } }, []); + useEffect(() => { + if (isFocused) { + init(); + dispatch({ + type: ACTIONS.CURRENT_SCREEN, + screen: params.type, + }); + } + }, [isFocused, allNotes, colorNotes]); const init = () => { eSendEvent(eScrollEvent, 0); @@ -49,18 +60,12 @@ export const Notes = ({navigation}) => { .notebook(params.notebookId) .topics.topic(params.title).all; - console.log(allNotes, 'here getting topics'); - if (allNotes && allNotes.length > 0) { setNotes(allNotes); } } }; - useEffect(() => { - init(); - }, [allNotes, colorNotes]); - const _renderItem = ({item, index}) => ( { const [state, dispatch] = useTracked(); const {colors, tags, selectionMode} = state; const [refreshing, setRefreshing] = useState(false); + const isFocused = useIsFocused(); useEffect(() => { - dispatch({type: ACTIONS.TAGS}); - dispatch({ - type: ACTIONS.CURRENT_SCREEN, - screen: 'tags', - }); - }, []); + if (isFocused) { + dispatch({type: ACTIONS.TAGS}); + dispatch({ + type: ACTIONS.CURRENT_SCREEN, + screen: 'tags', + }); + } + }, [isFocused]); return ( { const [state, dispatch] = useTracked(); const {colors, selectionMode, trash} = state; const [refreshing, setRefreshing] = useState(false); - useEffect(() => { - dispatch({ - type: ACTIONS.TRASH, - }); + const isFocused = useIsFocused(); - dispatch({ - type: ACTIONS.CURRENT_SCREEN, - screen: 'trash', - }); - }, []); + useEffect(() => { + if (isFocused) { + dispatch({ + type: ACTIONS.TRASH, + }); + + dispatch({ + type: ACTIONS.CURRENT_SCREEN, + screen: 'trash', + }); + } + }, [isFocused]); const _renderItem = ({item, index}) => (