diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index ebc88464b..6a6f6c417 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -828,4 +828,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 0282022703ad578ab2d9afbf3147ba3b373b4311
-COCOAPODS: 1.11.2
+COCOAPODS: 1.11.3
diff --git a/package.json b/package.json
index 41b31a112..f384fbf4e 100644
--- a/package.json
+++ b/package.json
@@ -108,7 +108,7 @@
"react-native-iap": "^7.5.6",
"react-native-image-crop-picker": "^0.35.2",
"react-native-image-zoom-viewer": "^2.2.27",
- "react-native-iphone-x-helper": "^1.3.1",
+ "react-native-iphone-x-helper": "Norcy/react-native-iphone-x-helper",
"react-native-keyboard-aware-scroll-view": "^0.9.1",
"react-native-level-fs": "^3.0.0",
"react-native-linear-gradient": "^2.4.2",
diff --git a/src/components/collapsibleCard/view/collapsibleCardView.js b/src/components/collapsibleCard/view/collapsibleCardView.js
index b83ef42ab..b7ea00f5f 100644
--- a/src/components/collapsibleCard/view/collapsibleCardView.js
+++ b/src/components/collapsibleCard/view/collapsibleCardView.js
@@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
-import { View, TouchableHighlight, Animated } from 'react-native';
+import { View, TouchableHighlight } from 'react-native';
+import Animated, { Easing } from 'react-native-reanimated';
// Constants
@@ -50,6 +51,7 @@ class CollapsibleCardView extends PureComponent {
Animated.timing(this.anime.height, {
toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue() + (moreHeight || 0),
duration: 200,
+ easing: Easing.inOut(Easing.ease),
}).start();
this.anime.expanded = !this.anime.expanded;
diff --git a/src/components/draftListItem/view/draftListItemView.tsx b/src/components/draftListItem/view/draftListItemView.tsx
index 60c5de868..f4a552d2d 100644
--- a/src/components/draftListItem/view/draftListItemView.tsx
+++ b/src/components/draftListItem/view/draftListItemView.tsx
@@ -3,6 +3,7 @@ import { View, Text, TouchableOpacity } from 'react-native';
import { injectIntl } from 'react-intl';
// Utils
+import FastImage from 'react-native-fast-image';
import { getTimeFromNow } from '../../../utils/time';
// Components
@@ -14,8 +15,6 @@ import { OptionsModal } from '../../atoms';
import styles from './draftListItemStyles';
import { ScheduledPostStatus } from '../../../providers/ecency/ecency.types';
import { PopoverWrapper } from '../../popoverWrapper/popoverWrapperView';
-import FastImage from 'react-native-fast-image';
-
const DraftListItemView = ({
title,
@@ -59,24 +58,24 @@ const DraftListItemView = ({
status === ScheduledPostStatus.PENDING
? intl.formatMessage({ id: 'schedules.pending' })
: status === ScheduledPostStatus.POSTPONED
- ? intl.formatMessage({ id: 'schedules.postponed' })
- : status === ScheduledPostStatus.PUBLISHED
- ? intl.formatMessage({ id: 'schedules.published' })
- : intl.formatMessage({ id: 'schedules.error' });
+ ? intl.formatMessage({ id: 'schedules.postponed' })
+ : status === ScheduledPostStatus.PUBLISHED
+ ? intl.formatMessage({ id: 'schedules.published' })
+ : intl.formatMessage({ id: 'schedules.error' });
const statusIcon =
status === ScheduledPostStatus.PENDING
? 'timer'
: status === ScheduledPostStatus.POSTPONED
- ? 'schedule'
- : status === ScheduledPostStatus.PUBLISHED
- ? 'check-circle'
- : 'error';
+ ? 'schedule'
+ : status === ScheduledPostStatus.PUBLISHED
+ ? 'check-circle'
+ : 'error';
const statusIconColor =
status === ScheduledPostStatus.PUBLISHED
? '#4FD688'
: status === ScheduledPostStatus.ERROR
- ? '#e63535'
- : '#c1c5c7';
+ ? '#e63535'
+ : '#c1c5c7';
return (
diff --git a/src/components/iconButton/view/iconButtonView.js b/src/components/iconButton/view/iconButtonView.js
index 359b927ab..f9df79f5d 100644
--- a/src/components/iconButton/view/iconButtonView.js
+++ b/src/components/iconButton/view/iconButtonView.js
@@ -1,5 +1,6 @@
import React, { Fragment } from 'react';
import { TouchableOpacity, ActivityIndicator } from 'react-native';
+import EStyleSheet from 'react-native-extended-stylesheet';
import { Icon } from '../../icon';
import styles from './iconButtonStyles';
@@ -47,7 +48,10 @@ const IconButton = ({
badgeCount={badgeCount}
/>
) : (
-
+
)}
diff --git a/src/components/notification/view/notificationView.tsx b/src/components/notification/view/notificationView.tsx
index 2c2738710..a52c10857 100644
--- a/src/components/notification/view/notificationView.tsx
+++ b/src/components/notification/view/notificationView.tsx
@@ -47,14 +47,13 @@ class NotificationView extends PureComponent {
// Component Functions
_handleOnDropdownSelect = async (index) => {
- const { getActivities, changeSelectedFilter, } = this.props;
+ const { changeSelectedFilter } = this.props;
const { filters, contentOffset } = this.state;
const _selectedFilter = filters[index].key;
this.setState({ selectedFilter: _selectedFilter, selectedIndex: index, contentOffset });
await changeSelectedFilter(_selectedFilter, index);
- getActivities(_selectedFilter, false);
this.listRef.current?.scrollToOffset({ x: 0, y: 0, animated: false });
};
@@ -176,34 +175,40 @@ class NotificationView extends PureComponent {
return 5;
};
-
_getActivityIndicator = () => (
);
-
_renderSectionHeader = ({ section: { title, index } }) => (
- )
-
+ );
_renderItem = ({ item }) => (
<>
- {item.sectionTitle && }
+ {item.sectionTitle && (
+
+ )}
{ this.props.handleOnUserPress(item.source) }}
+ handleOnUserPress={() => {
+ this.props.handleOnUserPress(item.source);
+ }}
globalProps={this.props.globalProps}
/>
>
- )
-
+ );
render() {
- const { readAllNotification, getActivities, isNotificationRefreshing, intl, isLoading } = this.props;
+ const {
+ readAllNotification,
+ getActivities,
+ isNotificationRefreshing,
+ intl,
+ isLoading,
+ } = this.props;
const { filters, selectedFilter, selectedIndex } = this.state;
const _notifications = this._getNotificationsArrays();
@@ -222,27 +227,28 @@ class NotificationView extends PureComponent {
onRightIconPress={readAllNotification}
/>
- {({ isDarkTheme }) =>
-
+ {({ isDarkTheme }) => (
`${item.id}-${index}`}
- onEndReached={() => getActivities(selectedFilter, true)}
+ onEndReached={() => getActivities(true)}
onEndReachedThreshold={0.3}
ListFooterComponent={this._renderFooterLoading}
ListEmptyComponent={
- isLoading ? : (
-
- {intl.formatMessage({ id: 'notification.noactivity' })}
-
+ isNotificationRefreshing ? (
+
+ ) : (
+
+ {intl.formatMessage({ id: 'notification.noactivity' })}
+
)
- }
+ }
contentContainerStyle={styles.listContentContainer}
refreshControl={
getActivities(selectedFilter)}
+ onRefresh={() => getActivities()}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"
@@ -251,8 +257,7 @@ class NotificationView extends PureComponent {
}
renderItem={this._renderItem}
/>
-
- }
+ )}
);
diff --git a/src/components/pinAnimatedInput/views/pinAnimatedInputView.js b/src/components/pinAnimatedInput/views/pinAnimatedInputView.js
index d24a0b160..92ffc3d99 100644
--- a/src/components/pinAnimatedInput/views/pinAnimatedInputView.js
+++ b/src/components/pinAnimatedInput/views/pinAnimatedInputView.js
@@ -33,6 +33,7 @@ class PinAnimatedInput extends Component {
toValue: 1,
duration: 250,
easing: Easing.linear,
+ useNativeDriver: false, //setting it to false as animation is not being used
}),
),
]).start((o) => {
@@ -87,6 +88,5 @@ class PinAnimatedInput extends Component {
);
}
}
-
export default PinAnimatedInput;
/* eslint-enable */
diff --git a/src/components/postBoost/postBoostStyles.js b/src/components/postBoost/postBoostStyles.js
index aa62377d9..dd45dc09d 100644
--- a/src/components/postBoost/postBoostStyles.js
+++ b/src/components/postBoost/postBoostStyles.js
@@ -44,7 +44,7 @@ export default EStyleSheet.create({
borderColor: '$borderColor',
borderRadius: 8,
padding: 2,
- color: '$primaryBlack',
+ // color: '$primaryBlack',
width: 172,
marginRight: 33,
},
diff --git a/src/components/postHtmlRenderer/postHtmlRenderer.tsx b/src/components/postHtmlRenderer/postHtmlRenderer.tsx
index 9ccef58f0..16b5f8fd4 100644
--- a/src/components/postHtmlRenderer/postHtmlRenderer.tsx
+++ b/src/components/postHtmlRenderer/postHtmlRenderer.tsx
@@ -1,10 +1,10 @@
import React, { memo, useMemo } from 'react';
import RenderHTML, { CustomRendererProps, Element, TNode } from 'react-native-render-html';
+import { useHtmlIframeProps, iframeModel } from '@native-html/iframe-plugin';
import styles from './postHtmlRendererStyles';
import { LinkData, parseLinkData } from './linkDataParser';
import VideoThumb from './videoThumb';
import { AutoHeightImage } from '../autoHeightImage/autoHeightImage';
-import { useHtmlIframeProps, iframeModel } from '@native-html/iframe-plugin';
import WebView from 'react-native-webview';
import { VideoPlayer } from '..';
import { useHtmlTableProps } from '@native-html/table-plugin';
@@ -46,7 +46,7 @@ export const PostHtmlRenderer = memo(
console.log('Comment body:', body);
- const _minTableColWidth = (contentWidth / 3) - 12;
+ const _minTableColWidth = contentWidth / 3 - 12;
const _handleOnLinkPress = (data: LinkData) => {
if (!data) {
@@ -119,10 +119,9 @@ export const PostHtmlRenderer = memo(
default:
break;
}
- } catch (error) { }
+ } catch (error) {}
};
-
//this method checks if image is a child of table column
//and calculates img width accordingly,
//returns full width if img is not part of table
@@ -142,8 +141,6 @@ export const PostHtmlRenderer = memo(
return getMaxImageWidth(tnode.parent);
};
-
-
//Does some needed dom modifications for proper rendering
const _onElement = (element: Element) => {
if (element.tagName === 'img' && element.attribs.src) {
@@ -152,10 +149,9 @@ export const PostHtmlRenderer = memo(
onElementIsImage(imgUrl);
}
-
//this avoids invalid rendering of first element of table pushing rest of columsn to extreme right.
if (element.tagName === 'table') {
- console.log('table detected')
+ console.log('table detected');
element.children.forEach((child) => {
if (child.name === 'tr') {
@@ -168,15 +164,15 @@ export const PostHtmlRenderer = memo(
if (gChild.name !== 'td' && headerIndex === -1) {
headerIndex = index;
} else if (colIndex === -1) {
- colIndex = index
+ colIndex = index;
}
}
- })
+ });
//if row contans a header with column siblings
//remove first child and place it as first separate row in table
if (headerIndex !== -1 && colIndex !== -1 && headerIndex < colIndex) {
- console.log("time to do some switching", headerIndex, colIndex);
+ console.log('time to do some switching', headerIndex, colIndex);
const header = child.children[headerIndex];
const headerRow = new Element('tr', {}, [header]);
@@ -184,13 +180,10 @@ export const PostHtmlRenderer = memo(
prependChild(element, headerRow);
}
}
- })
+ });
}
};
-
-
-
const _anchorRenderer = ({ InternalRenderer, tnode, ...props }: CustomRendererProps) => {
const parsedTnode = parseLinkData(tnode);
const _onPress = () => {
@@ -199,10 +192,8 @@ export const PostHtmlRenderer = memo(
_handleOnLinkPress(data);
};
-
//process video link
if (tnode.classes?.indexOf('markdown-video-link') >= 0) {
-
if (isComment) {
const imgElement = tnode.children.find((child) => {
return child.classes.indexOf('video-thumbnail') > 0 ? true : false;
@@ -226,23 +217,20 @@ export const PostHtmlRenderer = memo(
if (tnode.children.length === 1 && tnode.children[0].tagName === 'img') {
const maxImgWidth = getMaxImageWidth(tnode);
- return
+ return (
+
+ );
}
-
return ;
};
-
-
-
-
const _imageRenderer = ({ tnode }: CustomRendererProps) => {
const imgUrl = tnode.attributes.src;
const _onPress = () => {
@@ -280,15 +268,15 @@ export const PostHtmlRenderer = memo(
return ;
};
-
//based on number of columns a table have, sets scroll enabled or disable, also adjust table full width
const _tableRenderer = ({ InternalRenderer, ...props }: CustomRendererProps) => {
// const tableProps = useHtmlTableProps(props);
let maxColumns = 0;
- props.tnode.children.forEach((child) =>
- maxColumns = child.children.length > maxColumns ? child.children.length : maxColumns
- )
+ props.tnode.children.forEach(
+ (child) =>
+ (maxColumns = child.children.length > maxColumns ? child.children.length : maxColumns),
+ );
const isScrollable = maxColumns > 3;
const _tableWidth = isScrollable ? maxColumns * _minTableColWidth : contentWidth;
@@ -298,8 +286,8 @@ export const PostHtmlRenderer = memo(
- )
- }
+ );
+ };
// iframe renderer for rendering iframes in body
@@ -313,19 +301,10 @@ export const PostHtmlRenderer = memo(
handleVideoPress(iframeProps.source.uri);
}
};
- return (
-
- )
+ return ;
} else {
- return (
-
- );
+ return ;
}
-
};
const tagsStyles = useMemo(
@@ -341,68 +320,54 @@ export const PostHtmlRenderer = memo(
code: styles.code,
li: styles.li,
p: styles.p,
- h6: styles.h6
+ h6: styles.h6,
}),
- [contentWidth]
+ [contentWidth],
);
- const baseStyle = useMemo(
- () => (
- { ...styles.baseStyle, width: contentWidth }
- ),
- [contentWidth]
- );
+ const baseStyle = useMemo(() => ({ ...styles.baseStyle, width: contentWidth }), [contentWidth]);
const classesStyles = useMemo(
- () => (
- {
- phishy: styles.phishy,
- 'text-justify': styles.textJustify,
- 'text-center': styles.textCenter,
- }
- ),
+ () => ({
+ phishy: styles.phishy,
+ 'text-justify': styles.textJustify,
+ 'text-center': styles.textCenter,
+ }),
[],
);
const renderers = useMemo(
- () => (
- {
+ () =>
+ ({
img: _imageRenderer,
a: _anchorRenderer,
p: _paraRenderer,
iframe: _iframeRenderer,
- table: _tableRenderer
- } as any
- ),
+ table: _tableRenderer,
+ } as any),
[],
);
const domVisitors = useMemo(
- () => (
- {
- onElement: _onElement,
- }
- ),
+ () => ({
+ onElement: _onElement,
+ }),
[],
);
const customHTMLElementModels = useMemo(
- () => (
- {
- iframe: iframeModel,
- }
- ),
+ () => ({
+ iframe: iframeModel,
+ }),
[],
);
const renderersProps = useMemo(
- () => (
- {
- iframe: {
- scalesPageToFit: true
- },
- }
- ),
+ () => ({
+ iframe: {
+ scalesPageToFit: true,
+ },
+ }),
[],
);
diff --git a/src/components/progressiveImage/index.js b/src/components/progressiveImage/index.js
index 9006df132..46c3a2c27 100644
--- a/src/components/progressiveImage/index.js
+++ b/src/components/progressiveImage/index.js
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
-import { View, StyleSheet, Animated } from 'react-native';
+import { View, StyleSheet } from 'react-native';
import FastImage from 'react-native-fast-image';
+import Animated, { Easing } from 'react-native-reanimated';
const styles = StyleSheet.create({
imageOverlay: {
@@ -31,12 +32,14 @@ const ProgressiveImage = ({ thumbnailSource, source, style, ...props }) => {
}*/
Animated.timing(thumbnailAnimated, {
toValue: 1,
+ easing: Easing.inOut(Easing.ease),
}).start();
};
const onImageLoad = () => {
Animated.timing(imageAnimated, {
toValue: 1,
+ easing: Easing.inOut(Easing.ease),
}).start();
};
diff --git a/src/components/snippetEditorModal/snippetEditorModal.tsx b/src/components/snippetEditorModal/snippetEditorModal.tsx
index e5935443a..6d4bb7672 100644
--- a/src/components/snippetEditorModal/snippetEditorModal.tsx
+++ b/src/components/snippetEditorModal/snippetEditorModal.tsx
@@ -4,166 +4,143 @@ import { Alert, KeyboardAvoidingView, Platform, View } from 'react-native';
import { TextInput } from '..';
import { ThemeContainer } from '../../containers';
import { Snippet } from '../../models';
-import { addFragment, updateFragment} from '../../providers/ecency/ecency';
+import { useSnippetsMutation } from '../../providers/queries';
import { TextButton } from '../buttons';
import Modal from '../modal';
import styles from './snippetEditorModalStyles';
-
export interface SnippetEditorModalRef {
- showNewModal:()=>void;
- showEditModal:(snippet:Snippet)=>void;
+ showNewModal: () => void;
+ showEditModal: (snippet: Snippet) => void;
}
-interface SnippetEditorModalProps {
- onSnippetsUpdated:(snips:Array)=>void;
-}
+const SnippetEditorModal = ({}, ref) => {
+ const intl = useIntl();
+ const titleInputRef = useRef(null);
+ const bodyInputRef = useRef(null);
-const SnippetEditorModal = ({onSnippetsUpdated}: SnippetEditorModalProps, ref) => {
- const intl = useIntl();
- const titleInputRef = useRef(null);
- const bodyInputRef = useRef(null);
+ const snippetsMutation = useSnippetsMutation();
- const [title, setTitle] = useState('');
- const [body, setBody] = useState('');
- const [snippetId, setSnippetId] = useState(null);
- const [isNewSnippet, setIsNewSnippet] = useState(true);
- const [showModal, setShowModal] = useState(false);
- const [titleHeight, setTitleHeight] = useState(0)
+ const [title, setTitle] = useState('');
+ const [body, setBody] = useState('');
+ const [snippetId, setSnippetId] = useState(null);
+ const [isNewSnippet, setIsNewSnippet] = useState(true);
+ const [showModal, setShowModal] = useState(false);
+ const [titleHeight, setTitleHeight] = useState(0);
- useImperativeHandle(ref, () => ({
- showNewModal: () => {
- setTitle('');
- setBody('');
- setIsNewSnippet(true);
- setShowModal(true);
- },
- showEditModal:(snippet:Snippet)=>{
- setSnippetId(snippet.id);
- setTitle(snippet.title);
- setBody(snippet.body);
- setIsNewSnippet(false);
- setShowModal(true);
- }
- }));
+ useImperativeHandle(ref, () => ({
+ showNewModal: () => {
+ setTitle('');
+ setBody('');
+ setIsNewSnippet(true);
+ setShowModal(true);
+ },
+ showEditModal: (snippet: Snippet) => {
+ setSnippetId(snippet.id);
+ setTitle(snippet.title);
+ setBody(snippet.body);
+ setIsNewSnippet(false);
+ setShowModal(true);
+ },
+ }));
-
- //save snippet based on editor type
- const _saveSnippet = async () => {
- try{
- if(!title || !body){
- Alert.alert(intl.formatMessage({id:'snippets.message_incomplete'}));
- return;
- }
-
- let response = [];
- if(!isNewSnippet){
- console.log("Updating snippet:", snippetId, title, body)
- response = await updateFragment(snippetId, title, body);
- console.log("Response from add snippet: ", response)
- }else{
- console.log("Saving snippet:", title, body)
- const res = await addFragment(title, body)
- response = res && res.fragments
- console.log("Response from add snippet: ", response)
- }
- setShowModal(false);
- onSnippetsUpdated(response);
-
- }catch(err){
- Alert.alert(intl.formatMessage({id:'snippets.message_failed'}))
- console.warn("Failed to save snippet", err)
- }
-
+ //save snippet based on editor type
+ const _saveSnippet = async () => {
+ if (!title || !body) {
+ Alert.alert(intl.formatMessage({ id: 'snippets.message_incomplete' }));
+ return;
}
+ console.log('Saving snippet:', title, body);
- const _renderContent = (
-
- {({isDarkTheme})=>(
-
-
+ snippetsMutation.mutate({
+ id: isNewSnippet ? null : snippetId,
+ title,
+ body,
+ });
-
-
- {
- setTitleHeight(event.nativeEvent.contentSize.height);
- }}
- onChangeText={setTitle}
- value={title}
- />
-
-
-
-
+ setShowModal(false);
+ };
-
-
- setShowModal(false)}
- style={styles.closeButton}
- />
-
-
-
-
- )}
-
- )
+ const _renderContent = (
+
+ {({ isDarkTheme }) => (
+
+
+
+ {
+ setTitleHeight(event.nativeEvent.contentSize.height);
+ }}
+ onChangeText={setTitle}
+ value={title}
+ />
+
+
+
+
+
+
+ setShowModal(false)}
+ style={styles.closeButton}
+ />
+
+
+
+ )}
+
+ );
return (
- {setShowModal(false)}}
- presentationStyle="formSheet"
- title={intl.formatMessage({
- id:isNewSnippet
- ? 'snippets.title_add_snippet'
- : 'snippets.title_edit_snippet'
- })}
- animationType="slide"
- style={styles.modalStyle}
- >
- {_renderContent}
-
-
+ {
+ setShowModal(false);
+ }}
+ presentationStyle="formSheet"
+ title={intl.formatMessage({
+ id: isNewSnippet ? 'snippets.title_add_snippet' : 'snippets.title_edit_snippet',
+ })}
+ animationType="slide"
+ style={styles.modalStyle}
+ >
+ {_renderContent}
+
);
};
export default forwardRef(SnippetEditorModal);
-
-
diff --git a/src/components/snippetsModal/snippetItem.tsx b/src/components/snippetsModal/snippetItem.tsx
index 03fc755c0..95f81b444 100644
--- a/src/components/snippetsModal/snippetItem.tsx
+++ b/src/components/snippetsModal/snippetItem.tsx
@@ -1,43 +1,72 @@
import * as React from 'react';
-import { Text, View, Button } from 'react-native';
+import { useIntl } from 'react-intl';
+import { Alert, Text, View } from 'react-native';
+import { useSnippetDeleteMutation } from '../../providers/queries';
import IconButton from '../iconButton';
import styles from './snippetsModalStyles';
interface SnippetItemProps {
- title:string;
- body:string;
- index:number;
- onEditPress:()=>void;
- onRemovePress:()=>void;
+ id: string | null;
+ title: string;
+ body: string;
+ index: number;
+ onEditPress: () => void;
}
-const SnippetItem = ({title, body, index, onEditPress, onRemovePress}: SnippetItemProps) => {
+const SnippetItem = ({ id, title, body, index, onEditPress }: SnippetItemProps) => {
+ const intl = useIntl();
+ const snippetsDeleteMutation = useSnippetDeleteMutation();
+
+ const _onRemovePress = () => {
+ //asks for remvoe confirmation and run remove routing upon confirming
+ if (id) {
+ Alert.alert(
+ intl.formatMessage({ id: 'snippets.title_remove_confirmation' }),
+ intl.formatMessage({ id: 'snippets.message_remove_confirmation' }),
+ [
+ {
+ text: intl.formatMessage({ id: 'snippets.btn_cancel' }),
+ style: 'cancel',
+ },
+ {
+ text: intl.formatMessage({ id: 'snippets.btn_confirm' }),
+ onPress: () => snippetsDeleteMutation.mutate(id),
+ },
+ ],
+ );
+ }
+ };
+
return (
- {`${title}`}
-
-
+ {`${title}`}
+ {id && (
+ <>
+
+
+ >
+ )}
-
+
{`${body}`}
-
- )
+ );
};
-export default SnippetItem;
\ No newline at end of file
+export default SnippetItem;
diff --git a/src/components/snippetsModal/snippetsModal.tsx b/src/components/snippetsModal/snippetsModal.tsx
index b2b6ca8cd..c917ed38f 100644
--- a/src/components/snippetsModal/snippetsModal.tsx
+++ b/src/components/snippetsModal/snippetsModal.tsx
@@ -1,172 +1,106 @@
-import React, { useState, useEffect, useRef } from 'react';
-import { View, FlatList, Text, TouchableOpacity, Alert } from 'react-native';
+import React, { useRef } from 'react';
+import { View, FlatList, Text, TouchableOpacity, Alert, RefreshControl } from 'react-native';
import { useIntl } from 'react-intl';
-import { getFragments, deleteFragment } from '../../providers/ecency/ecency';
+import { deleteFragment } from '../../providers/ecency/ecency';
import { MainButton } from '..';
import styles from './snippetsModalStyles';
-import { RefreshControl } from 'react-native';
-import SnippetEditorModal, { SnippetEditorModalRef } from '../snippetEditorModal/snippetEditorModal';
+
+import SnippetEditorModal, {
+ SnippetEditorModalRef,
+} from '../snippetEditorModal/snippetEditorModal';
import SnippetItem from './snippetItem';
import { Snippet } from '../../models';
import { useAppSelector } from '../../hooks';
+import { useSnippetDeleteMutation, useSnippetsQuery } from '../../providers/queries';
interface SnippetsModalProps {
- handleOnSelect:(snippetText:string)=>void,
+ handleOnSelect: (snippetText: string) => void;
}
-const SnippetsModal = ({ handleOnSelect }:SnippetsModalProps) => {
+const SnippetsModal = ({ handleOnSelect }: SnippetsModalProps) => {
const editorRef = useRef(null);
const intl = useIntl();
- const isLoggedIn = useAppSelector(state => state.application.isLoggedIn)
-
- const [snippets, setSnippets] = useState([]);
- const [isLoading, setIsLoading] = useState(false);
-
- useEffect(() => {
- _getSnippets();
- }, []);
-
-
-
- //fetch snippets from server
- const _getSnippets = async () => {
- try{
-
- setIsLoading(true);
- const snips = await getFragments()
- console.log("snips received", snips)
- setSnippets(snips);
- setIsLoading(false);
-
- }catch(err){
- console.warn("Failed to get snippets")
- setIsLoading(false);
- }
- }
-
- //removes snippet from users snippet collection on user confirmation
- const _removeSnippet = async (id:string) => {
- try{
-
- setIsLoading(true);
- const snips = await deleteFragment(id)
- setSnippets(snips);
- setIsLoading(false);
-
- }catch(err){
- console.warn("Failed to get snippets")
- setIsLoading(false);
- }
- }
-
+ const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn);
+ const snippetsQuery = useSnippetsQuery();
//render list item for snippet and handle actions;
- const _renderItem = ({ item, index }:{item:Snippet, index:number}) => {
-
- const _onPress = () => handleOnSelect(item.body)
-
- //asks for remvoe confirmation and run remove routing upon confirming
- const _onRemovePress = () => {
- Alert.alert(
- intl.formatMessage({id:'snippets.title_remove_confirmation'}),
- intl.formatMessage({id:'snippets.message_remove_confirmation'}),
- [
- {
- text:intl.formatMessage({id:'snippets.btn_cancel'}),
- style:'cancel'
- },
- {
- text:intl.formatMessage({id:'snippets.btn_confirm'}),
- onPress:()=>_removeSnippet(item.id)
- }
- ]
- )
- }
+ const _renderItem = ({ item, index }: { item: Snippet; index: number }) => {
+ const _onPress = () => handleOnSelect(item.body);
const _onEditPress = () => {
- if(editorRef.current){
+ if (editorRef.current) {
editorRef.current.showEditModal(item);
}
- }
+ };
return (
-
+
- )
+ );
};
-
-
//render empty list placeholder
const _renderEmptyContent = () => {
return (
<>
- {intl.formatMessage({id:'snippets.label_no_snippets'})}
+ {intl.formatMessage({ id: 'snippets.label_no_snippets' })}
>
);
};
-
-
//renders footer with add snipept button and shows new snippet modal
const _renderFloatingButton = () => {
- if(!isLoggedIn){
+ if (!isLoggedIn) {
return null;
}
-
+
const _onPress = () => {
- if(editorRef.current){
+ if (editorRef.current) {
editorRef.current.showNewModal();
}
- }
+ };
return (
);
};
-
-
return (
index.toString()}
renderItem={_renderItem}
ListEmptyComponent={_renderEmptyContent}
refreshControl={
-
}
/>
{_renderFloatingButton()}
-
-
+
);
};
diff --git a/src/components/toastNotification/view/toastNotificaitonView.js b/src/components/toastNotification/view/toastNotificaitonView.js
index 4606ed037..8dfd04273 100644
--- a/src/components/toastNotification/view/toastNotificaitonView.js
+++ b/src/components/toastNotification/view/toastNotificaitonView.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react';
-import { Animated, TouchableOpacity, Text } from 'react-native';
+import { TouchableOpacity, Text } from 'react-native';
+import { View as AnimatedView } from 'react-native-animatable';
// Styles
import styles from './toastNotificationStyles';
@@ -12,70 +13,60 @@ class ToastNotification extends Component {
constructor(props) {
super(props);
- this.state = {
- animatedValue: new Animated.Value(0),
- };
- }
-
- // Component Functions
- _showToast() {
- const { duration } = this.props;
- const animatedValue = new Animated.Value(0);
-
- this.setState({ animatedValue });
-
- Animated.timing(animatedValue, { toValue: 1, duration: 350 }).start();
-
- if (duration) {
- this.closeTimer = setTimeout(() => {
- this._hideToast();
- }, duration);
- }
- }
-
- _hideToast() {
- const { animatedValue } = this.state;
- const { onHide } = this.props;
-
- Animated.timing(animatedValue, { toValue: 0.0, duration: 350 }).start(() => {
- if (onHide) {
- onHide();
- }
- });
-
- if (this.closeTimer) {
- clearTimeout(this.closeTimer);
- }
}
// Component Life Cycles
- UNSAFE_componentWillMount() {
+ componentDidMount() {
this._showToast();
}
+ handleViewRef = (ref) => (this.view = ref);
+
+ // Component Functions
+ _showToast = () => {
+ const { duration, isTop } = this.props;
+ const initialPosition = isTop ? { top: 0 } : { bottom: 0 };
+ const finalPosition = isTop ? { top: 100 } : { bottom: 100 };
+ this.view
+ .animate({ 0: { opacity: 0, ...initialPosition }, 1: { opacity: 1, ...finalPosition } })
+ .then((endState) => {
+ if (duration) {
+ this.closeTimer = setTimeout(() => {
+ this._hideToast();
+ }, duration);
+ }
+ });
+ };
+ _hideToast = () => {
+ const { isTop } = this.props;
+ const finalPosition = isTop ? { top: 0 } : { bottom: 0 };
+ const initialPosition = isTop ? { top: 100 } : { bottom: 100 };
+ this.view
+ .animate({ 0: { opacity: 1, ...initialPosition }, 1: { opacity: 0, ...finalPosition } })
+ .then((endState) => {
+ const { onHide } = this.props;
+ if (onHide) {
+ onHide();
+ }
+ });
+ };
+
render() {
- const { text, textStyle, style, onPress, isTop } = this.props;
- const { animatedValue } = this.state;
- const outputRange = isTop ? [-50, 0] : [50, 0];
- const y = animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange,
- });
- const position = isTop ? { top: 100 } : { bottom: 100 };
+ const { text, textStyle, style, onPress } = this.props;
return (
onPress && onPress()}>
-
{text}
-
+
);
}
diff --git a/src/components/toggleSwitch/view/toggleSwitchView.js b/src/components/toggleSwitch/view/toggleSwitchView.js
index 99862e453..37f55947e 100644
--- a/src/components/toggleSwitch/view/toggleSwitchView.js
+++ b/src/components/toggleSwitch/view/toggleSwitchView.js
@@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
-import { View, TouchableOpacity, Animated, NativeModules } from 'react-native';
+import { View, TouchableOpacity, NativeModules } from 'react-native';
+import Animated, { Easing } from 'react-native-reanimated';
// Constants
@@ -97,6 +98,7 @@ class ToggleSwitchView extends PureComponent {
Animated.timing(this.offsetX, {
toValue,
duration,
+ easing: Easing.inOut(Easing.ease),
}).start();
};
diff --git a/src/providers/ecency/ecency.ts b/src/providers/ecency/ecency.ts
index eddb80bed..531903960 100644
--- a/src/providers/ecency/ecency.ts
+++ b/src/providers/ecency/ecency.ts
@@ -15,9 +15,11 @@ import {
import {
CommentHistoryItem,
LatestMarketPrices,
+ NotificationFilters,
ReceivedVestingShare,
Referral,
ReferralStat,
+ Snippet,
} from './ecency.types';
/**
@@ -326,7 +328,7 @@ export const deleteFavorite = async (targetUsername: string) => {
export const getFragments = async () => {
try {
const response = await ecencyApi.post('/private-api/fragments');
- return response.data;
+ return response.data as Snippet[];
} catch (error) {
console.warn('Failed to get fragments', error);
bugsnagInstance.notify(error);
@@ -416,16 +418,9 @@ export const getLeaderboard = async (duration: 'day' | 'week' | 'month') => {
* @returns array of notifications
*/
export const getNotifications = async (data: {
- filter?:
- | 'rvotes'
- | 'mentions'
- | 'follows'
- | 'replies'
- | 'reblogs'
- | 'transfers'
- | 'delegations'
- | 'nfavorites';
+ filter?: NotificationFilters;
since?: string;
+ limit?: number;
}) => {
try {
const response = await ecencyApi.post('/private-api/notifications', data);
diff --git a/src/providers/ecency/ecency.types.ts b/src/providers/ecency/ecency.types.ts
index 571cfe098..79ccb9e7e 100644
--- a/src/providers/ecency/ecency.types.ts
+++ b/src/providers/ecency/ecency.types.ts
@@ -1,54 +1,61 @@
-import { QuoteItem } from "../../redux/reducers/walletReducer";
+import { QuoteItem } from '../../redux/reducers/walletReducer';
export interface ReceivedVestingShare {
- delegator:string;
- delegatee:string;
- vesting_shares:string;
- timestamp:string;
+ delegator: string;
+ delegatee: string;
+ vesting_shares: string;
+ timestamp: string;
+}
+
+export interface Snippet {
+ id: string;
+ title: string;
+ body: string;
+ created: string;
+ modified: string;
}
export interface EcencyUser {
- username:string;
- points:string;
- unclaimed_points:string;
- points_by_type:{[key:string]:string};
- unclaimed_points_by_type:{[key:string]:string};
+ username: string;
+ points: string;
+ unclaimed_points: string;
+ points_by_type: { [key: string]: string };
+ unclaimed_points_by_type: { [key: string]: string };
}
export interface Referral {
- id:number;
- referral:string;
- rewarded:boolean;
- username:string;
- created:string
+ id: number;
+ referral: string;
+ rewarded: boolean;
+ username: string;
+ created: string;
}
export interface ReferralStat {
- total: number;
- rewarded: number;
+ total: number;
+ rewarded: number;
}
export interface UserPoint {
- id: number;
- type: number;
- amount: string;
- created:string;
- memo?: string;
- receiver?: string;
- sender?: string;
-
+ id: number;
+ type: number;
+ amount: string;
+ created: string;
+ memo?: string;
+ receiver?: string;
+ sender?: string;
}
export interface LatestQuotes {
- [key:string]:QuoteItem
+ [key: string]: QuoteItem;
}
export interface CommentHistoryItem {
- body: string;
- tags: [string];
- title: string;
- timestamp:string;
- v: number;
+ body: string;
+ tags: [string];
+ title: string;
+ timestamp: string;
+ v: number;
}
export enum ScheduledPostStatus {
@@ -56,4 +63,16 @@ export enum ScheduledPostStatus {
POSTPONED = 2,
PUBLISHED = 3,
ERROR = 4,
-}
\ No newline at end of file
+}
+
+export enum NotificationFilters {
+ ACTIVITIES = "activities",
+ RVOTES = "rvotes",
+ MENTIONS = "mentions",
+ FOLLOWS = "follows",
+ REPLIES = "replies",
+ REBLOGS = "reblogs",
+ TRANFERS = "transfers",
+ DELEGATIONS = "delegations",
+ FAVOURITES = "nfavorites"
+}
diff --git a/src/providers/queries/editorQueries.ts b/src/providers/queries/editorQueries.ts
new file mode 100644
index 000000000..591f44314
--- /dev/null
+++ b/src/providers/queries/editorQueries.ts
@@ -0,0 +1,90 @@
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { useIntl } from 'react-intl';
+import { useAppDispatch } from '../../hooks';
+import { toastNotification } from '../../redux/actions/uiAction';
+import { addFragment, deleteFragment, getFragments, updateFragment } from '../ecency/ecency';
+import { Snippet } from '../ecency/ecency.types';
+import QUERIES from './queryKeys';
+
+interface SnippetMutationVars {
+ id: string | null;
+ title: string;
+ body: string;
+}
+
+export const useSnippetsQuery = () => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+ return useQuery([QUERIES.SNIPPETS.GET], getFragments, {
+ onError: () => {
+ dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
+ },
+ });
+};
+
+export const useSnippetsMutation = () => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+ const queryClient = useQueryClient();
+
+ return useMutation(
+ async (vars) => {
+ console.log('going to add/update snippet', vars);
+ if (vars.id) {
+ const response = await updateFragment(vars.id, vars.title, vars.body);
+ return response;
+ } else {
+ const response = await addFragment(vars.title, vars.body);
+ return response;
+ }
+ },
+ {
+ onMutate: (vars) => {
+ console.log('mutate snippets for add/update', vars);
+
+ const _newItem = {
+ id: vars.id,
+ title: vars.title,
+ body: vars.body,
+ created: new Date().toDateString(),
+ modified: new Date().toDateString(),
+ } as Snippet;
+
+ const data = queryClient.getQueryData([QUERIES.SNIPPETS.GET]);
+
+ let _newData: Snippet[] = data ? [...data] : [];
+ if (vars.id) {
+ const snipIndex = _newData.findIndex((item) => vars.id === item.id);
+ _newData[snipIndex] = _newItem;
+ } else {
+ _newData = [_newItem, ..._newData];
+ }
+
+ queryClient.setQueryData([QUERIES.SNIPPETS.GET], _newData);
+ },
+ onSuccess: (data) => {
+ console.log('added/updated snippet', data);
+ queryClient.invalidateQueries([QUERIES.SNIPPETS.GET]);
+ },
+ onError: () => {
+ dispatch(toastNotification(intl.formatMessage({ id: 'snippets.message_failed' })));
+ },
+ },
+ );
+};
+
+export const useSnippetDeleteMutation = () => {
+ const queryClient = useQueryClient();
+ const dispatch = useAppDispatch();
+ const intl = useIntl();
+ return useMutation(deleteFragment, {
+ retry: 3,
+ onSuccess: (data) => {
+ console.log('Success scheduled post delete', data);
+ queryClient.setQueryData([QUERIES.SNIPPETS.GET], data);
+ },
+ onError: () => {
+ dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
+ },
+ });
+};
diff --git a/src/providers/queries/index.ts b/src/providers/queries/index.ts
index 9a9705790..bf3b903af 100644
--- a/src/providers/queries/index.ts
+++ b/src/providers/queries/index.ts
@@ -22,3 +22,7 @@ export const initQueryClient = () => {
persistOptions: { persister: asyncStoragePersister },
} as PersistQueryClientProviderProps;
};
+
+export * from './notificationQueries';
+export * from './draftQueries';
+export * from './editorQueries';
diff --git a/src/providers/queries/notificationQueries.ts b/src/providers/queries/notificationQueries.ts
new file mode 100644
index 000000000..40ea235d6
--- /dev/null
+++ b/src/providers/queries/notificationQueries.ts
@@ -0,0 +1,132 @@
+import {
+ QueryKey,
+ useMutation,
+ UseMutationOptions,
+ useQueries,
+ useQueryClient,
+} from '@tanstack/react-query';
+import { useState } from 'react';
+import { useIntl } from 'react-intl';
+import bugsnapInstance from '../../config/bugsnag';
+import { useAppDispatch, useAppSelector } from '../../hooks';
+import { updateUnreadActivityCount } from '../../redux/actions/accountAction';
+import { toastNotification } from '../../redux/actions/uiAction';
+import { getNotifications, markNotifications } from '../ecency/ecency';
+import { NotificationFilters } from '../ecency/ecency.types';
+import { markHiveNotifications } from '../hive/dhive';
+import QUERIES from './queryKeys';
+
+const FETCH_LIMIT = 20;
+
+export const useNotificationsQuery = (filter: NotificationFilters) => {
+ const [isRefreshing, setIsRefreshing] = useState(false);
+ const [pageParams, setPageParams] = useState(['']);
+
+ const _fetchNotifications = async (pageParam: string) => {
+ console.log('fetching page since:', pageParam);
+ const response = await getNotifications({ filter, since: pageParam, limit: FETCH_LIMIT });
+ console.log('new page fetched', response);
+ return response || [];
+ };
+
+ const _getNextPageParam = (lastPage: any[]) => {
+ const lastId = lastPage && lastPage.length ? lastPage.lastItem.id : undefined;
+ console.log('extracting next page parameter', lastId);
+ return lastId;
+ };
+
+ //query initialization
+ const notificationQueries = useQueries({
+ queries: pageParams.map((pageParam) => ({
+ queryKey: [QUERIES.NOTIFICATIONS.GET, filter, pageParam],
+ queryFn: () => _fetchNotifications(pageParam),
+ initialData: [],
+ })),
+ });
+
+ const _refresh = async () => {
+ setIsRefreshing(true);
+ setPageParams(['']);
+ await notificationQueries[0].refetch();
+ setIsRefreshing(false);
+ };
+
+ const _fetchNextPage = () => {
+ const lastId = _getNextPageParam(notificationQueries.lastItem.data);
+ if (!pageParams.includes(lastId)) {
+ pageParams.push(lastId);
+ setPageParams([...pageParams]);
+ }
+ };
+
+ return {
+ data: notificationQueries.flatMap((query) => query.data),
+ isRefreshing,
+ isLoading: notificationQueries.lastItem.isLoading || notificationQueries.lastItem.isFetching,
+ fetchNextPage: _fetchNextPage,
+ refresh: _refresh,
+ };
+};
+
+export const useNotificationReadMutation = () => {
+ const intl = useIntl();
+ const dispatch = useAppDispatch();
+ const queryClient = useQueryClient();
+
+ const currentAccount = useAppSelector((state) => state.account.currentAccount);
+ const pinCode = useAppSelector((state) => state.application.pin);
+
+ //id is options, if no id is provided program marks all notifications as read;
+ const _mutationFn = async (id?: string) => {
+ try {
+ const response = await markNotifications(id);
+ console.log('Ecency notifications marked as Read', response);
+ if (!id) {
+ await markHiveNotifications(currentAccount, pinCode);
+ console.log('Hive notifications marked as Read');
+ }
+
+ return response.unread || 0;
+ } catch (err) {
+ bugsnapInstance.notify(err);
+ }
+ };
+
+ const _options: UseMutationOptions = {
+ onMutate: async (notificationId) => {
+ //TODO: find a way to optimise mutations by avoiding too many loops
+ console.log('on mutate data', notificationId);
+
+ //update query data
+ const queriesData: [QueryKey, any[] | undefined][] = queryClient.getQueriesData([
+ QUERIES.NOTIFICATIONS.GET,
+ ]);
+ console.log('query data', queriesData);
+
+ queriesData.forEach(([queryKey, data]) => {
+ if (data) {
+ console.log('mutating data', queryKey);
+ const _mutatedData = data.map((item) => ({
+ ...item,
+ read: !notificationId || notificationId === item.id ? 1 : item.read,
+ }));
+ queryClient.setQueryData(queryKey, _mutatedData);
+ }
+ });
+ },
+
+ onSuccess: async (unreadCount, notificationId) => {
+ console.log('on success data', unreadCount);
+
+ dispatch(updateUnreadActivityCount(unreadCount));
+ if (!notificationId) {
+ queryClient.invalidateQueries([QUERIES.NOTIFICATIONS.GET]);
+ }
+ },
+ onError: () => {
+ dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
+ },
+ };
+
+ return useMutation(_mutationFn, _options);
+};
diff --git a/src/providers/queries/queryKeys.ts b/src/providers/queries/queryKeys.ts
index f0195924d..3241e2d49 100644
--- a/src/providers/queries/queryKeys.ts
+++ b/src/providers/queries/queryKeys.ts
@@ -5,6 +5,12 @@ const QUERIES = {
SCHEDULES: {
GET: 'QUERY_GET_SCHEDULES',
},
+ NOTIFICATIONS:{
+ GET: 'QERUY_GET_NOTIFICATIONS'
+ },
+ SNIPPETS: {
+ GET: 'QUERY_GET_SNIPPETS',
+ }
};
export default QUERIES;
diff --git a/src/redux/store/store.ts b/src/redux/store/store.ts
index 8d15beac3..59e930dc3 100644
--- a/src/redux/store/store.ts
+++ b/src/redux/store/store.ts
@@ -2,38 +2,39 @@ import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { persistStore, persistReducer, createTransform } from 'redux-persist';
import AsyncStorage from '@react-native-community/async-storage';
+import createMigrate from 'redux-persist/es/createMigrate';
import Reactotron from '../../../reactotron-config';
import reducer from '../reducers';
-import createMigrate from 'redux-persist/es/createMigrate';
import MigrationHelpers from '../../utils/migrationHelpers';
const transformCacheVoteMap = createTransform(
- (inboundState:any) => ({
- ...inboundState,
- votes : Array.from(inboundState.votes),
- comments : Array.from(inboundState.comments),
- drafts : Array.from(inboundState.drafts),
- subscribedCommunities: Array.from(inboundState.subscribedCommunities)
+ (inboundState: any) => ({
+ ...inboundState,
+ votes: Array.from(inboundState.votes),
+ comments: Array.from(inboundState.comments),
+ drafts: Array.from(inboundState.drafts),
+ subscribedCommunities: Array.from(inboundState.subscribedCommunities),
}),
- (outboundState) => ({
- ...outboundState,
- votes:new Map(outboundState.votes),
- comments:new Map(outboundState.comments),
+ (outboundState) => ({
+ ...outboundState,
+ votes: new Map(outboundState.votes),
+ comments: new Map(outboundState.comments),
drafts: new Map(outboundState.drafts),
- subscribedCommunities: new Map(outboundState.subscribedCommunities)
+ subscribedCommunities: new Map(outboundState.subscribedCommunities),
}),
- {whitelist:['cache']}
+ { whitelist: ['cache'] },
);
const transformWalkthroughMap = createTransform(
- (inboundState:any) => ({ ...inboundState, walkthroughMap : Array.from(inboundState.walkthroughMap)}),
- (outboundState) => ({ ...outboundState, walkthroughMap:new Map(outboundState.walkthroughMap)}),
- {whitelist:['walkthrough']}
+ (inboundState: any) => ({
+ ...inboundState,
+ walkthroughMap: Array.from(inboundState.walkthroughMap),
+ }),
+ (outboundState) => ({ ...outboundState, walkthroughMap: new Map(outboundState.walkthroughMap) }),
+ { whitelist: ['walkthrough'] },
);
-
-
// Middleware: Redux Persist Config
const persistConfig = {
// Root
@@ -44,11 +45,8 @@ const persistConfig = {
// Blacklist (Don't Save Specific Reducers)
blacklist: ['communities', 'user', 'ui'],
timeout: 0,
- transforms:[
- transformCacheVoteMap,
- transformWalkthroughMap
- ],
- migrate: createMigrate(MigrationHelpers.reduxMigrations, {debug:false})
+ transforms: [transformCacheVoteMap, transformWalkthroughMap],
+ migrate: createMigrate(MigrationHelpers.reduxMigrations, { debug: false }),
};
// Middleware: Redux Persist Persisted Reducer
@@ -66,8 +64,7 @@ const persistor = persistStore(store);
export { store, persistor };
-
// Infer the `RootState` and `AppDispatch` types from the store itself
-export type RootState = ReturnType
+export type RootState = ReturnType;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
-export type AppDispatch = typeof store.dispatch
+export type AppDispatch = typeof store.dispatch;
diff --git a/src/screens/application/container/applicationContainer.tsx b/src/screens/application/container/applicationContainer.tsx
index 072d30f70..cf7f673cb 100644
--- a/src/screens/application/container/applicationContainer.tsx
+++ b/src/screens/application/container/applicationContainer.tsx
@@ -100,7 +100,7 @@ class ApplicationContainer extends Component {
super(props);
this.state = {
isRenderRequire: true,
- isIos: Platform.OS !== 'android',
+ // isIos: Platform.OS !== 'android',
appState: AppState.currentState,
foregroundNotificationData: null,
};
@@ -145,7 +145,7 @@ class ApplicationContainer extends Component {
);
};
- componentDidUpdate(prevProps, prevState) {
+ componentDidUpdate(prevProps) {
const { isGlobalRenderRequired, dispatch } = this.props;
if (isGlobalRenderRequired !== prevProps.isGlobalRenderRequired && isGlobalRenderRequired) {
@@ -218,7 +218,7 @@ class ApplicationContainer extends Component {
};
_handleDeepLink = async (url = '') => {
- const { currentAccount, intl } = this.props;
+ const { currentAccount } = this.props;
if (!url) {
return;
@@ -308,6 +308,7 @@ class ApplicationContainer extends Component {
if (appState.match(/inactive|background/) && nextAppState === 'active') {
this._refreshGlobalProps();
+ this._refreshUnreadActivityCount();
if (_isPinCodeOpen && this._pinCodeTimer) {
clearTimeout(this._pinCodeTimer);
}
@@ -356,7 +357,7 @@ class ApplicationContainer extends Component {
const type = get(push, 'type', '');
const fullPermlink =
get(push, 'permlink1', '') + get(push, 'permlink2', '') + get(push, 'permlink3', '');
- const username = get(push, 'target', '');
+ // const username = get(push, 'target', '');
const activity_id = get(push, 'id', '');
switch (type) {
@@ -487,8 +488,14 @@ class ApplicationContainer extends Component {
actions.fetchCoinQuotes();
};
+ _refreshUnreadActivityCount = async () => {
+ const { dispatch } = this.props as any;
+ const unreadActivityCount = await getUnreadNotificationCount();
+ dispatch(updateUnreadActivityCount(unreadActivityCount));
+ };
+
_getUserDataFromRealm = async () => {
- const { dispatch, pinCode, isPinCodeOpen: _isPinCodeOpen, isConnected } = this.props;
+ const { dispatch, isPinCodeOpen: _isPinCodeOpen, isConnected } = this.props;
let realmData = [];
const res = await getAuthStatus();
@@ -643,8 +650,12 @@ class ApplicationContainer extends Component {
//update notification settings and update push token for each signed accoutn useing access tokens
_registerDeviceForNotifications = (settings?: any) => {
- const { currentAccount, otherAccounts, notificationDetails, isNotificationsEnabled } =
- this.props;
+ const {
+ currentAccount,
+ otherAccounts,
+ notificationDetails,
+ isNotificationsEnabled,
+ } = this.props;
const isEnabled = settings ? !!settings.notification : isNotificationsEnabled;
settings = settings || notificationDetails;
diff --git a/src/screens/drafts/container/draftsContainer.tsx b/src/screens/drafts/container/draftsContainer.tsx
index 044ef9270..e8b811d8d 100644
--- a/src/screens/drafts/container/draftsContainer.tsx
+++ b/src/screens/drafts/container/draftsContainer.tsx
@@ -9,7 +9,7 @@ import {
useGetSchedulesQuery,
useMoveScheduleToDraftsMutation,
useScheduleDeleteMutation,
-} from '../../../providers/queries/draftQueries';
+} from '../../../providers/queries';
// Middleware
@@ -52,14 +52,11 @@ const DraftsContainer = ({ currentAccount, navigation, route }) => {
};
const _editDraft = (id: string) => {
- const selectedDraft = drafts.find((draft) => draft._id === id);
-
navigation.navigate({
name: ROUTES.SCREENS.EDITOR,
key: `editor_draft_${id}`,
params: {
- draft: selectedDraft,
- fetchPost: refetchDrafts,
+ draftId: id,
},
});
};
diff --git a/src/screens/editor/container/editorContainer.tsx b/src/screens/editor/container/editorContainer.tsx
index e389284fb..f9f0f1b69 100644
--- a/src/screens/editor/container/editorContainer.tsx
+++ b/src/screens/editor/container/editorContainer.tsx
@@ -8,8 +8,7 @@ import { isArray } from 'lodash';
// Services and Actions
import { Buffer } from 'buffer';
-
-import { useQueryClient } from '@tanstack/react-query';
+import { QueryClient, useQueryClient } from '@tanstack/react-query';
import { addDraft, updateDraft, getDrafts, addSchedule } from '../../../providers/ecency/ecency';
import { toastNotification, setRcOffer } from '../../../redux/actions/uiAction';
import {
@@ -22,6 +21,7 @@ import {
// Constants
import { default as ROUTES } from '../../../constants/routeNames';
+
// Utilities
import {
generatePermlink,
@@ -34,6 +34,7 @@ import {
extractImageUrls,
} from '../../../utils/editor';
// import { generateSignature } from '../../../utils/image';
+
// Component
import EditorScreen from '../screen/editorScreen';
import { removeBeneficiaries, setBeneficiaries } from '../../../redux/actions/editorActions';
@@ -85,7 +86,7 @@ class EditorContainer extends Component {
// Component Life Cycle Functions
componentDidMount() {
this._isMounted = true;
- const { currentAccount, route } = this.props;
+ const { currentAccount, route, queryClient } = this.props;
const username = currentAccount && currentAccount.name ? currentAccount.name : '';
let isReply;
let draftId;
@@ -98,16 +99,22 @@ class EditorContainer extends Component {
const navigationParams = route.params;
hasSharedIntent = navigationParams.hasSharedIntent;
- if (navigationParams.draft) {
- _draft = navigationParams.draft;
+ if (navigationParams.draftId) {
+ draftId = navigationParams.draftId;
+ const cachedDrafts: any = queryClient.getQueryData([QUERIES.DRAFTS.GET]);
- // this._loadMeta(_draft);
+ if (cachedDrafts && cachedDrafts.length) {
+ //get draft from query cache
+ const _draft = cachedDrafts.find((draft) => draft._id === draftId);
- this.setState({
- draftId: _draft._id,
- });
- this._getStorageDraft(username, isReply, _draft);
+ this.setState({
+ draftId,
+ });
+
+ this._getStorageDraft(username, isReply, _draft);
+ }
}
+
if (navigationParams.community) {
this.setState({
community: navigationParams.community,
@@ -240,9 +247,9 @@ class EditorContainer extends Component {
: paramDraft.tags.split(',');
this.setState({
draftPost: {
- title: paramDraft.title,
- body: paramDraft.body,
- tags: _tags,
+ title: paramDraft.title || '',
+ body: paramDraft.body || '',
+ tags: _tags || [],
meta: paramDraft.meta ? paramDraft.meta : null,
},
draftId: paramDraft._id,
diff --git a/src/screens/notification/container/notificationContainer.js b/src/screens/notification/container/notificationContainer.js
deleted file mode 100644
index 53adf7681..000000000
--- a/src/screens/notification/container/notificationContainer.js
+++ /dev/null
@@ -1,239 +0,0 @@
-/* eslint-disable react/no-unused-state */
-import React, { Component } from 'react';
-import { Alert } from 'react-native';
-import { connect } from 'react-redux';
-import get from 'lodash/get';
-import { injectIntl } from 'react-intl';
-
-// Actions and Services
-import { unionBy } from 'lodash';
-import { getNotifications, markNotifications } from '../../../providers/ecency/ecency';
-import { updateUnreadActivityCount } from '../../../redux/actions/accountAction';
-
-// Constants
-import ROUTES from '../../../constants/routeNames';
-
-// Components
-import NotificationScreen from '../screen/notificationScreen';
-import { showProfileModal } from '../../../redux/actions/uiAction';
-import { markHiveNotifications } from '../../../providers/hive/dhive';
-import bugsnapInstance from '../../../config/bugsnag';
-
-class NotificationContainer extends Component {
- constructor(props) {
- super(props);
- this.state = {
- notificationsMap: new Map(),
- lastNotificationId: null,
- isRefreshing: true,
- isLoading: false,
- selectedFilter: 'activities',
- endOfNotification: false,
- selectedIndex: 0,
- };
- }
-
- componentDidMount() {
- const { isConnected } = this.props;
- if (isConnected) {
- this._getActivities();
- }
- }
-
- _getActivities = (type = 'activities', loadMore = false, loadUnread = false) => {
- const { lastNotificationId, endOfNotification, isLoading, notificationsMap } = this.state;
- const since = loadMore ? lastNotificationId : null;
-
- if (isLoading) {
- return;
- }
-
- if (!endOfNotification || !loadMore || loadUnread) {
- this.setState({
- isRefreshing: !loadMore,
- isLoading: true,
- });
- getNotifications({ filter: type, since: since, limit: 20 })
- .then((res) => {
- const lastId = res.length > 0 ? [...res].pop().id : null;
-
- if (loadMore && (lastId === lastNotificationId || res.length === 0)) {
- this.setState({
- endOfNotification: true,
- isRefreshing: false,
- isLoading: false,
- });
- } else {
- console.log('');
- const stateNotifications = notificationsMap.get(type) || [];
- const _notifications = loadMore
- ? unionBy(stateNotifications, res, 'id')
- : loadUnread
- ? unionBy(res, stateNotifications, 'id')
- : res;
- notificationsMap.set(type, _notifications);
- this.setState({
- notificationsMap,
- lastNotificationId: lastId,
- isRefreshing: false,
- isLoading: false,
- });
- }
- })
- .catch(() => this.setState({ isRefreshing: false, isLoading: false }));
- }
- };
-
- _navigateToNotificationRoute = (data) => {
- const { navigation, dispatch } = this.props;
- const type = get(data, 'type');
- const permlink = get(data, 'permlink');
- const author = get(data, 'author');
- let routeName;
- let params;
- let key;
- if (data && !data.read) {
- markNotifications(data.id).then((result) => {
- const { unread } = result;
- dispatch(updateUnreadActivityCount(unread));
- });
- }
-
- if (permlink && author) {
- routeName = ROUTES.SCREENS.POST;
- key = permlink;
- params = {
- author,
- permlink,
- };
- } else if (type === 'follow') {
- routeName = ROUTES.SCREENS.PROFILE;
- key = get(data, 'follower');
- params = {
- username: get(data, 'follower'),
- };
- } else if (type === 'transfer') {
- routeName = ROUTES.TABBAR.WALLET;
- } else if (type === 'spin') {
- routeName = ROUTES.SCREENS.BOOST;
- } else if (type === 'inactive') {
- routeName = ROUTES.SCREENS.EDITOR;
- }
-
- if (routeName) {
- navigation.navigate({
- name: routeName,
- params,
- key,
- });
- }
- };
-
- _handleOnUserPress = (username) => {
- const { dispatch } = this.props;
- dispatch(showProfileModal(username));
- };
-
- _readAllNotification = () => {
- const { dispatch, intl, isConnected, currentAccount, pinCode } = this.props;
- const { notificationsMap } = this.state;
-
- if (!isConnected) {
- return;
- }
-
- this.setState({ isRefreshing: true });
-
- markNotifications()
- .then(() => {
- notificationsMap.forEach((notifications, key) => {
- const updatedNotifications = notifications.map((item) => ({ ...item, read: 1 }));
- notificationsMap.set(key, updatedNotifications);
- });
-
- dispatch(updateUnreadActivityCount(0));
- markHiveNotifications(currentAccount, pinCode)
- .then(() => {
- console.log('Hive notifications marked as Read');
- })
- .catch((err) => {
- bugsnapInstance.notify(err);
- });
- this.setState({ notificationsMap, isRefreshing: false });
- })
- .catch(() => {
- Alert.alert(
- intl.formatMessage({ id: 'alert.error' }),
- intl.formatMessage({ d: 'alert.unknow_error' }),
- );
- this.setState({ isRefreshing: false });
- });
- };
-
- _handleOnPressLogin = () => {
- const { navigation } = this.props;
-
- navigation.navigate(ROUTES.SCREENS.LOGIN);
- };
-
- _changeSelectedFilter = async (value, ind) => {
- this.setState({ selectedFilter: value, endOfNotification: false, selectedIndex: ind });
- };
-
- UNSAFE_componentWillReceiveProps(nextProps) {
- const { selectedFilter, notificationsMap } = this.state;
- const { currentAccount } = this.props;
- if (currentAccount && nextProps.currentAccount) {
- if (nextProps.currentAccount.name !== currentAccount.name) {
- this.setState(
- {
- endOfNotification: false,
- notificationsMap: new Map(),
- },
- () => this._getActivities(selectedFilter),
- );
- } else if (
- nextProps.currentAccount.unread_activity_count > currentAccount.unread_activity_count
- ) {
- notificationsMap.forEach((value, key) => {
- console.log('fetching new activities for ', key);
- this._getActivities(key, false, true);
- });
- }
- }
- }
-
- render() {
- const { isLoggedIn, globalProps } = this.props;
- const { notificationsMap, selectedFilter, isRefreshing, isLoading } = this.state;
-
- const _notifications = notificationsMap.get(selectedFilter) || [];
- return (
-
- );
- }
-}
-
-const mapStateToProps = (state) => ({
- isLoggedIn: state.application.isLoggedIn,
- isConnected: state.application.isConnected,
- pinCode: state.application.pin,
- currentAccount: state.account.currentAccount,
- globalProps: state.account.globalProps,
- activeBottomTab: state.ui.activeBottomTab,
-});
-
-export default injectIntl(connect(mapStateToProps)(NotificationContainer));
-/* eslint-enable */
diff --git a/src/screens/notification/container/notificationContainer.tsx b/src/screens/notification/container/notificationContainer.tsx
new file mode 100644
index 000000000..4608f4c59
--- /dev/null
+++ b/src/screens/notification/container/notificationContainer.tsx
@@ -0,0 +1,150 @@
+/* eslint-disable react/no-unused-state */
+import React, { useRef, useState } from 'react';
+import { useDispatch } from 'react-redux';
+import get from 'lodash/get';
+
+// Actions and Services
+import { useEffect } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
+
+// Constants
+import ROUTES from '../../../constants/routeNames';
+
+// Components
+import NotificationScreen from '../screen/notificationScreen';
+import { showProfileModal } from '../../../redux/actions/uiAction';
+import { useAppSelector } from '../../../hooks';
+import { useNotificationReadMutation, useNotificationsQuery } from '../../../providers/queries';
+import { NotificationFilters } from '../../../providers/ecency/ecency.types';
+import QUERIES from '../../../providers/queries/queryKeys';
+
+const NotificationContainer = ({ navigation }) => {
+ const dispatch = useDispatch();
+ const queryClient = useQueryClient();
+
+ const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn);
+ const isConnected = useAppSelector((state) => state.application.isConnected);
+ const currentAccount = useAppSelector((state) => state.account.currentAccount);
+ const globalProps = useAppSelector((state) => state.account.globalProps);
+
+ const unreadCountRef = useRef(currentAccount.unread_acitivity_count || 0);
+ const curUsername = useRef(currentAccount.username);
+
+ const notificationReadMutation = useNotificationReadMutation();
+ const allNotificationsQuery = useNotificationsQuery(NotificationFilters.ACTIVITIES);
+ const repliesNotificationsQuery = useNotificationsQuery(NotificationFilters.REPLIES);
+ const mentiosnNotificationsQuery = useNotificationsQuery(NotificationFilters.MENTIONS);
+
+ const [selectedFilter, setSelectedFilter] = useState(NotificationFilters.ACTIVITIES);
+
+ const selectedQuery =
+ selectedFilter === NotificationFilters.REPLIES
+ ? repliesNotificationsQuery
+ : selectedFilter === NotificationFilters.MENTIONS
+ ? mentiosnNotificationsQuery
+ : allNotificationsQuery;
+
+ useEffect(() => {
+ if (curUsername.current !== currentAccount.username) {
+ queryClient.removeQueries([QUERIES.NOTIFICATIONS.GET]);
+ selectedQuery.refresh();
+ curUsername.current = currentAccount.useranme;
+ }
+ }, [currentAccount.username]);
+
+ useEffect(() => {
+ if (currentAccount.unread_activity_count > unreadCountRef.current) {
+ queryClient.invalidateQueries([QUERIES.NOTIFICATIONS.GET]);
+ //TODO: fetch new notifications instead
+ }
+ unreadCountRef.current = currentAccount.unread_activity_count;
+ }, [currentAccount.unread_activity_count]);
+
+ const _getActivities = (loadMore = false) => {
+ if (loadMore) {
+ console.log('load more notifications');
+ selectedQuery.fetchNextPage();
+ } else {
+ console.log('refreshing');
+ selectedQuery.refresh();
+ }
+ };
+
+ const _navigateToNotificationRoute = (data) => {
+ const type = get(data, 'type');
+ const permlink = get(data, 'permlink');
+ const author = get(data, 'author');
+ let routeName;
+ let params;
+ let key;
+ if (data && !data.read) {
+ notificationReadMutation.mutate(data.id);
+ }
+
+ if (permlink && author) {
+ routeName = ROUTES.SCREENS.POST;
+ key = permlink;
+ params = {
+ author,
+ permlink,
+ };
+ } else if (type === 'follow') {
+ routeName = ROUTES.SCREENS.PROFILE;
+ key = get(data, 'follower');
+ params = {
+ username: get(data, 'follower'),
+ };
+ } else if (type === 'transfer') {
+ routeName = ROUTES.TABBAR.WALLET;
+ } else if (type === 'spin') {
+ routeName = ROUTES.SCREENS.BOOST;
+ } else if (type === 'inactive') {
+ routeName = ROUTES.SCREENS.EDITOR;
+ }
+
+ if (routeName) {
+ navigation.navigate({
+ name: routeName,
+ params,
+ key,
+ });
+ }
+ };
+
+ const _handleOnUserPress = (username) => {
+ dispatch(showProfileModal(username));
+ };
+
+ //TODO: handle mark as read mutations
+ const _readAllNotification = () => {
+ if (!isConnected) {
+ return;
+ }
+ notificationReadMutation.mutate();
+ };
+
+ const _handleOnPressLogin = () => {
+ navigation.navigate(ROUTES.SCREENS.LOGIN);
+ };
+
+ const _notifications = selectedQuery.data;
+
+ return (
+
+ );
+};
+
+export default NotificationContainer;
+/* eslint-enable */
diff --git a/src/utils/migrationHelpers.ts b/src/utils/migrationHelpers.ts
index cf9ba5d77..c5c9c4492 100644
--- a/src/utils/migrationHelpers.ts
+++ b/src/utils/migrationHelpers.ts
@@ -5,195 +5,178 @@ import Config from 'react-native-config';
import THEME_OPTIONS from '../constants/options/theme';
import { getUnreadNotificationCount } from '../providers/ecency/ecency';
import { getPointsSummary } from '../providers/ecency/ePoint';
-import { migrateToMasterKeyWithAccessToken, refreshSCToken, updatePinCode } from '../providers/hive/auth';
+import {
+ migrateToMasterKeyWithAccessToken,
+ refreshSCToken,
+ updatePinCode,
+} from '../providers/hive/auth';
import { getMutes } from '../providers/hive/dhive';
import AUTH_TYPE from '../constants/authType';
// Services
-import {
- getSettings, getUserDataWithUsername,
-} from '../realm/realm';
+import { getSettings, getUserDataWithUsername } from '../realm/realm';
import { updateCurrentAccount } from '../redux/actions/accountAction';
import {
- isDarkTheme,
- changeNotificationSettings,
- changeAllNotificationSettings,
- setApi,
- setCurrency,
- setLanguage,
- setNsfw,
- isDefaultFooter,
- isPinCodeOpen,
- setColorTheme,
- setSettingsMigrated,
- setPinCode,
- setEncryptedUnlockPin,
- setPostUpvotePercent,
- setCommentUpvotePercent,
+ isDarkTheme,
+ changeNotificationSettings,
+ changeAllNotificationSettings,
+ setApi,
+ setCurrency,
+ setLanguage,
+ setNsfw,
+ isDefaultFooter,
+ isPinCodeOpen,
+ setColorTheme,
+ setSettingsMigrated,
+ setPinCode,
+ setEncryptedUnlockPin,
+ setPostUpvotePercent,
+ setCommentUpvotePercent,
} from '../redux/actions/applicationActions';
import { fetchSubscribedCommunities } from '../redux/actions/communitiesAction';
import {
- hideActionModal,
- hideProfileModal,
- setRcOffer,
- toastNotification,
+ hideActionModal,
+ hideProfileModal,
+ setRcOffer,
+ toastNotification,
} from '../redux/actions/uiAction';
import { decryptKey, encryptKey } from './crypto';
-
//migrates settings from realm to redux once and do no user realm for settings again;
export const migrateSettings = async (dispatch: any, settingsMigratedV2: boolean) => {
+ if (settingsMigratedV2) {
+ return;
+ }
- if (settingsMigratedV2) {
- return;
+ //reset certain properties
+ dispatch(hideActionModal());
+ dispatch(hideProfileModal());
+ dispatch(toastNotification(''));
+ dispatch(setRcOffer(false));
+
+ const settings = await getSettings();
+
+ if (settings) {
+ const isDarkMode = Appearance.getColorScheme() === 'dark';
+ dispatch(isDarkTheme(settings.isDarkTheme !== null ? settings.isDarkTheme : isDarkMode));
+ dispatch(setColorTheme(THEME_OPTIONS.findIndex((item) => item.value === settings.isDarkTheme)));
+ await dispatch(isPinCodeOpen(!!settings.isPinCodeOpen));
+ if (settings.language !== '') dispatch(setLanguage(settings.language));
+ if (settings.server !== '') dispatch(setApi(settings.server));
+ if (settings.upvotePercent !== '') {
+ const percent = Number(settings.upvotePercent);
+ dispatch(setPostUpvotePercent(percent));
+ dispatch(setCommentUpvotePercent(percent));
+ }
+ if (settings.isDefaultFooter !== '') dispatch(isDefaultFooter(settings.isDefaultFooter)); //TODO: remove as not being used
+
+ if (settings.nsfw !== '') dispatch(setNsfw(settings.nsfw));
+
+ dispatch(setCurrency(settings.currency !== '' ? settings.currency : 'usd'));
+
+ if (settings.notification !== '') {
+ dispatch(
+ changeNotificationSettings({
+ type: 'notification',
+ action: settings.notification,
+ }),
+ );
+
+ dispatch(changeAllNotificationSettings(settings));
}
- //reset certain properties
- dispatch(hideActionModal());
- dispatch(hideProfileModal());
- dispatch(toastNotification(''));
- dispatch(setRcOffer(false));
-
-
- const settings = await getSettings();
-
- if (settings) {
- const isDarkMode = Appearance.getColorScheme() === 'dark';
- dispatch(isDarkTheme(settings.isDarkTheme !== null ? settings.isDarkTheme : isDarkMode));
- dispatch(setColorTheme(THEME_OPTIONS.findIndex(item => item.value === settings.isDarkTheme)));
- await dispatch(isPinCodeOpen(!!settings.isPinCodeOpen));
- if (settings.language !== '') dispatch(setLanguage(settings.language));
- if (settings.server !== '') dispatch(setApi(settings.server));
- if (settings.upvotePercent !== '') {
- const percent = Number(settings.upvotePercent);
- dispatch(setPostUpvotePercent(percent));
- dispatch(setCommentUpvotePercent(percent));
- }
- if (settings.isDefaultFooter !== '') dispatch(isDefaultFooter(settings.isDefaultFooter)); //TODO: remove as not being used
-
-
- if (settings.nsfw !== '') dispatch(setNsfw(settings.nsfw));
-
- dispatch(setCurrency(settings.currency !== '' ? settings.currency : 'usd'));
-
- if (settings.notification !== '') {
- dispatch(
- changeNotificationSettings({
- type: 'notification',
- action: settings.notification,
- }),
- );
-
- dispatch(changeAllNotificationSettings(settings));
- }
-
- await dispatch(setSettingsMigrated(true))
- }
-}
-
-
+ await dispatch(setSettingsMigrated(true));
+ }
+};
//migrates local user data to use default pin encruption instead of user pin encryption
export const migrateUserEncryption = async (dispatch, currentAccount, encUserPin, onFailure) => {
+ const oldPinCode = decryptKey(encUserPin, Config.PIN_KEY);
- const oldPinCode = decryptKey(encUserPin, Config.PIN_KEY);
+ if (oldPinCode === undefined || oldPinCode === Config.DEFAULT_PIN) {
+ return;
+ }
- if (oldPinCode === undefined || oldPinCode === Config.DEFAULT_PIN) {
- return;
+ try {
+ const pinData = {
+ pinCode: Config.DEFAULT_PIN,
+ username: currentAccount.username,
+ oldPinCode,
+ };
+
+ const response = updatePinCode(pinData);
+
+ const _currentAccount = currentAccount;
+ _currentAccount.local = response;
+
+ dispatch(
+ updateCurrentAccount({
+ ..._currentAccount,
+ }),
+ );
+
+ const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
+ dispatch(setPinCode(encryptedPin));
+ } catch (err) {
+ console.warn('pin update failure: ', err);
+ }
+
+ dispatch(setEncryptedUnlockPin(encUserPin));
+
+ const realmData = await getUserDataWithUsername(currentAccount.name);
+
+ let _currentAccount = currentAccount;
+ _currentAccount.username = _currentAccount.name;
+ _currentAccount.local = realmData[0];
+
+ try {
+ const pinHash = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
+ //migration script for previously mast key based logged in user not having access token
+ if (realmData[0].authType !== AUTH_TYPE.STEEM_CONNECT && realmData[0].accessToken === '') {
+ _currentAccount = await migrateToMasterKeyWithAccessToken(
+ _currentAccount,
+ realmData[0],
+ pinHash,
+ );
}
+ //refresh access token
+ const encryptedAccessToken = await refreshSCToken(_currentAccount.local, Config.DEFAULT_PIN);
+ _currentAccount.local.accessToken = encryptedAccessToken;
+ } catch (error) {
+ onFailure(error);
+ }
- try {
- const pinData = {
- pinCode: Config.DEFAULT_PIN,
- username: currentAccount.username,
- oldPinCode,
- };
-
- const response = updatePinCode(pinData)
-
- const _currentAccount = currentAccount;
- _currentAccount.local = response;
-
- dispatch(
- updateCurrentAccount({
- ..._currentAccount,
- }),
- );
-
- const encryptedPin = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
- dispatch(setPinCode(encryptedPin));
-
- } catch (err) {
- console.warn('pin update failure: ', err);
- }
-
-
- dispatch(setEncryptedUnlockPin(encUserPin))
-
- const realmData = await getUserDataWithUsername(currentAccount.name)
-
- let _currentAccount = currentAccount;
- _currentAccount.username = _currentAccount.name;
- _currentAccount.local = realmData[0];
-
- try {
- const pinHash = encryptKey(Config.DEFAULT_PIN, Config.PIN_KEY);
- //migration script for previously mast key based logged in user not having access token
- if (
- realmData[0].authType !== AUTH_TYPE.STEEM_CONNECT &&
- realmData[0].accessToken === ''
- ) {
- _currentAccount = await migrateToMasterKeyWithAccessToken(
- _currentAccount,
- realmData[0],
- pinHash,
- );
- }
-
- //refresh access token
- const encryptedAccessToken = await refreshSCToken(_currentAccount.local, Config.DEFAULT_PIN);
- _currentAccount.local.accessToken = encryptedAccessToken;
- } catch (error) {
- onFailure(error)
- }
-
- //get unread notifications
- try {
- _currentAccount.unread_activity_count = await getUnreadNotificationCount();
- _currentAccount.pointsSummary = await getPointsSummary(_currentAccount.username);
- _currentAccount.mutes = await getMutes(_currentAccount.username);
- } catch (err) {
- console.warn(
- 'Optional user data fetch failed, account can still function without them',
- err,
- );
- }
-
- dispatch(updateCurrentAccount({ ..._currentAccount }));
- dispatch(fetchSubscribedCommunities(_currentAccount.username));
-
-}
-
+ //get unread notifications
+ try {
+ _currentAccount.unread_activity_count = await getUnreadNotificationCount();
+ _currentAccount.pointsSummary = await getPointsSummary(_currentAccount.username);
+ _currentAccount.mutes = await getMutes(_currentAccount.username);
+ } catch (err) {
+ console.warn('Optional user data fetch failed, account can still function without them', err);
+ }
+ dispatch(updateCurrentAccount({ ..._currentAccount }));
+ dispatch(fetchSubscribedCommunities(_currentAccount.username));
+};
const reduxMigrations = {
- 0: (state) => {
- const upvotePercent = state.application.upvotePercent;
- state.application.postUpvotePercent = upvotePercent;
- state.application.commentUpvotePercent = upvotePercent
- state.application.upvotePercent = undefined;
- return state
- },
- 1: (state) => {
- state.application.notificationDetails.favoriteNotification = true
- return state;
- }
-}
+ 0: (state) => {
+ const upvotePercent = state.application.upvotePercent;
+ state.application.postUpvotePercent = upvotePercent;
+ state.application.commentUpvotePercent = upvotePercent;
+ state.application.upvotePercent = undefined;
+ return state;
+ },
+ 1: (state) => {
+ state.application.notificationDetails.favoriteNotification = true;
+ return state;
+ },
+};
export default {
- migrateSettings,
- migrateUserEncryption,
- reduxMigrations,
-}
+ migrateSettings,
+ migrateUserEncryption,
+ reduxMigrations,
+};
diff --git a/yarn.lock b/yarn.lock
index abdaedc85..268d38062 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8933,7 +8933,11 @@ react-native-image-zoom-viewer@^2.2.27:
dependencies:
react-native-image-pan-zoom "^2.1.9"
-react-native-iphone-x-helper@^1.0.3, react-native-iphone-x-helper@^1.3.1:
+react-native-iphone-x-helper@Norcy/react-native-iphone-x-helper:
+ version "2.0.0"
+ resolved "https://codeload.github.com/Norcy/react-native-iphone-x-helper/tar.gz/aff3730b2614947a72bcdd86e35ba0217ca1054c"
+
+react-native-iphone-x-helper@^1.0.3:
version "1.3.1"
resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010"
integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==