This commit is contained in:
Nouman Tahir 2022-09-26 15:28:13 +05:00
parent eb8c91a155
commit 485e6e4c77
10 changed files with 258 additions and 285 deletions

View File

@ -25,8 +25,16 @@ class ContainerHeaderView extends PureComponent {
// Component Functions
render() {
const { color, defaultTitle, fontSize, hasSeperator, iconName, isBoldTitle, title, isCenter } =
this.props;
const {
color,
defaultTitle,
fontSize,
hasSeperator,
iconName,
isBoldTitle,
title,
isCenter,
} = this.props;
return (
<View style={[styles.wrapper, hasSeperator && styles.hasTopBorder]}>

View File

@ -37,8 +37,16 @@ class MainButton extends Component {
};
_getBody = () => {
const { isLoading, text, secondText, iconColor, iconName, source, iconType, textStyle } =
this.props;
const {
isLoading,
text,
secondText,
iconColor,
iconName,
source,
iconType,
textStyle,
} = this.props;
if (isLoading) {
this._getIndicator();

View File

@ -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<TNode>) => {
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 <AutoHeightImage
contentWidth={maxImgWidth}
imgUrl={tnode.children[0].attributes.src}
isAnchored={false}
activeOpacity={0.8}
onPress={_onPress}
/>
return (
<AutoHeightImage
contentWidth={maxImgWidth}
imgUrl={tnode.children[0].attributes.src}
isAnchored={false}
activeOpacity={0.8}
onPress={_onPress}
/>;
);
}
return <InternalRenderer tnode={tnode} onPress={_onPress} {...props} />;
};
const _imageRenderer = ({ tnode }: CustomRendererProps<TNode>) => {
const imgUrl = tnode.attributes.src;
const _onPress = () => {
@ -280,15 +268,15 @@ export const PostHtmlRenderer = memo(
return <TDefaultRenderer {...props} />;
};
//based on number of columns a table have, sets scroll enabled or disable, also adjust table full width
const _tableRenderer = ({ InternalRenderer, ...props }: CustomRendererProps<TNode>) => {
// 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(
<ScrollView horizontal={true} scrollEnabled={isScrollable}>
<InternalRenderer {...props} />
</ScrollView>
)
}
);
};
// iframe renderer for rendering iframes in body
@ -313,19 +301,10 @@ export const PostHtmlRenderer = memo(
handleVideoPress(iframeProps.source.uri);
}
};
return (
<VideoThumb contentWidth={contentWidth} onPress={_onPress} />
)
return <VideoThumb contentWidth={contentWidth} onPress={_onPress} />;
} else {
return (
<VideoPlayer
mode='uri'
uri={iframeProps.source.uri}
contentWidth={contentWidth}
/>
);
return <VideoPlayer mode="uri" uri={iframeProps.source.uri} contentWidth={contentWidth} />;
}
};
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,
},
}),
[],
);

View File

@ -56,8 +56,13 @@ class TabBar extends PureComponent {
};
_renderUnderline = () => {
const { tabs, tabUnderlineDefaultWidth, tabUnderlineScaleX, scrollValue, underlineStyle } =
this.props;
const {
tabs,
tabUnderlineDefaultWidth,
tabUnderlineScaleX,
scrollValue,
underlineStyle,
} = this.props;
const { activeColor } = this.state;
const containerWidth = getWindowDimensions().nativeWidth;

View File

@ -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<typeof store.getState>
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
export type AppDispatch = typeof store.dispatch;

View File

@ -643,8 +643,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;

View File

@ -50,8 +50,7 @@ const RegisterScreen = ({ navigation, route }) => {
}, []);
const _handleEmailChange = (value) => {
const re =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
setIsEmailValid(re.test(value));
setEmail(value);
};

View File

@ -39,8 +39,9 @@ const CommunitiesResultsContainer = ({ children, searchValue }) => {
// handle cache when searchResultsScreen data updates in communities reducer
useEffect(() => {
if (subscribingCommunitiesInSearchResultsScreen && selectedCommunityItem) {
const { status } =
subscribingCommunitiesInSearchResultsScreen[selectedCommunityItem.communityId];
const { status } = subscribingCommunitiesInSearchResultsScreen[
selectedCommunityItem.communityId
];
if (status === statusMessage.SUCCESS) {
dispatch(updateSubscribedCommunitiesCache(selectedCommunityItem));
}

View File

@ -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,
};

View File

@ -125,9 +125,12 @@ export default (url) => {
}
if (
['https://ecency.com', 'https://hive.blog', 'https://peakd.com', 'https://leofinance.io'].some(
(x) => url.startsWith(x),
)
[
'https://ecency.com',
'https://hive.blog',
'https://peakd.com',
'https://leofinance.io',
].some((x) => url.startsWith(x))
) {
return parseAuthorPermlink(url);
}