From 67797d527d4397695510c92259e5b6f4400d2012 Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Thu, 24 Oct 2019 12:53:46 +0300 Subject: [PATCH 01/11] Changed markdown component with react hooks --- .../view/formats/applyListFormat.js | 9 +- .../view/formats/applyWebLinkFormat.js | 8 +- .../view/formats/applyWrapFormat.js | 8 +- .../view/formats/applyWrapFormatNewLines.js | 9 +- .../markdownEditor/view/formats/utils.js | 4 +- .../markdownEditor/view/markdownEditorView.js | 368 ++++++++++-------- 6 files changed, 213 insertions(+), 193 deletions(-) diff --git a/src/components/markdownEditor/view/formats/applyListFormat.js b/src/components/markdownEditor/view/formats/applyListFormat.js index 640048213..281be8720 100644 --- a/src/components/markdownEditor/view/formats/applyListFormat.js +++ b/src/components/markdownEditor/view/formats/applyListFormat.js @@ -1,9 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ getState, item, setState }) => { - const states = getState(); - let { text } = states; - const { selection } = states; +export default async ({ text, selection, setText, setNewSelection, item }) => { let newText; let newSelection; @@ -35,6 +32,6 @@ export default async ({ getState, item, setState }) => { newSelection = { start: selection.start + 3, end: selection.start + 3 }; } - await setState({ text: newText, textUpdated: true }); - await setState({ newSelection }); + await setText(newText); + await setNewSelection(newSelection); }; diff --git a/src/components/markdownEditor/view/formats/applyWebLinkFormat.js b/src/components/markdownEditor/view/formats/applyWebLinkFormat.js index 2cf5ff0a7..ff877cae8 100644 --- a/src/components/markdownEditor/view/formats/applyWebLinkFormat.js +++ b/src/components/markdownEditor/view/formats/applyWebLinkFormat.js @@ -3,8 +3,7 @@ import { isStringWebLink, replaceBetween } from './utils'; export const writeUrlTextHere = 'https://example.com'; export const writeTextHereString = 'Text here'; -export default async ({ getState, item, setState, isImage = null }) => { - const { selection, text } = getState(); +export default async ({ text, selection, setText, setNewSelection, item, isImage = null }) => { const imagePrefix = isImage ? '!' : ''; const itemText = item ? item.text : writeTextHereString; const itemUrl = item ? item.url : writeUrlTextHere; @@ -41,7 +40,6 @@ export default async ({ getState, item, setState, isImage = null }) => { } } - await setState({ text: newText, textUpdated: true }, async () => { - await setState({ newSelection }); - }); + await setText(newText); + await setNewSelection(newSelection); }; diff --git a/src/components/markdownEditor/view/formats/applyWrapFormat.js b/src/components/markdownEditor/view/formats/applyWrapFormat.js index d15d99c8e..31ccae3b4 100644 --- a/src/components/markdownEditor/view/formats/applyWrapFormat.js +++ b/src/components/markdownEditor/view/formats/applyWrapFormat.js @@ -1,7 +1,6 @@ import { replaceBetween } from './utils'; -export default ({ getState, item, setState }) => { - const { text, selection } = getState(); +export default async ({ text, selection, setText, setNewSelection, item }) => { const newText = replaceBetween( text, selection, @@ -15,7 +14,6 @@ export default ({ getState, item, setState }) => { newPosition = selection.end + item.wrapper.length * 2; } - setState({ text: newText, textUpdated: true }, () => { - setState({ newSelection: { start: newPosition, end: newPosition } }); - }); + await setText(newText); + await setNewSelection({ start: newPosition, end: newPosition }); }; diff --git a/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js b/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js index 96b188b95..d5fad76a7 100644 --- a/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js +++ b/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js @@ -1,7 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ getState, item, setState }) => { - const { text, selection } = getState(); +export default async ({ text, selection, setText, setNewSelection, item }) => { let newText = replaceBetween( text, selection, @@ -41,8 +40,6 @@ export default async ({ getState, item, setState }) => { )}`, ); } - - await setState({ text: newText, textUpdated: true }, async () => { - await setState({ newSelection: { start: newPosition, end: newPosition } }); - }); + await setText(newText); + await setNewSelection({ start: newPosition, end: newPosition }); }; diff --git a/src/components/markdownEditor/view/formats/utils.js b/src/components/markdownEditor/view/formats/utils.js index 110c3e4a5..aaa3fa062 100644 --- a/src/components/markdownEditor/view/formats/utils.js +++ b/src/components/markdownEditor/view/formats/utils.js @@ -1,9 +1,9 @@ import regexValidator from './webLinkValidator'; -export const replaceBetween = (text: string, selection: Object, what: string) => +export const replaceBetween = (text, selection, what) => text.substring(0, selection.start) + what + text.substring(selection.end); -export const isStringWebLink = (text: string): boolean => { +export const isStringWebLink = text => { const pattern = regexValidator; return pattern.test(text); }; diff --git a/src/components/markdownEditor/view/markdownEditorView.js b/src/components/markdownEditor/view/markdownEditorView.js index 4ceea3c11..74216ee68 100644 --- a/src/components/markdownEditor/view/markdownEditorView.js +++ b/src/components/markdownEditor/view/markdownEditorView.js @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { View, KeyboardAvoidingView, FlatList, Text, Platform, ScrollView } from 'react-native'; import ActionSheet from 'react-native-actionsheet'; import { renderPostBody } from '@esteemapp/esteem-render-helpers'; // Utils -import applyImageLink from './formats/applyWebLinkFormat'; import Formats from './formats/formats'; +import applyImageLink from './formats/applyWebLinkFormat'; // Components import { IconButton } from '../../iconButton'; @@ -16,73 +16,65 @@ import { TextInput } from '../../textInput'; // Styles import styles from './markdownEditorStyles'; -export default class MarkdownEditorView extends Component { - constructor(props) { - super(props); - this.state = { - text: props.draftBody || '', - selection: { start: 0, end: 0 }, - textUpdated: false, - newSelection: null, - }; +const MarkdownEditorView = ({ + draftBody, + handleIsFormValid, + handleOpenImagePicker, + intl, + isPreviewActive, + isReply, + isLoading, + initialFields, + onChange, + handleOnTextChange, + handleIsValid, + componentID, + uploadedImage, +}) => { + const [text, setText] = useState(draftBody || ''); + const [selection, setSelection] = useState({ start: 0, end: 0 }); - this.inputRef = React.createRef(); - this.galleryRef = React.createRef(); - this.clearRef = React.createRef(); - } + const [newSelection, setNewSelection] = useState(null); - // Lifecycle functions - UNSAFE_componentWillReceiveProps(nextProps) { - const { draftBody, uploadedImage, isPreviewActive } = this.props; - if (!nextProps.isPreviewActive && isPreviewActive) { - this.setState({ - selection: { start: 0, end: 0 }, - }); - } - if (nextProps.draftBody && draftBody !== nextProps.draftBody) { - this.setState({ - text: nextProps.draftBody, - }); - } + const inputRef = useRef(null); + const galleryRef = useRef(null); + const clearRef = useRef(null); - if ( - nextProps.uploadedImage && - nextProps.uploadedImage.url && - nextProps.uploadedImage !== uploadedImage - ) { + useEffect(() => { + setSelection({ start: 0, end: 0 }); + }, [isPreviewActive]); + + useEffect(() => { + if (uploadedImage && uploadedImage.url) { applyImageLink({ - getState: this._getState, - setState: async (state, callback) => { - await this.setState(state, callback); - }, - item: { url: nextProps.uploadedImage.url, text: nextProps.uploadedImage.hash }, - isImage: !!nextProps.uploadedImage, + text, + selection, + setText, + setNewSelection, + item: { url: uploadedImage.url, text: uploadedImage.hash }, + isImage: !!uploadedImage, }); } - } + }, [uploadedImage]); - componentDidUpdate(prevProps, prevState) { - const { text } = this.state; - const { handleIsFormValid } = this.props; + useEffect(() => { + setText(draftBody); + }, [draftBody]); - if (prevState.text !== text) { - const nextText = text.replace(prevState.text, ''); + useEffect(() => { + const nextText = text.replace(text, ''); - if (nextText && nextText.length > 0) { - this._changeText(text); + if (nextText && nextText.length > 0) { + _changeText(text); - if (handleIsFormValid) { - handleIsFormValid(text); - } + if (handleIsFormValid) { + handleIsFormValid(text); } } - } + }, [text]); - // Component functions - _changeText = input => { - const { onChange, handleOnTextChange, handleIsValid, componentID } = this.props; - - this.setState({ text: input }); + const _changeText = input => { + setText(input); if (onChange) { onChange(input); @@ -97,36 +89,22 @@ export default class MarkdownEditorView extends Component { } }; - _handleOnSelectionChange = event => { - const { newSelection } = this.state; - + const _handleOnSelectionChange = event => { if (newSelection) { - this.setState({ - selection: newSelection, - newSelection: null, - }); + setSelection(newSelection); + setNewSelection(null); return; } - this.setState({ - selection: event.nativeEvent.selection, - }); + setSelection(event.nativeEvent.selection); }; - _getState = () => { - return this.state; - }; + const _renderPreview = () => ( + + {text ? : ...} + + ); - _renderPreview = () => { - const { text } = this.state; - - return ( - - {text ? : ...} - - ); - }; - - _renderMarkupButton = ({ item, getState, setState }) => ( + const _renderMarkupButton = ({ item }) => ( item.onPress({ getState, setState, item })} + onPress={() => item.onPress({ text, selection, setText, setNewSelection, item })} /> ); - _renderEditorButtons = ({ getState, setState }) => ( + const _renderEditorButtons = () => ( - index !== 9 && this._renderMarkupButton({ item, getState, setState }) - } + renderItem={({ item, index }) => index !== 9 && _renderMarkupButton({ item })} horizontal /> @@ -158,10 +134,10 @@ export default class MarkdownEditorView extends Component { iconStyle={styles.icon} iconType="FontAwesome" name="link" - onPress={() => Formats[9].onPress({ getState, setState })} + onPress={() => Formats[9].onPress({ text, selection, setText, setNewSelection })} /> this.galleryRef.current.show()} + onPress={() => galleryRef.current.show()} style={styles.rightIcons} size={20} iconStyle={styles.icon} @@ -170,7 +146,7 @@ export default class MarkdownEditorView extends Component { /> this.clearRef.current.show()} + onPress={() => clearRef.current.show()} size={20} iconStyle={styles.clearIcon} iconType="FontAwesome" @@ -182,87 +158,141 @@ export default class MarkdownEditorView extends Component { ); - _handleClear = () => { - const { initialFields } = this.props; - - initialFields(); - - this.setState({ text: '' }); + const _handleClear = index => { + if (index === 0) { + initialFields(); + setText(''); + } }; - render() { - const { handleOpenImagePicker, intl, isPreviewActive, isReply, isLoading } = this.props; - const { text, selection } = this.state; + return ( + + {!isPreviewActive ? ( + + ) : ( + _renderPreview() + )} + {!isPreviewActive && _renderEditorButtons()} + { + handleOpenImagePicker(index === 0 ? 'image' : index === 1 && 'camera'); + }} + /> + + + ); +}; - return ( - - {!isPreviewActive ? ( - - ) : ( - this._renderPreview() - )} - {!isPreviewActive && - this._renderEditorButtons({ - getState: this._getState, - setState: (state, callback) => { - this.inputRef.current.focus(); - this.setState(state, callback); - }, - })} - { - handleOpenImagePicker(index === 0 ? 'image' : index === 1 && 'camera'); - }} - /> - index === 0 && this._handleClear()} - /> - - ); - } -} +export default MarkdownEditorView; +// class MarkdownEditorView extends Component { +// constructor(props) { +// super(props); +// this.state = { +// text: props.draftBody || '', +// selection: { start: 0, end: 0 }, +// textUpdated: false, +// newSelection: null, +// }; + +// this.inputRef = React.createRef(); +// this.galleryRef = React.createRef(); +// this.clearRef = React.createRef(); +// } + +// // Lifecycle functions +// UNSAFE_componentWillReceiveProps(nextProps) { +// const { draftBody, uploadedImage, isPreviewActive } = this.props; +// if (!nextProps.isPreviewActive && isPreviewActive) { +// this.setState({ +// selection: { start: 0, end: 0 }, +// }); +// } +// if (nextProps.draftBody && draftBody !== nextProps.draftBody) { +// this.setState({ +// text: nextProps.draftBody, +// }); +// } + +// if ( +// nextProps.uploadedImage && +// nextProps.uploadedImage.url && +// nextProps.uploadedImage !== uploadedImage +// ) { +// applyImageLink({ +// getState: this._getState, +// setState: async (state, callback) => { +// await this.setState(state, callback); +// }, +// item: { url: nextProps.uploadedImage.url, text: nextProps.uploadedImage.hash }, +// isImage: !!nextProps.uploadedImage, +// }); +// } +// } + +// componentDidUpdate(prevProps, prevState) { +// const { text } = this.state; +// const { handleIsFormValid } = this.props; + +// if (prevState.text !== text) { +// const nextText = text.replace(prevState.text, ''); + +// if (nextText && nextText.length > 0) { +// this._changeText(text); + +// if (handleIsFormValid) { +// handleIsFormValid(text); +// } +// } +// } +// } + +// // Component functions + +// } From 4abe42035bf1eff419579021448187914c5ff5df Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Mon, 28 Oct 2019 13:20:10 +0300 Subject: [PATCH 02/11] Fixed #1144 and android crash issues --- .../view/formats/applyListFormat.js | 3 +- .../view/formats/applyWebLinkFormat.js | 11 +- .../view/formats/applyWrapFormat.js | 3 +- .../view/formats/applyWrapFormatNewLines.js | 3 +- .../markdownEditor/view/markdownEditorView.js | 111 ++++++------------ 5 files changed, 55 insertions(+), 76 deletions(-) diff --git a/src/components/markdownEditor/view/formats/applyListFormat.js b/src/components/markdownEditor/view/formats/applyListFormat.js index 281be8720..7368d7426 100644 --- a/src/components/markdownEditor/view/formats/applyListFormat.js +++ b/src/components/markdownEditor/view/formats/applyListFormat.js @@ -1,6 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ text, selection, setText, setNewSelection, item }) => { +export default async ({ text, selection, setText, setNewSelection, setSelection, item }) => { let newText; let newSelection; @@ -34,4 +34,5 @@ export default async ({ text, selection, setText, setNewSelection, item }) => { await setText(newText); await setNewSelection(newSelection); + await setSelection(newSelection); }; diff --git a/src/components/markdownEditor/view/formats/applyWebLinkFormat.js b/src/components/markdownEditor/view/formats/applyWebLinkFormat.js index ff877cae8..25244fdfb 100644 --- a/src/components/markdownEditor/view/formats/applyWebLinkFormat.js +++ b/src/components/markdownEditor/view/formats/applyWebLinkFormat.js @@ -3,7 +3,15 @@ import { isStringWebLink, replaceBetween } from './utils'; export const writeUrlTextHere = 'https://example.com'; export const writeTextHereString = 'Text here'; -export default async ({ text, selection, setText, setNewSelection, item, isImage = null }) => { +export default async ({ + text, + selection, + setText, + setSelection, + setNewSelection, + item, + isImage = null, +}) => { const imagePrefix = isImage ? '!' : ''; const itemText = item ? item.text : writeTextHereString; const itemUrl = item ? item.url : writeUrlTextHere; @@ -42,4 +50,5 @@ export default async ({ text, selection, setText, setNewSelection, item, isImage await setText(newText); await setNewSelection(newSelection); + await setSelection(newSelection); }; diff --git a/src/components/markdownEditor/view/formats/applyWrapFormat.js b/src/components/markdownEditor/view/formats/applyWrapFormat.js index 31ccae3b4..e4f448eb5 100644 --- a/src/components/markdownEditor/view/formats/applyWrapFormat.js +++ b/src/components/markdownEditor/view/formats/applyWrapFormat.js @@ -1,6 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ text, selection, setText, setNewSelection, item }) => { +export default async ({ text, selection, setText, setNewSelection, setSelection, item }) => { const newText = replaceBetween( text, selection, @@ -16,4 +16,5 @@ export default async ({ text, selection, setText, setNewSelection, item }) => { await setText(newText); await setNewSelection({ start: newPosition, end: newPosition }); + await setSelection({ start: newPosition, end: newPosition }); }; diff --git a/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js b/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js index d5fad76a7..1021c0054 100644 --- a/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js +++ b/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js @@ -1,6 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ text, selection, setText, setNewSelection, item }) => { +export default async ({ text, selection, setText, setSelection, setNewSelection, item }) => { let newText = replaceBetween( text, selection, @@ -42,4 +42,5 @@ export default async ({ text, selection, setText, setNewSelection, item }) => { } await setText(newText); await setNewSelection({ start: newPosition, end: newPosition }); + await setSelection({ start: newPosition, end: newPosition }); }; diff --git a/src/components/markdownEditor/view/markdownEditorView.js b/src/components/markdownEditor/view/markdownEditorView.js index 74216ee68..9cc452c7f 100644 --- a/src/components/markdownEditor/view/markdownEditorView.js +++ b/src/components/markdownEditor/view/markdownEditorView.js @@ -32,7 +32,9 @@ const MarkdownEditorView = ({ uploadedImage, }) => { const [text, setText] = useState(draftBody || ''); + const [textChanged, setTextChanged] = useState(false); const [selection, setSelection] = useState({ start: 0, end: 0 }); + const [selectionArray, setSelectionArray] = useState(false); // Workaround for android selection const [newSelection, setNewSelection] = useState(null); @@ -75,6 +77,11 @@ const MarkdownEditorView = ({ const _changeText = input => { setText(input); + setTextChanged(true); + setSelectionArray([]); + setTimeout(() => { + setTextChanged(false); + }, 100); if (onChange) { onChange(input); @@ -89,13 +96,34 @@ const MarkdownEditorView = ({ } }; - const _handleOnSelectionChange = event => { + const _handleOnSelectionChange = async event => { + if (textChanged) { + if (selectionArray.length > 0) { + return; + } + selectionArray.push(event.nativeEvent.selection); + setSelectionArray(selectionArray); + } if (newSelection) { - setSelection(newSelection); - setNewSelection(null); + setTimeout(() => { + setNewSelection(null); + }, 100); return; } - setSelection(event.nativeEvent.selection); + if ( + selection.start === event.nativeEvent.selection.start && + selection.end === event.nativeEvent.selection.end + ) { + return; + } + await setSelection(event.nativeEvent.selection); + }; + + const _getSelection = () => { + if (selection.start > text.length || selection.end > text.length) { + return { start: text.length, end: text.length }; + } + return selection; }; const _renderPreview = () => ( @@ -112,7 +140,9 @@ const MarkdownEditorView = ({ iconStyle={styles.icon} iconType={item.iconType} name={item.icon} - onPress={() => item.onPress({ text, selection, setText, setNewSelection, item })} + onPress={() => + item.onPress({ text, selection, setText, setNewSelection, setSelection, item }) + } /> ); @@ -134,7 +164,9 @@ const MarkdownEditorView = ({ iconStyle={styles.icon} iconType="FontAwesome" name="link" - onPress={() => Formats[9].onPress({ text, selection, setText, setNewSelection })} + onPress={() => + Formats[9].onPress({ text, selection, setText, setNewSelection, setSelection }) + } /> galleryRef.current.show()} @@ -180,7 +212,7 @@ const MarkdownEditorView = ({ id: isReply ? 'editor.reply_placeholder' : 'editor.default_placeholder', })} placeholderTextColor="#c1c5c7" - selection={selection} + selection={_getSelection()} selectionColor="#357ce6" style={styles.textWrapper} underlineColorAndroid="transparent" @@ -231,68 +263,3 @@ const MarkdownEditorView = ({ }; export default MarkdownEditorView; -// class MarkdownEditorView extends Component { -// constructor(props) { -// super(props); -// this.state = { -// text: props.draftBody || '', -// selection: { start: 0, end: 0 }, -// textUpdated: false, -// newSelection: null, -// }; - -// this.inputRef = React.createRef(); -// this.galleryRef = React.createRef(); -// this.clearRef = React.createRef(); -// } - -// // Lifecycle functions -// UNSAFE_componentWillReceiveProps(nextProps) { -// const { draftBody, uploadedImage, isPreviewActive } = this.props; -// if (!nextProps.isPreviewActive && isPreviewActive) { -// this.setState({ -// selection: { start: 0, end: 0 }, -// }); -// } -// if (nextProps.draftBody && draftBody !== nextProps.draftBody) { -// this.setState({ -// text: nextProps.draftBody, -// }); -// } - -// if ( -// nextProps.uploadedImage && -// nextProps.uploadedImage.url && -// nextProps.uploadedImage !== uploadedImage -// ) { -// applyImageLink({ -// getState: this._getState, -// setState: async (state, callback) => { -// await this.setState(state, callback); -// }, -// item: { url: nextProps.uploadedImage.url, text: nextProps.uploadedImage.hash }, -// isImage: !!nextProps.uploadedImage, -// }); -// } -// } - -// componentDidUpdate(prevProps, prevState) { -// const { text } = this.state; -// const { handleIsFormValid } = this.props; - -// if (prevState.text !== text) { -// const nextText = text.replace(prevState.text, ''); - -// if (nextText && nextText.length > 0) { -// this._changeText(text); - -// if (handleIsFormValid) { -// handleIsFormValid(text); -// } -// } -// } -// } - -// // Component functions - -// } From 06d959d793211899b479aad2afd274b18fa671bb Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Mon, 28 Oct 2019 22:16:58 +0300 Subject: [PATCH 03/11] Merged with development --- ios/Podfile.lock | 10 ++- .../UserInterfaceState.xcuserstate | Bin 40328 -> 40625 bytes package.json | 1 + .../placeHolder/listItemPlaceHolderView.js | 52 +++++------ .../view/placeHolder/listPlaceHolderView.js | 8 +- .../bottomTabBar/view/bottomTabBarView.js | 63 ++++++++------ src/components/bottomTabBar/view/tabbar.js | 21 ++--- .../markdownEditor/view/markdownEditorView.js | 38 ++++---- .../container/searchModalContainer.js | 81 +++++++++++++----- .../container/votersDisplayContainer.js | 36 -------- src/components/votersDisplay/index.js | 5 +- .../votersDisplay/view/votersDisplayStyles.js | 1 + .../votersDisplay/view/votersDisplayView.js | 80 +++++++++-------- src/containers/darkThemeContainer.js | 14 +++ src/containers/index.js | 4 + src/containers/redeemContainer.js | 1 - src/containers/themeContainer.js | 18 ++++ .../container/applicationContainer.js | 16 +++- .../container/notificationContainer.js | 4 +- src/screens/reblogs/screen/reblogScreen.js | 13 ++- src/screens/voters/screen/votersScreen.js | 12 ++- src/utils/postUrlParser.js | 75 ++++++++++++++++ yarn.lock | 34 ++++++++ 23 files changed, 382 insertions(+), 205 deletions(-) delete mode 100644 src/components/votersDisplay/container/votersDisplayContainer.js create mode 100644 src/containers/darkThemeContainer.js create mode 100644 src/containers/themeContainer.js create mode 100644 src/utils/postUrlParser.js diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e68b0dc5d..86342f777 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -277,6 +277,8 @@ PODS: - React-cxxreact (= 0.61.2) - React-jsi (= 0.61.2) - ReactCommon/jscallinvoker (= 0.61.2) + - ReactNativeDarkMode (0.1.2): + - React - RNGestureHandler (1.4.1): - React - RNIap (3.4.15): @@ -341,6 +343,7 @@ DEPENDENCIES: - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - ReactCommon/jscallinvoker (from `../node_modules/react-native/ReactCommon`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - ReactNativeDarkMode (from `../node_modules/react-native-dark-mode`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNIap (from `../node_modules/react-native-iap`) - RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`) @@ -351,7 +354,7 @@ DEPENDENCIES: - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: - https://github.com/cocoapods/specs.git: + https://github.com/CocoaPods/Specs.git: - AppCenter - AppCenterReactNativeShared - boost-for-react-native @@ -433,6 +436,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/Vibration" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + ReactNativeDarkMode: + :path: "../node_modules/react-native-dark-mode" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNIap: @@ -492,6 +497,7 @@ SPEC CHECKSUMS: React-RCTText: e3ef6191cdb627855ff7fe8fa0c1e14094967fb8 React-RCTVibration: fb54c732fd20405a76598e431aa2f8c2bf527de9 ReactCommon: 5848032ed2f274fcb40f6b9ec24067787c42d479 + ReactNativeDarkMode: 315535c6f7a066bc4e8ba60591a63aafa16d5dca RNGestureHandler: 4cb47a93019c1a201df2644413a0a1569a51c8aa RNIap: b4c77c8bc4501203f4b743126a05da23f10f40b4 RNImageCropPicker: bfb3ea9c8622f290532e2fe63f369e0d5a52f597 @@ -505,4 +511,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 4faf3202c73803d0ba69b4aaf79ce8642ecf11b2 -COCOAPODS: 1.7.5 +COCOAPODS: 1.8.3 diff --git a/ios/eSteem.xcworkspace/xcuserdata/ue.xcuserdatad/UserInterfaceState.xcuserstate b/ios/eSteem.xcworkspace/xcuserdata/ue.xcuserdatad/UserInterfaceState.xcuserstate index 0943eb020114df1b684d323bda17454ddbf75e2b..0779ce063714d275f9db76c65ed4475e1e35dbf1 100644 GIT binary patch delta 23142 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 R=OYP!2HyJ5uK&}5{txI+Nw)w1 delta 22655 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 Pe~oOqg2#rxll%TZ6lQ(_ diff --git a/package.json b/package.json index f6e606786..a0cbabcdc 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-native-autocomplete-input": "^4.1.0", "react-native-code-push": "esteemapp/react-native-code-push", "react-native-config": "luggit/react-native-config#master", + "react-native-dark-mode": "^0.1.2", "react-native-datepicker": "^1.7.2", "react-native-extended-stylesheet": "^0.10.0", "react-native-fast-image": "^4.0.14", diff --git a/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderView.js b/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderView.js index 807143c46..15a198e66 100644 --- a/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderView.js +++ b/src/components/basicUIElements/view/placeHolder/listItemPlaceHolderView.js @@ -1,34 +1,38 @@ import React from 'react'; -import { connect } from 'react-redux'; import { View } from 'react-native'; import Placeholder from 'rn-placeholder'; +import { ThemeContainer } from '../../../../containers'; + import styles from './listItemPlaceHolderStyles'; -const ListItemPlaceHolderView = ({ isDarkTheme }) => { - const color = isDarkTheme ? '#2e3d51' : '#f5f5f5'; - +const ListItemPlaceHolderView = () => { return ( - - - - - - + + {({ isDarkTheme }) => ( + + + + + + + )} + ); }; -const mapStateToProps = state => ({ - isDarkTheme: state.application.isDarkTheme, -}); - -export default connect(mapStateToProps)(ListItemPlaceHolderView); +export default ListItemPlaceHolderView; diff --git a/src/components/basicUIElements/view/placeHolder/listPlaceHolderView.js b/src/components/basicUIElements/view/placeHolder/listPlaceHolderView.js index 4aea8d528..66478b5d0 100644 --- a/src/components/basicUIElements/view/placeHolder/listPlaceHolderView.js +++ b/src/components/basicUIElements/view/placeHolder/listPlaceHolderView.js @@ -1,6 +1,5 @@ /* eslint-disable radix */ import React, { Fragment } from 'react'; -import { connect } from 'react-redux'; import { Dimensions } from 'react-native'; import times from 'lodash/times'; @@ -18,9 +17,4 @@ const ListPlaceHolderView = () => { return {listElements}; }; - -const mapStateToProps = state => ({ - isDarkTheme: state.application.isDarkTheme, -}); - -export default connect(mapStateToProps)(ListPlaceHolderView); +export default ListPlaceHolderView; diff --git a/src/components/bottomTabBar/view/bottomTabBarView.js b/src/components/bottomTabBar/view/bottomTabBarView.js index efdc7ee03..93749f4f1 100644 --- a/src/components/bottomTabBar/view/bottomTabBarView.js +++ b/src/components/bottomTabBar/view/bottomTabBarView.js @@ -9,6 +9,9 @@ import { updateActiveBottomTab } from '../../../redux/actions/uiAction'; // Constants import ROUTES from '../../../constants/routeNames'; +// Container +import { DarkThemeContainer } from '../../../containers'; + // Components import TabBar from './tabbar'; @@ -38,36 +41,40 @@ const BottomTabBarView = ({ useEffect(() => { dispatch(updateActiveBottomTab(routes[index].routeName)); - }, [dispatch, routes, index]); + }, [dispatch, index, routes]); return ( - - _jumpTo(routes[i], index, routes, jumpTo)} - activeTintColor={activeTintColor} - inactiveTintColor={inactiveTintColor} - > - {routes.map(route => ( - - ))} - - + + {({ isDarkTheme }) => ( + + _jumpTo(routes[i], index, routes, jumpTo)} + activeTintColor={activeTintColor} + inactiveTintColor={inactiveTintColor} + > + {routes.map(route => ( + + ))} + + + )} + ); }; diff --git a/src/components/bottomTabBar/view/tabbar.js b/src/components/bottomTabBar/view/tabbar.js index ff4c65919..ccb63de69 100644 --- a/src/components/bottomTabBar/view/tabbar.js +++ b/src/components/bottomTabBar/view/tabbar.js @@ -36,7 +36,6 @@ export default class TabBar extends Component { circleRadius: new Animated.Value(91 + selectedIndex * value), pathD: new Animated.Value(selectedIndex * value), pathX: selectedIndex * value, - showIcon: true, animateConstant: value, }; @@ -72,15 +71,16 @@ export default class TabBar extends Component { _move = index => { const { animateConstant, pathD, circleRadius } = this.state; - this.setState({ - selectedIndex: index, - showIcon: false, - }); + this.setState({ selectedIndex: '' }); Animated.timing(pathD, { toValue: 0 + index * animateConstant, duration: 450, - }).start(() => this.setState({ showIcon: true })); + }).start(() => { + setTimeout(() => { + this.setState({ selectedIndex: index }); + }, 350); + }); Animated.timing(circleRadius, { toValue: 91 + index * animateConstant, }).start(); @@ -88,20 +88,19 @@ export default class TabBar extends Component { render() { const { children, backgroundColor, circleBackgroundColor, style } = this.props; - const { selectedIndex, showIcon, pathX, circleRadius } = this.state; + const { selectedIndex, pathX, circleRadius } = this.state; return ( {children.map((route, i) => { - const element = React.cloneElement(route, { + return React.cloneElement(route, { selected: selectedIndex === i, onPress: this._onPress, key: i, index: i, - showIcon, + showIcon: true, }); - return element; })} @@ -148,8 +147,10 @@ const TabBarItem = ({ icon, selectedIcon, index, selected, onPress, showIcon, di ); } + return ; } + return ( {!isPreviewActive ? ( - + + {({ isDarkTheme }) => ( + + )} + ) : ( _renderPreview() )} diff --git a/src/components/searchModal/container/searchModalContainer.js b/src/components/searchModal/container/searchModalContainer.js index 46a79c619..31975211f 100644 --- a/src/components/searchModal/container/searchModalContainer.js +++ b/src/components/searchModal/container/searchModalContainer.js @@ -5,13 +5,14 @@ import get from 'lodash/get'; // Services and Actions import { search } from '../../../providers/esteem/esteem'; -import { lookupAccounts, getTrendingTags } from '../../../providers/steem/dsteem'; +import { lookupAccounts, getTrendingTags, getPurePost } from '../../../providers/steem/dsteem'; // Constants -import { default as ROUTES } from '../../../constants/routeNames'; +import ROUTES from '../../../constants/routeNames'; // Utilities import { getResizedAvatar } from '../../../utils/image'; +import postUrlParser from '../../../utils/postUrlParser'; // Component import SearchModalView from '../view/searchModalView'; @@ -41,31 +42,67 @@ class SearchModalContainer extends PureComponent { _handleOnChangeSearchInput = text => { const { isConnected } = this.props; + if (text && text.length < 2) return; if (this.timer) { clearTimeout(this.timer); } if (!isConnected) return; - this.timer = setTimeout(() => { - if (text && text !== '@' && text !== '#') { - if (text[0] === '@') { - lookupAccounts(text.substr(1)).then(res => { - const users = res.map(item => ({ - image: getResizedAvatar(item), - text: item, - ...item, - })); - this.setState({ searchResults: { type: 'user', data: users } }); - }); - } else if (text[0] === '#') { - getTrendingTags(text.substr(1)).then(res => { - const tags = res.map(item => ({ - text: `#${get(item, 'name', '')}`, - ...item, - })); + if (text && text !== '@' && text !== '#') { + if (text[0] === '@') { + lookupAccounts(text.substr(1)).then(res => { + const users = res.map(item => ({ + image: getResizedAvatar(item), + text: item, + ...item, + })); + this.setState({ searchResults: { type: 'user', data: users } }); + }); + } else if (text[0] === '#') { + getTrendingTags(text.substr(1)).then(res => { + const tags = res.map(item => ({ + text: `#${get(item, 'name', '')}`, + ...item, + })); - this.setState({ searchResults: { type: 'tag', data: tags } }); - }); + this.setState({ searchResults: { type: 'tag', data: tags } }); + }); + } else if (text.includes('https')) { + const postUrl = postUrlParser(text.replace(/\s/g, '')); + + if (postUrl) { + const { author, permlink } = postUrl; + + if (author) { + if (permlink) { + getPurePost(author, permlink).then(post => { + if (post.id !== 0) { + const result = {}; + const metadata = JSON.parse(get(post, 'json_metadata', '')); + if (get(metadata, 'image', false) && metadata.image.length > 0) { + result.image = metadata.image[0]; + } else { + result.image = getResizedAvatar(author); + } + result.author = author; + result.text = post.title; + result.permlink = permlink; + this.setState({ searchResults: { type: 'content', data: [result] } }); + } else { + this.setState({ searchResults: { type: 'content', data: [] } }); + } + }); + } else { + lookupAccounts(author).then(res => { + const users = res.map(item => ({ + image: getResizedAvatar(item), + text: item, + ...item, + })); + this.setState({ searchResults: { type: 'user', data: users } }); + }); + } + } } else { search({ q: text }).then(res => { res.results = res.results @@ -79,7 +116,7 @@ class SearchModalContainer extends PureComponent { }); } } - }, 500); + } }; _handleOnPressListItem = (type, item) => { diff --git a/src/components/votersDisplay/container/votersDisplayContainer.js b/src/components/votersDisplay/container/votersDisplayContainer.js deleted file mode 100644 index 81d47a6ca..000000000 --- a/src/components/votersDisplay/container/votersDisplayContainer.js +++ /dev/null @@ -1,36 +0,0 @@ -import React, { Component } from 'react'; -import { withNavigation } from 'react-navigation'; - -// Constants -import ROUTES from '../../../constants/routeNames'; - -// Component -import VotersDisplayView from '../view/votersDisplayView'; - -/* - * Props Name Description Value - *@props --> props name here description here Value Type Here - * - */ - -class VotersDisplayContainer extends Component { - _handleOnUserPress = username => { - const { navigation } = this.props; - - navigation.navigate({ - routeName: ROUTES.SCREENS.PROFILE, - params: { - username, - }, - key: username, - }); - }; - - render() { - const { votes } = this.props; - - return ; - } -} - -export default withNavigation(VotersDisplayContainer); diff --git a/src/components/votersDisplay/index.js b/src/components/votersDisplay/index.js index e291ead0a..393bd3168 100644 --- a/src/components/votersDisplay/index.js +++ b/src/components/votersDisplay/index.js @@ -1,5 +1,4 @@ -import VotersDisplayView from './view/votersDisplayView'; -import VotersDisplay from './container/votersDisplayContainer'; +import VotersDisplay from './view/votersDisplayView'; -export { VotersDisplay, VotersDisplayView }; +export { VotersDisplay }; export default VotersDisplay; diff --git a/src/components/votersDisplay/view/votersDisplayStyles.js b/src/components/votersDisplay/view/votersDisplayStyles.js index b0bb0a797..761151b11 100644 --- a/src/components/votersDisplay/view/votersDisplayStyles.js +++ b/src/components/votersDisplay/view/votersDisplayStyles.js @@ -2,6 +2,7 @@ import EStyleSheet from 'react-native-extended-stylesheet'; export default EStyleSheet.create({ container: { + flex: 1, padding: 8, flexDirection: 'row', height: '$deviceHeight - 150', diff --git a/src/components/votersDisplay/view/votersDisplayView.js b/src/components/votersDisplay/view/votersDisplayView.js index 32a680315..dff5406a6 100644 --- a/src/components/votersDisplay/view/votersDisplayView.js +++ b/src/components/votersDisplay/view/votersDisplayView.js @@ -1,27 +1,37 @@ -import React, { Component } from 'react'; -import { View, FlatList, Text } from 'react-native'; -import { injectIntl } from 'react-intl'; +import React from 'react'; +import { SafeAreaView, FlatList, Text } from 'react-native'; +import { withNavigation } from 'react-navigation'; +import { useIntl } from 'react-intl'; // Utils import { getTimeFromNow } from '../../../utils/time'; // Components import { UserListItem } from '../../basicUIElements'; + +// Constants +import ROUTES from '../../../constants/routeNames'; + // Styles import styles from './votersDisplayStyles'; -class VotersDisplayView extends Component { - /* Props - * ------------------------------------------------ - * @prop { type } name - Description.... - */ +const VotersDisplayView = ({ votes, navigation }) => { + const intl = useIntl(); - // Component Functions - _renderItem = (item, index) => { - const { handleOnUserPress } = this.props; + const _handleOnUserPress = username => { + navigation.navigate({ + routeName: ROUTES.SCREENS.PROFILE, + params: { + username, + }, + key: username, + }); + }; + + const _renderItem = (item, index) => { const value = `$ ${item.value}`; const percent = `${item.percent}%`; - + console.log(item); return ( handleOnUserPress(item.voter)} + handleOnPress={() => _handleOnUserPress(item.voter)} isClickable subRightText={percent} /> ); }; - render() { - const { votes, intl } = this.props; + return ( + + {votes && votes.length > 0 ? ( + item.voter} + removeClippedSubviews={false} + renderItem={({ item, index }) => _renderItem(item, index)} + /> + ) : ( + + {intl.formatMessage({ + id: 'voters.no_user', + })} + + )} + + ); +}; - return ( - - {votes.length > 0 ? ( - item.voter} - removeClippedSubviews={false} - renderItem={({ item, index }) => this._renderItem(item, index)} - /> - ) : ( - - {intl.formatMessage({ - id: 'voters.no_user', - })} - - )} - - ); - } -} - -export default injectIntl(VotersDisplayView); +export default withNavigation(VotersDisplayView); diff --git a/src/containers/darkThemeContainer.js b/src/containers/darkThemeContainer.js new file mode 100644 index 000000000..17a52101a --- /dev/null +++ b/src/containers/darkThemeContainer.js @@ -0,0 +1,14 @@ +import { React } from 'react'; +import { connect } from 'react-redux'; + +const DarkThemeContainer = ({ children, isDarkTheme }) => + children && + children({ + isDarkTheme, + }); + +const mapStateToProps = state => ({ + isDarkTheme: state.application.isDarkTheme, +}); + +export default connect(mapStateToProps)(DarkThemeContainer); diff --git a/src/containers/index.js b/src/containers/index.js index 3d725da0a..d946950dd 100644 --- a/src/containers/index.js +++ b/src/containers/index.js @@ -1,3 +1,4 @@ +import DarkThemeContainer from './darkThemeContainer'; import InAppPurchaseContainer from './inAppPurchaseContainer'; import PointsContainer from './pointsContainer'; import ProfileContainer from './profileContainer'; @@ -5,8 +6,10 @@ import ProfileEditContainer from './profileEditContainer'; import RedeemContainer from './redeemContainer'; import SpinGameContainer from './spinGameContainer'; import TransferContainer from './transferContainer'; +import ThemeContainer from './themeContainer'; export { + DarkThemeContainer, InAppPurchaseContainer, PointsContainer, ProfileContainer, @@ -14,4 +17,5 @@ export { RedeemContainer, SpinGameContainer, TransferContainer, + ThemeContainer, }; diff --git a/src/containers/redeemContainer.js b/src/containers/redeemContainer.js index 7a6383a6c..9e326c3ef 100644 --- a/src/containers/redeemContainer.js +++ b/src/containers/redeemContainer.js @@ -140,7 +140,6 @@ class RedeemContainer extends Component { const mapStateToProps = state => ({ username: state.account.currentAccount.name, - isDarkTheme: state.application.isDarkTheme, activeBottomTab: state.ui.activeBottomTab, isConnected: state.application.isConnected, accounts: state.account.otherAccounts, diff --git a/src/containers/themeContainer.js b/src/containers/themeContainer.js new file mode 100644 index 000000000..22a24adff --- /dev/null +++ b/src/containers/themeContainer.js @@ -0,0 +1,18 @@ +/* eslint-disable no-unused-vars */ +import React from 'react'; +import { connect } from 'react-redux'; + +const ThemeContainer = ({ children, isDarkTheme }) => { + return ( + children && + children({ + isDarkTheme, + }) + ); +}; + +const mapStateToProps = state => ({ + isDarkTheme: state.application.isDarkTheme, +}); + +export default connect(mapStateToProps)(ThemeContainer); diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index 97e0c8190..aab3efdae 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -1,3 +1,4 @@ +/* eslint-disable curly */ import { Component } from 'react'; import { Platform, BackHandler, Alert, Linking, AppState } from 'react-native'; import NetInfo from '@react-native-community/netinfo'; @@ -12,6 +13,10 @@ import { NavigationActions } from 'react-navigation'; import { bindActionCreators } from 'redux'; import EStyleSheet from 'react-native-extended-stylesheet'; import { forEach, isEmpty, some } from 'lodash'; +import { + initialMode as nativeThemeInitialMode, + eventEmitter as nativeThemeEventEmitter, +} from 'react-native-dark-mode'; // Constants import AUTH_TYPE from '../../../constants/authType'; @@ -96,7 +101,6 @@ class ApplicationContainer extends Component { componentDidMount = () => { const { isIos } = this.state; - this._setNetworkListener(); if (!isIos) BackHandler.addEventListener('hardwareBackPress', this._onBackPress); @@ -111,6 +115,14 @@ class ApplicationContainer extends Component { setPreviousAppState(); this._createPushListener(); + + if (nativeThemeEventEmitter) { + nativeThemeEventEmitter.on('currentModeChanged', newMode => { + const { dispatch } = this.props; + + dispatch(isDarkTheme(newMode === 'dark')); + }); + } }; UNSAFE_componentWillReceiveProps(nextProps) { @@ -527,7 +539,7 @@ class ApplicationContainer extends Component { const settings = await getSettings(); if (settings) { - if (settings.isDarkTheme !== '') dispatch(isDarkTheme(settings.isDarkTheme)); + dispatch(isDarkTheme(nativeThemeInitialMode === 'dark' || settings.isDarkTheme)); if (settings.isPinCodeOpen !== '') dispatch(isPinCodeOpen(settings.isPinCodeOpen)); if (settings.language !== '') dispatch(setLanguage(settings.language)); if (settings.server !== '') dispatch(setApi(settings.server)); diff --git a/src/screens/notification/container/notificationContainer.js b/src/screens/notification/container/notificationContainer.js index 7814506b0..79136338b 100644 --- a/src/screens/notification/container/notificationContainer.js +++ b/src/screens/notification/container/notificationContainer.js @@ -151,13 +151,12 @@ class NotificationContainer extends Component { render() { const { isLoggedIn } = this.props; - const { notifications, isNotificationRefreshing, isDarkTheme } = this.state; + const { notifications, isNotificationRefreshing } = this.state; return ( ({ isLoggedIn: state.application.isLoggedIn, - isDarkTheme: state.application.isDarkTheme, isConnected: state.application.isConnected, username: state.account.currentAccount.name, diff --git a/src/screens/reblogs/screen/reblogScreen.js b/src/screens/reblogs/screen/reblogScreen.js index 1710a2f07..c9618d7b1 100644 --- a/src/screens/reblogs/screen/reblogScreen.js +++ b/src/screens/reblogs/screen/reblogScreen.js @@ -1,12 +1,12 @@ import React from 'react'; -import { View, FlatList } from 'react-native'; +import { View, FlatList, SafeAreaView } from 'react-native'; import { useIntl } from 'react-intl'; - -// Constants +import get from 'lodash/get'; // Components import { BasicHeader, UserListItem } from '../../../components'; +// Container import AccountListContainer from '../../../containers/accountListContainer'; // Utils @@ -31,13 +31,12 @@ const ReblogScreen = ({ navigation }) => { id: 'reblog.title', }); - const activeVotes = - navigation.state && navigation.state.params && navigation.state.params.reblogs; + const activeVotes = get(navigation, 'state.params.reblogs'); return ( {({ data, filterResult, handleSearch, handleOnUserPress }) => ( - + { removeClippedSubviews={false} renderItem={({ item, index }) => renderUserListItem(item, index, handleOnUserPress)} /> - + )} ); diff --git a/src/screens/voters/screen/votersScreen.js b/src/screens/voters/screen/votersScreen.js index 1f16886a6..14229ef31 100644 --- a/src/screens/voters/screen/votersScreen.js +++ b/src/screens/voters/screen/votersScreen.js @@ -1,8 +1,7 @@ import React from 'react'; -import { View } from 'react-native'; +import { SafeAreaView } from 'react-native'; import { useIntl } from 'react-intl'; - -// Constants +import get from 'lodash/get'; // Components import { BasicHeader, FilterBar, VotersDisplay } from '../../../components'; @@ -20,13 +19,12 @@ const VotersScreen = ({ navigation }) => { id: 'voters.voters_info', }); - const activeVotes = - navigation.state && navigation.state.params && navigation.state.params.activeVotes; + const activeVotes = get(navigation, 'state.params.activeVotes'); return ( {({ data, filterResult, handleOnVotersDropdownSelect, handleSearch }) => ( - + { onDropdownSelect={handleOnVotersDropdownSelect} /> - + )} ); diff --git a/src/utils/postUrlParser.js b/src/utils/postUrlParser.js new file mode 100644 index 000000000..871b863ed --- /dev/null +++ b/src/utils/postUrlParser.js @@ -0,0 +1,75 @@ +export default url => { + const parseCatAuthorPermlink = u => { + const postRegex = /^https?:\/\/(.*)\/(.*)\/(@[\w.\d-]+)\/(.*)/i; + const postMatch = u.match(postRegex); + if (postMatch && postMatch.length === 5) { + return { + author: postMatch[3].replace('@', ''), + permlink: postMatch[4], + }; + } + const authorRegex = /^https?:\/\/(.*)\/(.*)\/(@[\w.\d-]+)/i; + const authorMatch = u.match(authorRegex); + if (authorMatch && authorMatch.length === 4) { + return { + author: authorMatch[3].replace('@', ''), + permlink: null, + }; + } + return null; + }; + + const parseAuthorPermlink = u => { + const r = /^https?:\/\/(.*)\/(@[\w.\d-]+)\/(.*)/i; + const match = u.match(r); + if (match && match.length === 4) { + return { + author: match[2].replace('@', ''), + permlink: match[3], + }; + } + const authorRegex = /^https?:\/\/(.*)\/(@[\w.\d-]+)/i; + const authorMatch = u.match(authorRegex); + if (authorMatch && authorMatch.length === 3) { + return { + author: authorMatch[2].replace('@', ''), + permlink: null, + }; + } + + return null; + }; + + if ( + ['https://esteem.app', 'https://steemit.com', 'https://steempeak.com'].some(x => + url.startsWith(x), + ) + ) { + return parseCatAuthorPermlink(url); + } + + if (['https://busy.org', 'https://steemhunt.com'].some(x => url.startsWith(x))) { + return parseAuthorPermlink(url); + } + + // For non urls like @good-karma/esteem-london-presentation-e3105ba6637ed + let match = url.match(/^[/]?(@[\w.\d-]+)\/(.*)/); + if (match && match.length === 3) { + return { + author: match[1].replace('@', ''), + permlink: match[2], + }; + } + + // For non urls with category like esteem/@good-karma/esteem-london-presentation-e3105ba6637ed + match = url.match(/^[/]?([\w.\d-]+)\/(@[\w.\d-]+)\/(.*)/); + if (match && match.length === 4) { + return { + category: match[1], + author: match[2].replace('@', ''), + permlink: match[3], + }; + } + + return null; +}; diff --git a/yarn.lock b/yarn.lock index ae3c3ac35..f9148e61a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1476,6 +1476,11 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + "@types/hoist-non-react-statics@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -1519,6 +1524,14 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/react-native@*": + version "0.60.21" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.21.tgz#81a41cae7b232f52ab3983d854f4a0b0df79531e" + integrity sha512-E7F+P/UG4Utu+kh8Hy544i0m4CzpHw1awNX6hVfkhlu4mXSlAn6KLZzKEkPBbHm7g1kspmZTiuP23HAKZpASPw== + dependencies: + "@types/prop-types" "*" + "@types/react" "*" + "@types/react@*": version "16.9.5" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.5.tgz#079dabd918b19b32118c25fd00a786bb6d0d5e51" @@ -3854,6 +3867,11 @@ events@^1.1.0: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -7669,6 +7687,17 @@ react-native-config@luggit/react-native-config#master: version "0.11.7" resolved "https://codeload.github.com/luggit/react-native-config/tar.gz/89a602bf8be3808838403a97afaf915caeec76c2" +react-native-dark-mode@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/react-native-dark-mode/-/react-native-dark-mode-0.1.2.tgz#4afd760073d5eb02bb83fc09004733d036e9f2d8" + integrity sha512-oCanZ5LBJvhmgNCG2uPtvKRfBZ85nBCz/2UHkizXDt3t7ep1Vp994REye/RkIW86bbpPH/Dt8QUHExw5R0ftpg== + dependencies: + "@types/events" "*" + "@types/react" "*" + "@types/react-native" "*" + events "^3.0.0" + toolkit.ts "^0.0.2" + react-native-datepicker@^1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/react-native-datepicker/-/react-native-datepicker-1.7.2.tgz#58d0822591a0ac9b32aba082650222a0ee29669d" @@ -9325,6 +9354,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toolkit.ts@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/toolkit.ts/-/toolkit.ts-0.0.2.tgz#91bde730e5e6ad1a22146cdaf83f4a52721cf3b2" + integrity sha512-yJJTVbCwiD6AfFgReewJCGJuODmyZUeL1sDjnxp33t0UBxnezgQrLbz/F9++RC28CTlk5u5pVji4TbeondYEkw== + tough-cookie@^2.3.3, tough-cookie@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" From 1043af2b0452c1037f01fd15cb9ef33e57837f5f Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Tue, 29 Oct 2019 16:16:44 +0300 Subject: [PATCH 04/11] Fixed auto navigate action after login, changed navigation actions with navigation service --- src/components/userAvatar/view/userAvatarView.js | 9 +++------ src/index.js | 7 +------ src/navigation/service.js | 14 ++++++++++---- .../application/container/applicationContainer.js | 9 +++------ .../application/screen/applicationScreen.js | 7 ++++++- src/screens/pinCode/container/pinCodeContainer.js | 14 ++++---------- 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/components/userAvatar/view/userAvatarView.js b/src/components/userAvatar/view/userAvatarView.js index 37dcdd398..7c6e17699 100644 --- a/src/components/userAvatar/view/userAvatarView.js +++ b/src/components/userAvatar/view/userAvatarView.js @@ -1,10 +1,10 @@ import React, { Component } from 'react'; import { TouchableOpacity } from 'react-native'; import { connect } from 'react-redux'; -import { NavigationActions } from 'react-navigation'; import FastImage from 'react-native-fast-image'; import styles from './userAvatarStyles'; +import NavigationService from '../../../navigation/service'; // Constants import ROUTES from '../../../constants/routeNames'; @@ -30,21 +30,18 @@ class UserAvatarView extends Component { // Component Functions _handleOnAvatarPress = username => { const { - dispatch, currentUsername: { name }, } = this.props; const routeName = name === username ? ROUTES.TABBAR.PROFILE : ROUTES.SCREENS.PROFILE; - const navigateAction = NavigationActions.navigate({ - routeName, + NavigationService.navigate({ + routeName: routeName, params: { username, }, key: username, - action: NavigationActions.navigate({ routeName }), }); - dispatch(navigateAction); }; render() { diff --git a/src/index.js b/src/index.js index b4a8bdc2e..f4b57b5a6 100755 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,6 @@ import { Provider, connect } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import { IntlProvider } from 'react-intl'; import { useScreens } from 'react-native-screens'; -import { setTopLevelNavigator } from './navigation/service'; import { flattenMessages } from './utils/flattenMessages'; import messages from './config/locales'; @@ -30,11 +29,7 @@ const App = connect(mapStateToProps)(_renderApp); export default () => { return ( - { - setTopLevelNavigator(navigatorRef); - }} - /> + ); }; diff --git a/src/navigation/service.js b/src/navigation/service.js index 8cec6cc7c..6e424c6fb 100644 --- a/src/navigation/service.js +++ b/src/navigation/service.js @@ -2,15 +2,21 @@ import { NavigationActions } from 'react-navigation'; let _navigator; -export const setTopLevelNavigator = navigatorRef => { +const setTopLevelNavigator = navigatorRef => { _navigator = navigatorRef; }; -export const navigate = (routeName, params) => { +const navigate = navigationProps => { _navigator.dispatch( NavigationActions.navigate({ - routeName, - params, + ...navigationProps, }), ); }; + +// add other navigation functions that you need and export them + +export default { + navigate, + setTopLevelNavigator, +}; diff --git a/src/screens/application/container/applicationContainer.js b/src/screens/application/container/applicationContainer.js index aab3efdae..bfb091680 100644 --- a/src/screens/application/container/applicationContainer.js +++ b/src/screens/application/container/applicationContainer.js @@ -38,6 +38,7 @@ import { import { getUser, getPost } from '../../../providers/steem/dsteem'; import { switchAccount } from '../../../providers/steem/auth'; import { setPushToken } from '../../../providers/esteem/esteem'; +import NavigationService from '../../../navigation/service'; // Actions import { @@ -260,13 +261,11 @@ class ApplicationContainer extends Component { if (routeName && (profile || content)) { this.navigationTimeout = setTimeout(() => { clearTimeout(this.navigationTimeout); - const navigateAction = NavigationActions.navigate({ + NavigationService.navigate({ routeName, params, key: permlink || author, - action: NavigationActions.navigate({ routeName }), }); - dispatch(navigateAction); }, 2000); } }; @@ -402,13 +401,11 @@ class ApplicationContainer extends Component { } if (!some(params, isEmpty)) { - const navigateAction = NavigationActions.navigate({ + NavigationService.navigate({ routeName, params, key, - action: NavigationActions.navigate({ routeName }), }); - dispatch(navigateAction); } } }, diff --git a/src/screens/application/screen/applicationScreen.js b/src/screens/application/screen/applicationScreen.js index d71e0a721..74f11ac0c 100644 --- a/src/screens/application/screen/applicationScreen.js +++ b/src/screens/application/screen/applicationScreen.js @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import { createAppContainer } from 'react-navigation'; import AppNavitation from '../../../navigation/routes'; +import NavigationService from '../../../navigation/service'; // Services import { toastNotification as toastNotificationAction } from '../../../redux/actions/uiAction'; @@ -59,7 +60,11 @@ class ApplicationScreen extends Component { )} {!isConnected && } - + { + NavigationService.setTopLevelNavigator(navigatorRef); + }} + /> {isShowToastNotification && ( diff --git a/src/screens/pinCode/container/pinCodeContainer.js b/src/screens/pinCode/container/pinCodeContainer.js index 4548cec71..cc78bbf67 100644 --- a/src/screens/pinCode/container/pinCodeContainer.js +++ b/src/screens/pinCode/container/pinCodeContainer.js @@ -3,10 +3,10 @@ import { Alert } from 'react-native'; import { connect } from 'react-redux'; import { injectIntl } from 'react-intl'; import Config from 'react-native-config'; -import { NavigationActions } from 'react-navigation'; import get from 'lodash/get'; // Actions & Services +import NavigationService from '../../../navigation/service'; import { setUserDataWithPinCode, verifyPinCode, @@ -111,12 +111,10 @@ class PinCodeContainer extends Component { if (callback) callback(pin, oldPinCode); dispatch(closePinCodeModal()); if (navigateTo) { - const navigateAction = NavigationActions.navigate({ + NavigationService.navigate({ routeName: navigateTo, params: navigateParams, - action: NavigationActions.navigate({ routeName: navigateTo }), }); - dispatch(navigateAction); } resolve(); }); @@ -174,12 +172,10 @@ class PinCodeContainer extends Component { if (callback) callback(pin, oldPinCode); dispatch(closePinCodeModal()); if (navigateTo) { - const navigateAction = NavigationActions.navigate({ + NavigationService.navigate({ routeName: navigateTo, params: navigateParams, - action: NavigationActions.navigate({ routeName: navigateTo }), }); - dispatch(navigateAction); } resolve(); }); @@ -215,12 +211,10 @@ class PinCodeContainer extends Component { dispatch(closePinCodeModal()); if (callback) callback(pin, oldPinCode); if (navigateTo) { - const navigateAction = NavigationActions.navigate({ + NavigationService.navigate({ routeName: navigateTo, params: navigateParams, - action: NavigationActions.navigate({ routeName: navigateTo }), }); - dispatch(navigateAction); } }) .catch(err => { From a1e8112e1f8e05bdcfa27ec9c4359482e85dc615 Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Wed, 30 Oct 2019 20:37:14 +0300 Subject: [PATCH 05/11] Fixed #820, fixed #1144 --- .../view/formats/applyListFormat.js | 6 +- .../view/formats/applyWebLinkFormat.js | 14 +--- .../view/formats/applyWrapFormat.js | 8 +- .../view/formats/applyWrapFormatNewLines.js | 8 +- .../markdownEditor/view/markdownEditorView.js | 83 +++++++++---------- 5 files changed, 52 insertions(+), 67 deletions(-) diff --git a/src/components/markdownEditor/view/formats/applyListFormat.js b/src/components/markdownEditor/view/formats/applyListFormat.js index 7368d7426..4f6868b2f 100644 --- a/src/components/markdownEditor/view/formats/applyListFormat.js +++ b/src/components/markdownEditor/view/formats/applyListFormat.js @@ -1,6 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ text, selection, setText, setNewSelection, setSelection, item }) => { +export default async ({ text, selection, setTextAndSelection, item }) => { let newText; let newSelection; @@ -32,7 +32,5 @@ export default async ({ text, selection, setText, setNewSelection, setSelection, newSelection = { start: selection.start + 3, end: selection.start + 3 }; } - await setText(newText); - await setNewSelection(newSelection); - await setSelection(newSelection); + setTextAndSelection({ text: newText, selection: newSelection }); }; diff --git a/src/components/markdownEditor/view/formats/applyWebLinkFormat.js b/src/components/markdownEditor/view/formats/applyWebLinkFormat.js index 25244fdfb..24866d162 100644 --- a/src/components/markdownEditor/view/formats/applyWebLinkFormat.js +++ b/src/components/markdownEditor/view/formats/applyWebLinkFormat.js @@ -3,15 +3,7 @@ import { isStringWebLink, replaceBetween } from './utils'; export const writeUrlTextHere = 'https://example.com'; export const writeTextHereString = 'Text here'; -export default async ({ - text, - selection, - setText, - setSelection, - setNewSelection, - item, - isImage = null, -}) => { +export default async ({ text, selection, setTextAndSelection, item, isImage = null }) => { const imagePrefix = isImage ? '!' : ''; const itemText = item ? item.text : writeTextHereString; const itemUrl = item ? item.url : writeUrlTextHere; @@ -48,7 +40,5 @@ export default async ({ } } - await setText(newText); - await setNewSelection(newSelection); - await setSelection(newSelection); + setTextAndSelection({ text: newText, selection: newSelection }); }; diff --git a/src/components/markdownEditor/view/formats/applyWrapFormat.js b/src/components/markdownEditor/view/formats/applyWrapFormat.js index e4f448eb5..89ccbf318 100644 --- a/src/components/markdownEditor/view/formats/applyWrapFormat.js +++ b/src/components/markdownEditor/view/formats/applyWrapFormat.js @@ -1,6 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ text, selection, setText, setNewSelection, setSelection, item }) => { +export default async ({ text, selection, setTextAndSelection, item }) => { const newText = replaceBetween( text, selection, @@ -13,8 +13,6 @@ export default async ({ text, selection, setText, setNewSelection, setSelection, } else { newPosition = selection.end + item.wrapper.length * 2; } - - await setText(newText); - await setNewSelection({ start: newPosition, end: newPosition }); - await setSelection({ start: newPosition, end: newPosition }); + const newSelection = { start: newPosition, end: newPosition }; + setTextAndSelection({ text: newText, selection: newSelection }); }; diff --git a/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js b/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js index 1021c0054..871eb5c78 100644 --- a/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js +++ b/src/components/markdownEditor/view/formats/applyWrapFormatNewLines.js @@ -1,6 +1,6 @@ import { replaceBetween } from './utils'; -export default async ({ text, selection, setText, setSelection, setNewSelection, item }) => { +export default async ({ text, selection, setTextAndSelection, item }) => { let newText = replaceBetween( text, selection, @@ -40,7 +40,7 @@ export default async ({ text, selection, setText, setSelection, setNewSelection, )}`, ); } - await setText(newText); - await setNewSelection({ start: newPosition, end: newPosition }); - await setSelection({ start: newPosition, end: newPosition }); + + const newSelection = { start: newPosition, end: newPosition }; + setTextAndSelection({ text: newText, selection: newSelection }); }; diff --git a/src/components/markdownEditor/view/markdownEditorView.js b/src/components/markdownEditor/view/markdownEditorView.js index c3113a665..0ed208102 100644 --- a/src/components/markdownEditor/view/markdownEditorView.js +++ b/src/components/markdownEditor/view/markdownEditorView.js @@ -34,27 +34,43 @@ const MarkdownEditorView = ({ uploadedImage, }) => { const [text, setText] = useState(draftBody || ''); - const [textChanged, setTextChanged] = useState(false); const [selection, setSelection] = useState({ start: 0, end: 0 }); - const [selectionArray, setSelectionArray] = useState(false); // Workaround for android selection - - const [newSelection, setNewSelection] = useState(null); + const [editable, setEditable] = useState(null); const inputRef = useRef(null); const galleryRef = useRef(null); const clearRef = useRef(null); useEffect(() => { - setSelection({ start: 0, end: 0 }); + if (!isPreviewActive) { + _setTextAndSelection({ selection: { start: 0, end: 0 }, text }); + } }, [isPreviewActive]); + useEffect(() => { + if (text === '' && draftBody !== '') { + _setTextAndSelection({ selection: { start: 0, end: 0 }, text: draftBody }); + } + }, [draftBody]); + + useEffect(() => { + if (editable === null) { + // workaround for android context menu issue + setEditable(false); + setTimeout(() => { + setEditable(!isLoading); + }, 100); + } else { + setEditable(!isLoading); + } + }, [isLoading]); + useEffect(() => { if (uploadedImage && uploadedImage.url) { applyImageLink({ text, selection, - setText, - setNewSelection, + setTextAndSelection: _setTextAndSelection, item: { url: uploadedImage.url, text: uploadedImage.hash }, isImage: !!uploadedImage, }); @@ -79,11 +95,6 @@ const MarkdownEditorView = ({ const _changeText = input => { setText(input); - setTextChanged(true); - setSelectionArray([]); - setTimeout(() => { - setTextChanged(false); - }, 100); if (onChange) { onChange(input); @@ -99,33 +110,21 @@ const MarkdownEditorView = ({ }; const _handleOnSelectionChange = async event => { - if (textChanged) { - if (selectionArray.length > 0) { - return; - } - selectionArray.push(event.nativeEvent.selection); - setSelectionArray(selectionArray); - } - if (newSelection) { - setTimeout(() => { - setNewSelection(null); - }, 100); - return; - } - if ( - selection.start === event.nativeEvent.selection.start && - selection.end === event.nativeEvent.selection.end - ) { - return; - } - await setSelection(event.nativeEvent.selection); + setSelection(event.nativeEvent.selection); }; - const _getSelection = () => { - if (selection.start > text.length || selection.end > text.length) { - return { start: text.length, end: text.length }; - } - return selection; + const _setTextAndSelection = ({ selection: _selection, text: _text }) => { + inputRef.current.setNativeProps({ + text: _text, + }); + // Workaround for iOS selection update issue + setTimeout(() => { + inputRef.current.setNativeProps({ + selection: _selection, + }); + setSelection(_selection); + }, 200); + _changeText(_text); }; const _renderPreview = () => ( @@ -143,7 +142,7 @@ const MarkdownEditorView = ({ iconType={item.iconType} name={item.icon} onPress={() => - item.onPress({ text, selection, setText, setNewSelection, setSelection, item }) + item.onPress({ text, selection, setTextAndSelection: _setTextAndSelection, item }) } /> @@ -167,7 +166,7 @@ const MarkdownEditorView = ({ iconType="FontAwesome" name="link" onPress={() => - Formats[9].onPress({ text, selection, setText, setNewSelection, setSelection }) + Formats[9].onPress({ text, selection, setTextAndSelection: _setTextAndSelection }) } /> )} From 527b5e14e1e9e71af3a762e2165023683fc0c0f5 Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Thu, 31 Oct 2019 12:13:08 +0300 Subject: [PATCH 06/11] Changed wallet icon with profile icon --- src/components/bottomTabBar/view/bottomTabBarStyles.js | 2 +- src/navigation/baseNavigator.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/bottomTabBar/view/bottomTabBarStyles.js b/src/components/bottomTabBar/view/bottomTabBarStyles.js index b1f716359..be73eeab6 100644 --- a/src/components/bottomTabBar/view/bottomTabBarStyles.js +++ b/src/components/bottomTabBar/view/bottomTabBarStyles.js @@ -16,7 +16,7 @@ export default EStyleSheet.create({ zIndex: 1, position: 'absolute', bottom: 0, - marginHorizontal: 19, + marginHorizontal: 20, justifyContent: 'space-between', }, navItem: { diff --git a/src/navigation/baseNavigator.js b/src/navigation/baseNavigator.js index 6af0acd63..780be1a76 100644 --- a/src/navigation/baseNavigator.js +++ b/src/navigation/baseNavigator.js @@ -52,7 +52,7 @@ const BaseNavigator = createBottomTabNavigator( screen: Profile, navigationOptions: () => ({ tabBarIcon: ({ tintColor }) => ( - + ), }), }, From 258b7d3111c437607295f28e8d79afd7b561a570 Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Thu, 31 Oct 2019 17:13:29 +0300 Subject: [PATCH 07/11] Increase upvote and threedot icon size --- .../postDropdown/view/postDropdownStyles.js | 1 + src/components/upvote/view/upvoteStyles.js | 3 ++- src/components/upvote/view/upvoteView.js | 14 ++++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/postDropdown/view/postDropdownStyles.js b/src/components/postDropdown/view/postDropdownStyles.js index 89045fbff..ae16574a3 100644 --- a/src/components/postDropdown/view/postDropdownStyles.js +++ b/src/components/postDropdown/view/postDropdownStyles.js @@ -4,5 +4,6 @@ export default EStyleSheet.create({ icon: { color: '$iconColor', marginRight: 2.7, + fontSize: 25, }, }); diff --git a/src/components/upvote/view/upvoteStyles.js b/src/components/upvote/view/upvoteStyles.js index bf7e72451..e68266928 100644 --- a/src/components/upvote/view/upvoteStyles.js +++ b/src/components/upvote/view/upvoteStyles.js @@ -9,8 +9,9 @@ export default EStyleSheet.create({ }, upvoteIcon: { alignSelf: 'center', - fontSize: 20, + fontSize: 24, color: '$primaryBlue', + marginRight: 5, }, popoverSlider: { flexDirection: 'row', diff --git a/src/components/upvote/view/upvoteView.js b/src/components/upvote/view/upvoteView.js index c20afbdcd..db3028b63 100644 --- a/src/components/upvote/view/upvoteView.js +++ b/src/components/upvote/view/upvoteView.js @@ -249,12 +249,14 @@ class UpvoteView extends Component { /> ) : ( - + + + )} From e6eb234d849dfa06e8ad72dec70d4cf98f7d12a7 Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Fri, 1 Nov 2019 13:32:48 +0300 Subject: [PATCH 08/11] Fixed notification disable issue #1134 #1165 --- src/components/posts/view/postsView.js | 7 +-- .../settings/container/settingsContainer.js | 49 +++++++++++-------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/components/posts/view/postsView.js b/src/components/posts/view/postsView.js index 28d7985fc..155c67f09 100644 --- a/src/components/posts/view/postsView.js +++ b/src/components/posts/view/postsView.js @@ -359,12 +359,7 @@ class PostsView extends Component { render() { const { refreshing, posts, isShowFilterBar } = this.state; - const { - filterOptions, - selectedOptionIndex, - isHideImage, - handleImagesHide, - } = this.props; + const { filterOptions, selectedOptionIndex, isHideImage, handleImagesHide } = this.props; return ( diff --git a/src/screens/settings/container/settingsContainer.js b/src/screens/settings/container/settingsContainer.js index 2d74babcc..9a6feb5ef 100644 --- a/src/screens/settings/container/settingsContainer.js +++ b/src/screens/settings/container/settingsContainer.js @@ -236,21 +236,23 @@ class SettingsContainer extends Component { dispatch(changeNotificationSettings({ action, type: actionType })); setNotificationSettings({ action, type: actionType }); - if (actionType === 'notification') { - await Push.setEnabled(action); - this._setPushToken(action ? [1, 2, 3, 4, 5, 6] : notifyTypes); - } else { - Object.keys(notificationDetails).map(item => { - const notificationType = item.replace('Notification', ''); + Object.keys(notificationDetails).map(item => { + const notificationType = item.replace('Notification', ''); - if (notificationType === actionType.replace('notification.', '')) { - if (action) { - notifyTypes.push(notifyTypesConst[notificationType]); - } - } else if (notificationDetails[item]) { + if (notificationType === actionType.replace('notification.', '')) { + if (action) { notifyTypes.push(notifyTypesConst[notificationType]); } - }); + } else if (notificationDetails[item]) { + notifyTypes.push(notifyTypesConst[notificationType]); + } + }); + notifyTypes.sort(); + + if (actionType === 'notification') { + await Push.setEnabled(action); + this._setPushToken(action ? notifyTypes : []); + } else { this._setPushToken(notifyTypes); } }; @@ -286,21 +288,25 @@ class SettingsContainer extends Component { }; _setPushToken = async notifyTypes => { - const { isNotificationSettingsOpen, isLoggedIn, username } = this.props; + const { isLoggedIn, otherAccounts = [] } = this.props; if (isLoggedIn) { const token = await AppCenter.getInstallId(); getExistUser().then(isExistUser => { if (isExistUser) { - const data = { - username, - token, - system: Platform.OS, - allows_notify: Number(isNotificationSettingsOpen), - notify_types: notifyTypes, - }; - setPushToken(data); + otherAccounts.forEach(item => { + const { isNotificationSettingsOpen } = this.props; + + const data = { + username: item.username, + token, + system: Platform.OS, + allows_notify: Number(isNotificationSettingsOpen), + notify_types: notifyTypes, + }; + setPushToken(data); + }); } }); } @@ -401,6 +407,7 @@ const mapStateToProps = state => ({ username: state.account.currentAccount && state.account.currentAccount.name, currentAccount: state.account.currentAccount, + otherAccounts: state.account.otherAccounts, }); export default injectIntl(connect(mapStateToProps)(SettingsContainer)); From e3858f43cdc571b44c440ee35bc25ecd7c0672dc Mon Sep 17 00:00:00 2001 From: Mustafa Buyukcelebi Date: Sat, 2 Nov 2019 12:11:41 +0300 Subject: [PATCH 09/11] Fixed null avatar crash issue. Fixed editor title issue --- src/components/editorElements/titleArea/view/titleAreaView.js | 2 +- src/components/userAvatar/view/userAvatarView.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/editorElements/titleArea/view/titleAreaView.js b/src/components/editorElements/titleArea/view/titleAreaView.js index a2741b1fe..621857734 100644 --- a/src/components/editorElements/titleArea/view/titleAreaView.js +++ b/src/components/editorElements/titleArea/view/titleAreaView.js @@ -48,7 +48,7 @@ export default class TitleAreaView extends Component { const { text, height } = this.state; return ( - + Date: Sat, 2 Nov 2019 15:16:22 +0300 Subject: [PATCH 10/11] Removed memo input from powerup screen, added default value for destination input #1255 --- src/screens/transfer/screen/transferScreen.js | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/screens/transfer/screen/transferScreen.js b/src/screens/transfer/screen/transferScreen.js index 10216d10c..0ec8b0451 100644 --- a/src/screens/transfer/screen/transferScreen.js +++ b/src/screens/transfer/screen/transferScreen.js @@ -31,10 +31,10 @@ class TransferView extends Component { super(props); this.state = { from: props.currentAccountName, - destination: '', + destination: props.transferType === 'powerUp' ? props.currentAccountName : '', amount: '', memo: '', - isUsernameValid: false, + isUsernameValid: props.transferType === 'powerUp' && props.currentAccountName ? true : false, steemConnectTransfer: false, isTransfering: false, }; @@ -217,22 +217,26 @@ class TransferView extends Component { )} /> - - this._renderInput( - intl.formatMessage({ id: 'transfer.memo_placeholder' }), - 'memo', - 'default', - true, - ) - } - /> - - this._renderDescription(intl.formatMessage({ id: 'transfer.memo_desc' })) - } - /> + {transferType !== 'powerUp' && ( + + this._renderInput({ + placeholder: intl.formatMessage({ id: 'transfer.memo_placeholder' }), + state: 'memo', + keyboardType: 'default', + isTextArea: true, + }) + } + /> + )} + {transferType !== 'powerUp' && ( + + this._renderDescription(intl.formatMessage({ id: 'transfer.memo_desc' })) + } + /> + )} Date: Sat, 9 Nov 2019 23:52:29 +0300 Subject: [PATCH 11/11] quick fix for auto correction --- src/components/markdownEditor/view/markdownEditorView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/markdownEditor/view/markdownEditorView.js b/src/components/markdownEditor/view/markdownEditorView.js index 0ed208102..5bd4f5dcc 100644 --- a/src/components/markdownEditor/view/markdownEditorView.js +++ b/src/components/markdownEditor/view/markdownEditorView.js @@ -210,6 +210,7 @@ const MarkdownEditorView = ({ {({ isDarkTheme }) => (