Merge branch 'development' into nt/use-query-notifications

# Conflicts:
#	src/providers/ecency/ecency.ts
This commit is contained in:
Nouman Tahir 2022-09-23 14:05:03 +05:00
commit 320de3310b
13 changed files with 706 additions and 793 deletions

View File

@ -107,7 +107,6 @@
"react-native-highlight-words": "^1.0.1",
"react-native-iap": "^7.5.6",
"react-native-image-crop-picker": "^0.35.2",
"react-native-image-size": "^1.1.3",
"react-native-image-zoom-viewer": "^2.2.27",
"react-native-iphone-x-helper": "^1.3.1",
"react-native-keyboard-aware-scroll-view": "^0.9.1",

View File

@ -14,16 +14,9 @@ export default EStyleSheet.create({
body: {
marginHorizontal: 9,
},
image: {
margin: 0,
alignItems: 'center',
alignSelf: 'center',
//height: 200,
//width: '$deviceWidth - 16',
borderRadius: 8,
backgroundColor: '$primaryLightGray',
// paddingVertical: 10,
marginVertical: 5,
thumbnail: {
width: '$deviceWidth - 16',
height: 300
},
postDescripton: {
flexDirection: 'column',

View File

@ -1,7 +1,6 @@
import React, { useRef, useState, useEffect, Fragment } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { injectIntl } from 'react-intl';
import ImageSize from 'react-native-image-size';
// Utils
import { getTimeFromNow } from '../../../utils/time';
@ -9,20 +8,14 @@ import { getTimeFromNow } from '../../../utils/time';
// Components
import { PostHeaderDescription } from '../../postElements';
import { IconButton } from '../../iconButton';
import ProgressiveImage from '../../progressiveImage';
import { OptionsModal } from '../../atoms';
// Styles
import styles from './draftListItemStyles';
import { ScheduledPostStatus } from '../../../providers/ecency/ecency.types';
import { PopoverWrapper } from '../../popoverWrapper/popoverWrapperView';
import getWindowDimensions from '../../../utils/getWindowDimensions';
import FastImage from 'react-native-fast-image';
// Defaults
const DEFAULT_IMAGE =
'https://images.ecency.com/DQmT8R33geccEjJfzZEdsRHpP3VE8pu3peRCnQa1qukU4KR/no_image_3x.png';
const dim = getWindowDimensions();
const DraftListItemView = ({
title,
@ -45,32 +38,13 @@ const DraftListItemView = ({
}) => {
const actionSheet = useRef(null);
const moveActionSheet = useRef(null);
const [calcImgHeight, setCalcImgHeight] = useState(300);
const [deleteRequested, setIsDeleteRequested] = useState(false);
useEffect(() => {
if (deleteRequested && !isDeleting) {
setIsDeleteRequested(false);
}
}, [isDeleting]);
// Component Life Cycles
useEffect(() => {
let _isMounted = false;
if (image) {
if (!_isMounted) {
ImageSize.getSize(thumbnail.uri).then((size) => {
setCalcImgHeight(Math.floor((size.height / size.width) * dim.width));
});
}
}
return () => {
_isMounted = true;
};
}, []);
const _onItemPress = () => {
if (isSchedules) {
moveActionSheet.current.show();
@ -85,24 +59,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 (
<Fragment>
@ -145,13 +119,10 @@ const DraftListItemView = ({
<View style={styles.body}>
<TouchableOpacity onPress={_onItemPress}>
{image !== null && (
<ProgressiveImage
<FastImage
source={image}
thumbnailSource={thumbnail}
style={[
styles.thumbnail,
{ width: dim.width - 16, height: Math.min(calcImgHeight, dim.height) },
]}
style={styles.thumbnail}
resizeMode={FastImage.resizeMode.cover}
/>
)}
<View style={[styles.postDescripton]}>

View File

@ -6,14 +6,24 @@ import { upload } from '../../config/imageApi';
import serverList from '../../config/serverListApi';
import { SERVER_LIST } from '../../constants/options/api';
import { parsePost } from '../../utils/postParser';
import { convertCommentHistory, convertLatestQuotes, convertReferral, convertReferralStat } from './converters';
import { CommentHistoryItem, LatestMarketPrices, NotificationFilters, ReceivedVestingShare, Referral, ReferralStat } from './ecency.types';
import {
convertCommentHistory,
convertLatestQuotes,
convertReferral,
convertReferralStat,
} from './converters';
import {
CommentHistoryItem,
LatestMarketPrices,
NotificationFilters,
ReceivedVestingShare,
Referral,
ReferralStat,
} from './ecency.types';
/**
/**
* ************************************
* CURRENCY APIS IMPLEMENTATION
* CURRENCY APIS IMPLEMENTATION
* ************************************
*/
@ -30,10 +40,10 @@ export const getCurrencyRate = (currency) =>
export const getLatestQuotes = async (currencyRate: number): Promise<LatestMarketPrices> => {
try {
console.log('using currency rate', currencyRate);
const res = await ecencyApi.get(`/private-api/market-data/latest`);
const res = await ecencyApi.get('/private-api/market-data/latest');
if (!res.data) {
throw new Error("No quote data returned");
throw new Error('No quote data returned');
}
const data = convertLatestQuotes(res.data, currencyRate);
@ -43,10 +53,9 @@ export const getLatestQuotes = async (currencyRate: number): Promise<LatestMarke
} catch (error) {
bugsnagInstance.notify(error);
console.warn(error);
throw error
throw error;
}
}
};
export const getCurrencyTokenRate = (currency, token) =>
ecencyApi
@ -57,27 +66,22 @@ export const getCurrencyTokenRate = (currency, token) =>
return 0;
});
export const getReceivedVestingShares = async (username: string): Promise<ReceivedVestingShare[]> => {
export const getReceivedVestingShares = async (
username: string,
): Promise<ReceivedVestingShare[]> => {
try {
const res = await ecencyApi.get(`/private-api/received-vesting/${username}`);
console.log("Vesting Shares User", username, res.data);
console.log('Vesting Shares User', username, res.data);
if (!res.data || !res.data.list) {
throw new Error("No vesting shares for user")
throw new Error('No vesting shares for user');
}
return res.data.list;
} catch (error) {
bugsnagInstance.notify(error);
console.warn(error);
throw error
throw error;
}
}
};
/**
* returns list of saved drafts on ecency server
@ -90,24 +94,21 @@ export const getDrafts = async () => {
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* @params draftId
*/
export const deleteDraft = async (draftId: string) => {
try {
const data = { id: draftId }
const res = await ecencyApi.post(`/private-api/drafts-delete`, data);
return res.data || []
const data = { id: draftId };
const res = await ecencyApi.post('/private-api/drafts-delete', data);
return res.data || [];
} catch (error) {
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* @params title
@ -117,8 +118,8 @@ export const deleteDraft = async (draftId: string) => {
*/
export const addDraft = async (title: string, body: string, tags: string, meta: Object) => {
try {
const data = { title, body, tags, meta }
const res = await ecencyApi.post('/private-api/drafts-add', data)
const data = { title, body, tags, meta };
const res = await ecencyApi.post('/private-api/drafts-add', data);
const { drafts } = res.data;
if (drafts) {
return drafts.pop(); //return recently saved last draft in the list
@ -129,8 +130,7 @@ export const addDraft = async (title: string, body: string, tags: string, meta:
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* @params draftId
@ -139,14 +139,20 @@ export const addDraft = async (title: string, body: string, tags: string, meta:
* @params tags
* @params meta
*/
export const updateDraft = async (draftId: string, title: string, body: string, tags: string, meta: Object) => {
export const updateDraft = async (
draftId: string,
title: string,
body: string,
tags: string,
meta: Object,
) => {
try {
const data = { id: draftId, title, body, tags, meta }
const res = await ecencyApi.post(`/private-api/drafts-update`, data)
const data = { id: draftId, title, body, tags, meta };
const res = await ecencyApi.post('/private-api/drafts-update', data);
if (res.data) {
return res.data
return res.data;
} else {
throw new Error("No data returned in response")
throw new Error('No data returned in response');
}
} catch (error) {
bugsnagInstance.notify(error);
@ -154,31 +160,29 @@ export const updateDraft = async (draftId: string, title: string, body: string,
}
};
/**
/**
* ************************************
* BOOKMARKS ECENCY APIS IMPLEMENTATION
* BOOKMARKS ECENCY APIS IMPLEMENTATION
* ************************************
*/
/**
* Adds post to user's bookmarks
* @param author
* @param permlink
* @param author
* @param permlink
* @returns array of saved bookmarks
*/
export const addBookmark = async (author: string, permlink: string) => {
try {
const data = { author, permlink };
const response = await ecencyApi.post(`/private-api/bookmarks-add`, data);
const response = await ecencyApi.post('/private-api/bookmarks-add', data);
return response.data;
} catch (error) {
console.warn("Failed to add bookmark", error)
bugsnagInstance.notify(error)
throw error
console.warn('Failed to add bookmark', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* fetches saved bookmarks of user
@ -186,15 +190,14 @@ export const addBookmark = async (author: string, permlink: string) => {
*/
export const getBookmarks = async () => {
try {
const response = await ecencyApi.post(`/private-api/bookmarks`);
const response = await ecencyApi.post('/private-api/bookmarks');
return response.data;
} catch (error) {
console.warn("Failed to get saved bookmarks", error)
bugsnagInstance.notify(error)
throw error
console.warn('Failed to get saved bookmarks', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Deletes bookmark from user's saved bookmarks
@ -203,52 +206,48 @@ export const getBookmarks = async () => {
*/
export const deleteBookmark = async (bookmarkId: string) => {
try {
const data = { id: bookmarkId }
const response = await ecencyApi.post(`/private-api/bookmarks-delete`, data);
const data = { id: bookmarkId };
const response = await ecencyApi.post('/private-api/bookmarks-delete', data);
return response.data;
} catch (error) {
console.warn("Failed to delete bookmark", error)
bugsnagInstance.notify(error)
throw error
console.warn('Failed to delete bookmark', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
export const addReport = async (type: 'content' | 'user', data: string) => {
try {
const response = await api
.post('/report', {
type,
data
})
return response.data
const response = await api.post('/report', {
type,
data,
});
return response.data;
} catch (err) {
console.warn("Failed to report to ecency")
console.warn('Failed to report to ecency');
bugsnagInstance.notify(err);
throw err;
}
}
};
export const deleteAccount = async (username: string) => {
try {
const response = await api
.post('/request-delete', {
username,
})
return response.data
const response = await api.post('/request-delete', {
username,
});
return response.data;
} catch (err) {
console.warn("Failed to report to ecency")
console.warn('Failed to report to ecency');
bugsnagInstance.notify(err);
throw err;
}
}
};
/**
* ************************************
* FAVOURITES ECENCY APIS IMPLEMENTATION
* ************************************
*/
/**
* ************************************
* FAVOURITES ECENCY APIS IMPLEMENTATION
* ************************************
*/
/**
* Fetches user favourites
@ -256,14 +255,14 @@ export const deleteAccount = async (username: string) => {
*/
export const getFavorites = async () => {
try {
const response = await ecencyApi.post(`/private-api/favorites`)
const response = await ecencyApi.post('/private-api/favorites');
return response.data;
} catch (error) {
console.warn("Failed to get favorites", error);
console.warn('Failed to get favorites', error);
bugsnagInstance.notify(error);
throw error
throw error;
}
}
};
/**
* Checks if user is precent in current user's favourites
@ -273,13 +272,13 @@ export const getFavorites = async () => {
export const checkFavorite = async (targetUsername: string) => {
try {
const data = { account: targetUsername };
const response = await ecencyApi.post(`/private-api/favorites-check`, data);
const response = await ecencyApi.post('/private-api/favorites-check', data);
return response.data || false;
} catch (error) {
console.warn("Failed to check favorite", error);
console.warn('Failed to check favorite', error);
bugsnagInstance.notify(error);
}
}
};
/**
* Adds taget user to current user's favourites
@ -289,15 +288,14 @@ export const checkFavorite = async (targetUsername: string) => {
export const addFavorite = async (targetUsername: string) => {
try {
const data = { account: targetUsername };
const response = await ecencyApi.post(`/private-api/favorites-add`, data);
const response = await ecencyApi.post('/private-api/favorites-add', data);
return response.data;
} catch (error) {
console.warn("Failed to add user favorites", error);
console.warn('Failed to add user favorites', error);
bugsnagInstance.notify(error);
throw error
throw error;
}
}
};
/**
* Removes taget user to current user's favourites
@ -307,38 +305,35 @@ export const addFavorite = async (targetUsername: string) => {
export const deleteFavorite = async (targetUsername: string) => {
try {
const data = { account: targetUsername };
const response = await ecencyApi.post(`/private-api/favorites-delete`, data);
const response = await ecencyApi.post('/private-api/favorites-delete', data);
return response.data;
} catch (error) {
console.warn("Failed to add user favorites", error);
console.warn('Failed to add user favorites', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
/**
* ************************************
* SNIPPETS ECENCY APIS IMPLEMENTATION
* SNIPPETS ECENCY APIS IMPLEMENTATION
* ************************************
*/
/**
* Fetches all saved user fragments/snippets from ecency
* @returns array of fragments
*/
export const getFragments = async () => {
try {
const response = await ecencyApi.post(`/private-api/fragments`);
const response = await ecencyApi.post('/private-api/fragments');
return response.data;
} catch (error) {
console.warn("Failed to get fragments", error);
bugsnagInstance.notify(error)
console.warn('Failed to get fragments', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Adds new fragment/snippets to user's saved fragments/snippets
@ -350,14 +345,14 @@ export const getFragments = async () => {
export const addFragment = async (title: string, body: string) => {
try {
const data = { title, body };
const response = await ecencyApi.post(`/private-api/fragments-add`, data);
const response = await ecencyApi.post('/private-api/fragments-add', data);
return response.data;
} catch (error) {
console.warn("Failed to add fragment", error);
bugsnagInstance.notify(error)
console.warn('Failed to add fragment', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Updates a fragment content using fragment id
@ -369,14 +364,14 @@ export const addFragment = async (title: string, body: string) => {
export const updateFragment = async (fragmentId: string, title: string, body: string) => {
try {
const data = { id: fragmentId, title, body };
const response = await ecencyApi.post(`/private-api/fragments-update`, data);
const response = await ecencyApi.post('/private-api/fragments-update', data);
return response.data;
} catch (error) {
console.warn("Failed to update fragment", error);
bugsnagInstance.notify(error)
console.warn('Failed to update fragment', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Deletes user saved fragment using specified fragment id
@ -386,26 +381,24 @@ export const updateFragment = async (fragmentId: string, title: string, body: st
export const deleteFragment = async (fragmentId: string) => {
try {
const data = { id: fragmentId };
const response = await ecencyApi.post(`/private-api/fragments-delete`, data);
const response = await ecencyApi.post('/private-api/fragments-delete', data);
return response.data;
} catch (error) {
console.warn("Failed to delete fragment", error);
bugsnagInstance.notify(error)
console.warn('Failed to delete fragment', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* ************************************
* ACTIVITES ECENCY APIS IMPLEMENTATION
* ************************************
*/
/**
* ************************************
* ACTIVITES ECENCY APIS IMPLEMENTATION
* ************************************
*/
export const getLeaderboard = async (duration: 'day' | 'week' | 'month') => {
try {
const response = await ecencyApi.get(`private-api/leaderboard/${duration}`)
const response = await ecencyApi.get(`private-api/leaderboard/${duration}`);
const rawData = response.data;
if (!rawData || !isArray(rawData)) {
@ -413,10 +406,10 @@ export const getLeaderboard = async (duration: 'day' | 'week' | 'month') => {
}
return rawData;
} catch (error) {
bugsnagInstance.notify(error)
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* fetches notifications from ecency server using filter and since props
@ -429,85 +422,81 @@ export const getNotifications = async (data: {
limit?: number,
}) => {
try {
const response = await ecencyApi.post(`/private-api/notifications`, data);
const response = await ecencyApi.post('/private-api/notifications', data);
return response.data;
} catch (error) {
console.warn("Failed to get notifications", error)
bugsnagInstance.notify(error)
console.warn('Failed to get notifications', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
export const getUnreadNotificationCount = async (accessToken?: string) => {
try {
const data = accessToken ? { code: accessToken } : {}
const response = await ecencyApi.post(`/private-api/notifications/unread`, data)
const data = accessToken ? { code: accessToken } : {};
const response = await ecencyApi.post('/private-api/notifications/unread', data);
return response.data ? response.data.count : 0;
} catch (error) {
bugsnagInstance.notify(error);
return 0;
}
}
};
export const markNotifications = async (id: string | null = null) => {
try {
const data = id ? { id } : {};
const response = await ecencyApi.post((`/private-api/notifications/mark`), data);
return response.data
const response = await ecencyApi.post('/private-api/notifications/mark', data);
return response.data;
} catch (error) {
bugsnagInstance.notify(error);
throw error
throw error;
}
};
export const setPushToken = async (data, accessToken = null) => {
try {
if (!data.username) {
console.log("skipping push token setting, as no user is provided")
console.log('skipping push token setting, as no user is provided');
return;
}
if(accessToken){
data.code = accessToken
if (accessToken) {
data.code = accessToken;
}
const res = await await ecencyApi.post((`/private-api/register-device`), data);
const res = await await ecencyApi.post('/private-api/register-device', data);
return res.data;
} catch (error) {
console.warn("Failed to set push token on server")
console.warn('Failed to set push token on server');
bugsnagInstance.notify(error);
}
}
};
/**
/**
* ************************************
* SEARCH ECENCY APIS IMPLEMENTATION
* SEARCH ECENCY APIS IMPLEMENTATION
* ************************************
*/
export const search = async (data: {
q: string,
sort: string,
hideLow: string,
since?: string,
scroll_id?: string
q: string;
sort: string;
hideLow: string;
since?: string;
scroll_id?: string;
}) => {
try {
const response = await ecencyApi.post('/search-api/search', data);
return response.data;
} catch (error) {
console.warn("Search failed", error);
console.warn('Search failed', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
*
*
* @param q query
* @returns array of path strings
*/
@ -517,15 +506,14 @@ export const searchPath = async (q: string) => {
const response = await ecencyApi.post('/search-api/search-path', data);
return response.data;
} catch (error) {
console.warn("path search failed", error)
console.warn('path search failed', error);
bugsnagInstance.notify(error);
throw error
throw error;
}
}
};
/**
*
*
* @param q query
* @param limit number of posts to fetch
* @param random random
@ -537,19 +525,18 @@ export const searchAccount = async (q: string = '', limit: number = 20, random:
q,
limit,
random,
}
const response = await ecencyApi.post(`/search-api/search-account`, data)
};
const response = await ecencyApi.post('/search-api/search-account', data);
return response.data;
} catch (error) {
console.warn("account search failed", error)
console.warn('account search failed', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
*
*
* @param q query
* @param limit number of posts to fetch
* @param random random
@ -561,32 +548,30 @@ export const searchTag = async (q: string = '', limit: number = 20, random: numb
q,
limit,
random,
}
const response = await ecencyApi.post(`/search-api/search-tag`, data);
};
const response = await ecencyApi.post('/search-api/search-tag', data);
return response.data;
} catch (error) {
console.warn("tag search failed", error)
console.warn('tag search failed', error);
bugsnagInstance.notify(error);
throw error
throw error;
}
}
};
/**
/**
* ************************************
* SCHEDULES ECENCY APIS IMPLEMENTATION
* SCHEDULES ECENCY APIS IMPLEMENTATION
* ************************************
*/
/**
* Adds new post to scheduled posts
* @param permlink
* @param title
* @param body
* @param meta
* @param options
* @param scheduleDate
* @param permlink
* @param title
* @param body
* @param meta
* @param options
* @param scheduleDate
* @returns All scheduled posts
*/
export const addSchedule = async (
@ -595,7 +580,7 @@ export const addSchedule = async (
body: string,
meta: any,
options: any,
scheduleDate: string
scheduleDate: string,
) => {
try {
const data = {
@ -606,16 +591,15 @@ export const addSchedule = async (
schedule: scheduleDate,
options,
reblog: 0,
}
const response = await ecencyApi
.post('/private-api/schedules-add', data)
};
const response = await ecencyApi.post('/private-api/schedules-add', data);
return response.data;
} catch (error) {
console.warn("Failed to add post to schedule", error)
console.warn('Failed to add post to schedule', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Fetches all scheduled posts against current user
@ -623,90 +607,89 @@ export const addSchedule = async (
*/
export const getSchedules = async () => {
try {
const response = await ecencyApi.post(`/private-api/schedules`)
const response = await ecencyApi.post('/private-api/schedules');
return response.data || [];
} catch (error) {
console.warn("Failed to get schedules")
bugsnagInstance.notify(error)
console.warn('Failed to get schedules');
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Removes post from scheduled posts using post id;
* @param id
* @param id
* @returns array of scheduled posts
*/
export const deleteScheduledPost = async (id: string) => {
try {
const data = { id };
const response = await ecencyApi.post(`/private-api/schedules-delete`, data);
const response = await ecencyApi.post('/private-api/schedules-delete', data);
return response.data || [];
} catch (error) {
console.warn("Failed to delete scheduled post")
bugsnagInstance.notify(error)
console.warn('Failed to delete scheduled post');
bugsnagInstance.notify(error);
throw error;
}
}
};
/**
* Moves scheduled post to draft using schedule id
* @param id
* @param id
* @returns Array of scheduled posts
*/
export const moveScheduledToDraft = async (id: string) => {
try {
const data = { id }
const response = await ecencyApi.post(`/private-api/schedules-move`, data);
const data = { id };
const response = await ecencyApi.post('/private-api/schedules-move', data);
return response.data;
} catch (error) {
console.warn("Failed to move scheduled post to drafts")
bugsnagInstance.notify(error)
console.warn('Failed to move scheduled post to drafts');
bugsnagInstance.notify(error);
throw error;
}
}
};
// Old image service
/**
/**
* ************************************
* IMAGES ECENCY APIS IMPLEMENTATION
* IMAGES ECENCY APIS IMPLEMENTATION
* ************************************
*/
export const getImages = async () => {
try {
const response = await ecencyApi.post('/private-api/images')
const response = await ecencyApi.post('/private-api/images');
return response.data;
} catch (error) {
console.warn('Failed to get images', error);
bugsnagInstance.notify(error);
}
}
};
export const addImage = async (url: string) => {
try {
const data = { url };
const response = await ecencyApi.post(`/private-api/images-add`, data);
const response = await ecencyApi.post('/private-api/images-add', data);
return response.data;
} catch (error) {
console.warn('Failed to add image', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
export const deleteImage = async (id: string) => {
try {
const data = { id };
const response = await ecencyApi.post(`/private-api/images-delete`, data);
const response = await ecencyApi.post('/private-api/images-delete', data);
return response.data;
} catch (error) {
console.warn('Failed to delete image', error);
bugsnagInstance.notify(error);
throw error;
}
}
};
export const uploadImage = async (media, username, sign, uploadProgress = null) => {
try {
@ -719,16 +702,15 @@ export const uploadImage = async (media, username, sign, uploadProgress = null)
const fData = new FormData();
fData.append('file', file);
const res = await upload(fData, username, sign, uploadProgress)
const res = await upload(fData, username, sign, uploadProgress);
if (!res || !res.data) {
throw new Error("Returning response missing media data");
throw new Error('Returning response missing media data');
}
return res;
} catch (error) {
console.warn("Image upload failed", error)
return { error }
console.warn('Image upload failed', error);
return { error };
}
};
@ -736,8 +718,6 @@ export const uploadImage = async (media, username, sign, uploadProgress = null)
export const getNodes = () => serverList.get().then((resp) => resp.data.hived || SERVER_LIST);
/**
* refreshes access token using refresh token
* @param code refresh token
@ -747,16 +727,14 @@ export const getSCAccessToken = async (code: string) => {
try {
const response = await ecencyApi.post('/auth-api/hs-token-refresh', {
code,
})
});
return response.data;
} catch (error) {
console.warn("failed to refresh token")
console.warn('failed to refresh token');
bugsnagInstance.notify(error);
throw error
throw error;
}
}
};
/**
* fetches promoted posts for tab content
@ -772,31 +750,26 @@ export const getPromotedEntries = async (username: string) => {
);
});
} catch (error) {
console.warn("Failed to get promoted enties")
console.warn('Failed to get promoted enties');
bugsnagInstance.notify(error);
return error;
}
};
export const purchaseOrder = (data) =>
api
.post('/purchase-order', data)
.then((resp) => resp.data)
.catch((error) => bugsnagInstance.notify(error));
export const getPostReblogs = (data) =>
api
.get(`/post-reblogs/${data.author}/${data.permlink}`)
.then((resp) => resp.data)
.catch((error) => bugsnagInstance.notify(error));
/**
* Registers new user with ecency and hive, on confirmation sends
* Registers new user with ecency and hive, on confirmation sends
* details to user email
* @param username for new user
* @param email of new user
@ -808,8 +781,8 @@ export const signUp = async (username: string, email: string, referral?: string)
const data = {
username,
email,
referral
}
referral,
};
const response = await ecencyApi.post('/private-api/account-create', data);
return response.status === 202;
} catch (error) {
@ -818,31 +791,35 @@ export const signUp = async (username: string, email: string, referral?: string)
}
};
/**
/**
* ************************************
* REFERRAL API IMPLEMENTATION
* REFERRAL API IMPLEMENTATION
* ************************************
*/
export const getReferralsList = async (username: string, maxId: number | undefined): Promise<Referral[]> => {
export const getReferralsList = async (
username: string,
maxId: number | undefined,
): Promise<Referral[]> => {
try {
const res = await ecencyApi.get(`/private-api/referrals/${username}`, {
params: {
max_id: maxId
}
max_id: maxId,
},
});
console.log('Referrals List', username, res.data);
if (!res.data) {
throw new Error('No Referrals for this user!');
}
const referralsList = res.data.length > 0 ? res.data.map((referralItem: any) => convertReferral(referralItem)) : [];
const referralsList =
res.data.length > 0 ? res.data.map((referralItem: any) => convertReferral(referralItem)) : [];
return referralsList;
} catch (error) {
bugsnagInstance.notify(error);
console.warn(error);
throw error;
}
}
};
export const getReferralsStats = async (username: string): Promise<ReferralStat> => {
try {
@ -857,20 +834,23 @@ export const getReferralsStats = async (username: string): Promise<ReferralStat>
console.warn(error);
throw error;
}
}
};
/**
/**
* ************************************
* EDIT HISTORY API IMPLEMENTATION
* EDIT HISTORY API IMPLEMENTATION
* ************************************
*/
export const getCommentHistory = async (author: string, permlink: string): Promise<CommentHistoryItem[]> => {
export const getCommentHistory = async (
author: string,
permlink: string,
): Promise<CommentHistoryItem[]> => {
try {
const data = {
author,
permlink
}
permlink,
};
const res = await ecencyApi.post('/private-api/comment-history', data);
console.log('comment history', res.data);
if (!res.data) {
@ -881,5 +861,4 @@ export const getCommentHistory = async (author: string, permlink: string): Promi
bugsnagInstance.notify(error);
throw error;
}
}
};

View File

@ -2,105 +2,105 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useIntl } from 'react-intl';
import { useAppDispatch } from '../../hooks';
import { toastNotification } from '../../redux/actions/uiAction';
import { deleteDraft, deleteScheduledPost, getDrafts, getSchedules, moveScheduledToDraft } from '../ecency/ecency';
import {
deleteDraft,
deleteScheduledPost,
getDrafts,
getSchedules,
moveScheduledToDraft,
} from '../ecency/ecency';
import QUERIES from './queryKeys';
/** hook used to return user drafts */
export const useGetDraftsQuery = () => {
return useQuery([QUERIES.DRAFTS.GET], _getDrafts);
return useQuery([QUERIES.DRAFTS.GET], _getDrafts);
};
/** used to return user schedules */
export const useGetSchedulesQuery = () => {
return useQuery([QUERIES.SCHEDULES.GET], _getSchedules);
return useQuery([QUERIES.SCHEDULES.GET], _getSchedules);
};
export const useDraftDeleteMutation = () => {
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
const intl = useIntl();
return useMutation(deleteDraft, {
retry: 3,
onSuccess: (data) => {
console.log("Success draft delete", data);
queryClient.setQueryData([QUERIES.DRAFTS.GET], _sortData(data));
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
}
})
}
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
const intl = useIntl();
return useMutation(deleteDraft, {
retry: 3,
onSuccess: (data) => {
console.log('Success draft delete', data);
queryClient.setQueryData([QUERIES.DRAFTS.GET], _sortData(data));
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
},
});
};
export const useScheduleDeleteMutation = () => {
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
const intl = useIntl();
return useMutation(deleteScheduledPost, {
retry: 3,
onSuccess: (data) => {
console.log("Success scheduled post delete", data);
queryClient.setQueryData([QUERIES.SCHEDULES.GET], _sortData(data));
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
}
})
}
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
const intl = useIntl();
return useMutation(deleteScheduledPost, {
retry: 3,
onSuccess: (data) => {
console.log('Success scheduled post delete', data);
queryClient.setQueryData([QUERIES.SCHEDULES.GET], _sortData(data));
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
},
});
};
export const useMoveScheduleToDraftsMutation = () => {
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
const intl = useIntl();
return useMutation(moveScheduledToDraft, {
retry: 3,
onSuccess: (data) => {
console.log("Moved to drafts data", data);
queryClient.setQueryData([QUERIES.SCHEDULES.GET], _sortData(data))
queryClient.invalidateQueries([
QUERIES.DRAFTS.GET,
])
dispatch(toastNotification(intl.formatMessage({ id: 'alert.success_moved' })))
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
}
})
}
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
const intl = useIntl();
return useMutation(moveScheduledToDraft, {
retry: 3,
onSuccess: (data) => {
console.log('Moved to drafts data', data);
queryClient.setQueryData([QUERIES.SCHEDULES.GET], _sortData(data));
queryClient.invalidateQueries([QUERIES.DRAFTS.GET]);
dispatch(toastNotification(intl.formatMessage({ id: 'alert.success_moved' })));
},
onError: () => {
dispatch(toastNotification(intl.formatMessage({ id: 'alert.fail' })));
},
});
};
const _getDrafts = async () => {
try {
const data = await getDrafts();
return _sortData(data || []);
} catch (err) {
throw new Error('draft.load_error');
}
try {
const data = await getDrafts();
return _sortData(data || []);
} catch (err) {
throw new Error('draft.load_error');
}
};
const _getSchedules = async () => {
try {
const data = await getSchedules();
return _sortDataS(data);
} catch (err) {
throw new Error('drafts.load_error');
}
try {
const data = await getSchedules();
return _sortDataS(data);
} catch (err) {
throw new Error('drafts.load_error');
}
};
const _sortDataS = (data) =>
data.sort((a, b) => {
const dateA = new Date(a.schedule).getTime();
const dateB = new Date(b.schedule).getTime();
data.sort((a, b) => {
const dateA = new Date(a.schedule).getTime();
const dateB = new Date(b.schedule).getTime();
return dateB > dateA ? 1 : -1;
});
return dateB > dateA ? 1 : -1;
});
const _sortData = (data) =>
data.sort((a, b) => {
const dateA = new Date(a.created).getTime();
const dateB = new Date(b.created).getTime();
data.sort((a, b) => {
const dateA = new Date(a.created).getTime();
const dateB = new Date(b.created).getTime();
return dateB > dateA ? 1 : -1;
});
return dateB > dateA ? 1 : -1;
});

View File

@ -2,63 +2,70 @@ import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'rea
import { useIntl } from 'react-intl';
import { View } from 'react-native';
import { BeneficiarySelectionContent, DateTimePicker, MainButton, Modal, SettingsItem } from '../../../components';
import { View as AnimatedView } from 'react-native-animatable';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import {
BeneficiarySelectionContent,
DateTimePicker,
MainButton,
Modal,
SettingsItem,
} from '../../../components';
import styles from './postOptionsModalStyles';
import ThumbSelectionContent from './thumbSelectionContent';
import {View as AnimatedView} from 'react-native-animatable';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
const REWARD_TYPES = [
{
key:'default',
intlId:'editor.reward_default'
key: 'default',
intlId: 'editor.reward_default',
},
{
key:'sp',
intlId:'editor.reward_power_up'
key: 'sp',
intlId: 'editor.reward_power_up',
},
{
key:'dp',
intlId:'editor.reward_decline'
key: 'dp',
intlId: 'editor.reward_decline',
},
]
];
export interface PostOptionsModalRef {
showModal:()=>void;
showModal: () => void;
}
interface PostOptionsModalProps {
body:string;
draftId:string;
thumbUrl:string,
isEdit:boolean;
isCommunityPost:boolean;
body: string;
draftId: string;
thumbUrl: string;
isEdit: boolean;
isCommunityPost: boolean;
rewardType: string;
isUploading: boolean;
handleRewardChange:(rewardType:string)=>void;
handleThumbSelection:(url:string)=>void;
handleScheduleChange:(datetime:string|null)=>void;
handleShouldReblogChange:(shouldReblog:boolean)=>void;
handleFormUpdate:()=>void;
handleRewardChange: (rewardType: string) => void;
handleThumbSelection: (url: string) => void;
handleScheduleChange: (datetime: string | null) => void;
handleShouldReblogChange: (shouldReblog: boolean) => void;
handleFormUpdate: () => void;
}
const PostOptionsModal = forwardRef(({
body,
draftId,
thumbUrl,
isEdit,
isCommunityPost,
rewardType,
isUploading,
handleRewardChange,
handleThumbSelection,
handleScheduleChange,
handleShouldReblogChange,
handleFormUpdate
}: PostOptionsModalProps, ref) => {
const PostOptionsModal = forwardRef(
(
{
body,
draftId,
thumbUrl,
isEdit,
isCommunityPost,
rewardType,
isUploading,
handleRewardChange,
handleThumbSelection,
handleScheduleChange,
handleShouldReblogChange,
handleFormUpdate,
}: PostOptionsModalProps,
ref,
) => {
const intl = useIntl();
const [showModal, setShowModal] = useState(false);
@ -70,84 +77,83 @@ const PostOptionsModal = forwardRef(({
// removed the useeffect causing index reset bug
useEffect(()=>{
if(!scheduleLater){
handleScheduleChange(null)
}else if(scheduledFor) {
handleScheduleChange(scheduledFor)
}
}, [scheduleLater, scheduledFor])
useEffect(() => {
handleShouldReblogChange(shouldReblog)
}, [shouldReblog])
if (!scheduleLater) {
handleScheduleChange(null);
} else if (scheduledFor) {
handleScheduleChange(scheduledFor);
}
}, [scheduleLater, scheduledFor]);
useEffect(() => {
if(!isCommunityPost && shouldReblog){
handleShouldReblogChange(shouldReblog);
}, [shouldReblog]);
useEffect(() => {
if (!isCommunityPost && shouldReblog) {
setShouldReblog(false);
}
}, [isCommunityPost])
}, [isCommunityPost]);
// load rewardtype from props if it is already saved in drafts
useEffect(() => {
if(rewardType){
let rewardTypeKey = REWARD_TYPES.findIndex((item) => item.key === rewardType)
if (rewardType) {
let rewardTypeKey = REWARD_TYPES.findIndex((item) => item.key === rewardType);
setRewardTypeIndex(rewardTypeKey);
}
},[rewardType])
}, [rewardType]);
useImperativeHandle(ref, () => ({
show: () => {
setShowModal(true);
},
}));
show: () => {
setShowModal(true);
},
}));
const _handleRewardChange = (index:number) => {
setRewardTypeIndex(index)
const rewardTypeKey = REWARD_TYPES[index].key
const _handleRewardChange = (index: number) => {
setRewardTypeIndex(index);
const rewardTypeKey = REWARD_TYPES[index].key;
if (handleRewardChange) {
handleRewardChange(rewardTypeKey);
}
}
};
const _handleDatePickerChange = (date:string) => {
const _handleDatePickerChange = (date: string) => {
setScheduledFor(date);
}
};
const _onDonePress = () => {
setShowModal(false);
handleFormUpdate();
}
};
// handle index change here instead of useeffetc
const _handleThumbIndexSelection = (url:string) => {
handleThumbSelection(url)
}
const _handleThumbIndexSelection = (url: string) => {
handleThumbSelection(url);
};
const _renderContent = () => (
<View style={styles.fillSpace}>
<KeyboardAwareScrollView style={styles.fillSpace} >
<KeyboardAwareScrollView style={styles.fillSpace}>
<View style={styles.container}>
{!isEdit && (
<>
<SettingsItem
title={intl.formatMessage({id:'editor.scheduled_for'}) }
title={intl.formatMessage({ id: 'editor.scheduled_for' })}
type="dropdown"
actionType="reward"
options={[
intl.formatMessage({id:"editor.scheduled_immediate"}),
intl.formatMessage({id:"editor.scheduled_later"}),
intl.formatMessage({ id: 'editor.scheduled_immediate' }),
intl.formatMessage({ id: 'editor.scheduled_later' }),
]}
selectedOptionIndex={scheduleLater ? 1 : 0}
handleOnChange={(index)=> {
handleOnChange={(index) => {
setScheduleLater(index === 1);
if (index !== 1) {
handleScheduleChange(null);
}
}}
/>
{scheduleLater && (
<AnimatedView animation="flipInX" duration={700}>
<DateTimePicker
@ -156,23 +162,19 @@ const PostOptionsModal = forwardRef(({
disabled={true}
/>
</AnimatedView>
)}
<SettingsItem
title={intl.formatMessage({
id: 'editor.setting_reward',
})}
type="dropdown"
actionType="reward"
options={
REWARD_TYPES.map((type)=>intl.formatMessage({ id: type.intlId}))
}
options={REWARD_TYPES.map((type) => intl.formatMessage({ id: type.intlId }))}
selectedOptionIndex={rewardTypeIndex}
handleOnChange={_handleRewardChange}
/>
{isCommunityPost && (
<SettingsItem
title={intl.formatMessage({
@ -184,42 +186,33 @@ const PostOptionsModal = forwardRef(({
handleOnChange={setShouldReblog}
/>
)}
</>
</>
)}
<ThumbSelectionContent
<ThumbSelectionContent
body={body}
thumbUrl={thumbUrl}
isUploading={isUploading}
onThumbSelection={_handleThumbIndexSelection}
/>
{!isEdit && (
<BeneficiarySelectionContent
draftId={draftId}
setDisableDone={setDisableDone}
/>
<BeneficiarySelectionContent draftId={draftId} setDisableDone={setDisableDone} />
)}
</View>
</KeyboardAwareScrollView>
<MainButton
style={{...styles.saveButton }}
style={{ ...styles.saveButton }}
isDisable={disableDone}
onPress={_onDonePress}
text={intl.formatMessage({id:"editor.done"})}
/>
text={intl.formatMessage({ id: 'editor.done' })}
/>
</View>
)
);
return (
<Modal
return (
<Modal
isOpen={showModal}
handleOnModalClose={() => {
setShowModal(false);
@ -228,14 +221,14 @@ const PostOptionsModal = forwardRef(({
isFullScreen
isCloseButton
presentationStyle="formSheet"
title={intl.formatMessage({id:"editor.settings_title"})}
title={intl.formatMessage({ id: 'editor.settings_title' })}
animationType="slide"
style={styles.modalStyle}
>
{_renderContent()}
</Modal>
);
});
{_renderContent()}
</Modal>
);
},
);
export default PostOptionsModal
export default PostOptionsModal;

View File

@ -2,60 +2,59 @@ import EStyleSheet from 'react-native-extended-stylesheet';
import { getBottomSpace } from 'react-native-iphone-x-helper';
export default EStyleSheet.create({
sheetContent: {
backgroundColor: '$primaryBackgroundColor',
position:'absolute',
bottom:0,
left:0,
right:0,
zIndex:999
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
zIndex: 999,
},
thumbStyle: {
width: 72,
height: 72,
marginVertical: 8,
marginRight: 8,
borderRadius: 12,
backgroundColor: '$primaryLightGray',
},
checkContainer: {
position: 'absolute',
top: 12,
left: 6,
backgroundColor: '$pureWhite',
borderRadius: 12,
},
settingLabel: {
color: '$primaryDarkGray',
fontSize: 14,
fontWeight: 'bold',
flexGrow: 1,
textAlign: 'left',
},
listContainer: {
paddingBottom: getBottomSpace() + 16,
},
container: {
paddingVertical: 16,
},
bodyWrapper: { flex: 1, paddingTop: 20, paddingBottom: 20 },
inputWrapper: { flexDirection: 'row', alignItems: 'center' },
contentLabel: { color: '$iconColor', marginTop: 4, textAlign: 'left' },
weightInput: { width: 80 },
weightFormInput: { flex: 1, color: '$primaryBlack', paddingLeft: 12 },
weightFormInputWrapper: { marginTop: 8 },
usernameInput: { flex: 1, color: '$primaryBlack', marginLeft: 16 },
usernameFormInputWrapper: { marginTop: 8, marginRight: 12 },
footerWrapper: { paddingTop: 16 },
saveButton: {
width: 140,
height: 44,
alignSelf: 'flex-end',
justifyContent: 'center',
},
doneButton: { borderRadius: 16, backgroundColor: '$primaryBlue' },
thumbSelectContainer: {
marginTop: 12,
},
thumbStyle:{
width:72,
height:72,
marginVertical:8,
marginRight:8,
borderRadius:12,
backgroundColor:'$primaryLightGray'
},
checkContainer:{
position: 'absolute',
top: 12,
left: 6,
backgroundColor: '$pureWhite',
borderRadius: 12
},
settingLabel:{
color: '$primaryDarkGray',
fontSize: 14,
fontWeight: 'bold',
flexGrow: 1,
textAlign:'left',
},
listContainer:{
paddingBottom:getBottomSpace() + 16,
},
container:{
paddingVertical:16
},
bodyWrapper: { flex: 1, paddingTop: 20, paddingBottom:20},
inputWrapper: { flexDirection: 'row', alignItems: 'center'},
contentLabel: { color: '$iconColor', marginTop:4, textAlign:'left' },
weightInput: {width:80},
weightFormInput: { flex:1, color: '$primaryBlack', paddingLeft: 12 },
weightFormInputWrapper: { marginTop: 8 },
usernameInput: { flex:1, color: '$primaryBlack', marginLeft: 16 },
usernameFormInputWrapper: { marginTop: 8, marginRight: 12 },
footerWrapper: { paddingTop:16 },
saveButton: {
width: 140,
height: 44,
alignSelf: 'flex-end',
justifyContent: 'center',
},
doneButton:{borderRadius:16, backgroundColor:'$primaryBlue'},
thumbSelectContainer:{
marginTop:12,
}
});

View File

@ -3,115 +3,107 @@ import { useIntl } from 'react-intl';
import { ActivityIndicator, Alert, Text, TouchableOpacity, View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { FlatList } from 'react-native-gesture-handler';
import { extractImageUrls } from '../../../utils/editor';
import styles from './styles';
import ESStyleSheet from 'react-native-extended-stylesheet';
import { Icon } from '../../../components';
import EStyleSheet from 'react-native-extended-stylesheet';
import { View as AnimatedView } from 'react-native-animatable';
import { extractImageUrls } from '../../../utils/editor';
import styles from './styles';
import { Icon } from '../../../components';
interface ThumbSelectionContentProps {
body: string;
thumbUrl: string;
isUploading: boolean;
onThumbSelection: (url: string) => void;
body: string;
thumbUrl: string;
isUploading: boolean;
onThumbSelection: (url: string) => void;
}
const ThumbSelectionContent = ({ body, thumbUrl, onThumbSelection, isUploading }: ThumbSelectionContentProps) => {
const intl = useIntl();
const ThumbSelectionContent = ({
body,
thumbUrl,
onThumbSelection,
isUploading,
}: ThumbSelectionContentProps) => {
const intl = useIntl();
const [imageUrls, setImageUrls] = useState<string[]>([]);
const [needMore, setNeedMore] = useState(true);
const [thumbIndex, setThumbIndex] = useState(0);
const [imageUrls, setImageUrls] = useState<string[]>([]);
const [needMore, setNeedMore] = useState(true);
const [thumbIndex, setThumbIndex] = useState(0);
useEffect(() => {
const urls = extractImageUrls({ body });
useEffect(() => {
const urls = extractImageUrls({ body });
if (urls.length < 2) {
setNeedMore(true);
onThumbSelection(urls[0] || '');
setThumbIndex(0);
setImageUrls([])
} else {
setNeedMore(false);
setImageUrls(urls)
}
const _urlIndex = urls.indexOf(thumbUrl)
if (_urlIndex < 0) {
onThumbSelection(urls[0] || '');
setThumbIndex(0);
} else {
setThumbIndex(_urlIndex)
}
}, [body])
//VIEW_RENDERERS
const _renderImageItem = ({ item, index }: { item: string, index: number }) => {
const _onPress = () => {
onThumbSelection(item);
setThumbIndex(index);
}
const isSelected = item === thumbUrl && index === thumbIndex;
return (
<TouchableOpacity onPress={() => _onPress()} >
<FastImage
source={{ uri: item }}
style={styles.thumbStyle}
resizeMode='cover'
/>
{isSelected && (
<AnimatedView duration={300} animation='zoomIn' style={styles.checkContainer}>
<Icon
color={EStyleSheet.value('$primaryBlue')}
iconType="MaterialCommunityIcons"
name={'checkbox-marked-circle'}
size={20}
/>
</AnimatedView>
)}
</TouchableOpacity>
)
if (urls.length < 2) {
setNeedMore(true);
onThumbSelection(urls[0] || '');
setThumbIndex(0);
setImageUrls([]);
} else {
setNeedMore(false);
setImageUrls(urls);
}
const _renderHeader = () => (
isUploading &&
<View style={{ flex: 1, justifyContent: 'center', marginRight: 16 }}>
<ActivityIndicator color={ESStyleSheet.value('$primaryBlack')} />
</View>
const _urlIndex = urls.indexOf(thumbUrl);
if (_urlIndex < 0) {
onThumbSelection(urls[0] || '');
setThumbIndex(0);
} else {
setThumbIndex(_urlIndex);
}
}, [body]);
)
//VIEW_RENDERERS
const _renderImageItem = ({ item, index }: { item: string; index: number }) => {
const _onPress = () => {
onThumbSelection(item);
setThumbIndex(index);
};
const isSelected = item === thumbUrl && index === thumbIndex;
return (
<View style={styles.thumbSelectContainer}>
<Text style={styles.settingLabel}>{intl.formatMessage({ id: 'editor.select_thumb' })}</Text>
{
needMore ? (
<Text style={styles.contentLabel}>{intl.formatMessage({ id: 'editor.add_more_imgs' })}</Text>
) : (
<FlatList
data={imageUrls}
renderItem={_renderImageItem}
ListHeaderComponent={_renderHeader}
keyExtractor={(item, index) => `${item}-${index}`}
horizontal={true}
contentContainerStyle={styles.listContainer}
showsHorizontalScrollIndicator={false} />
)
}
</View>
<TouchableOpacity onPress={() => _onPress()}>
<FastImage source={{ uri: item }} style={styles.thumbStyle} resizeMode="cover" />
{isSelected && (
<AnimatedView duration={300} animation="zoomIn" style={styles.checkContainer}>
<Icon
color={EStyleSheet.value('$primaryBlue')}
iconType="MaterialCommunityIcons"
name="checkbox-marked-circle"
size={20}
/>
</AnimatedView>
)}
</TouchableOpacity>
);
};
const _renderHeader = () =>
isUploading && (
<View style={{ flex: 1, justifyContent: 'center', marginRight: 16 }}>
<ActivityIndicator color={ESStyleSheet.value('$primaryBlack')} />
</View>
);
return (
<View style={styles.thumbSelectContainer}>
<Text style={styles.settingLabel}>{intl.formatMessage({ id: 'editor.select_thumb' })}</Text>
{needMore ? (
<Text style={styles.contentLabel}>
{intl.formatMessage({ id: 'editor.add_more_imgs' })}
</Text>
) : (
<FlatList
data={imageUrls}
renderItem={_renderImageItem}
ListHeaderComponent={_renderHeader}
keyExtractor={(item, index) => `${item}-${index}`}
horizontal={true}
contentContainerStyle={styles.listContainer}
showsHorizontalScrollIndicator={false}
/>
)}
</View>
);
};
export default ThumbSelectionContent;

View File

@ -1,22 +1,20 @@
import React, { useImperativeHandle, useRef, useState } from 'react';
import React, { useImperativeHandle, useRef, useState, forwardRef } from 'react';
import { FlatList } from 'react-native-gesture-handler';
import ActionSheet from 'react-native-actions-sheet';
import EStyleSheet from 'react-native-extended-stylesheet';
import styles from './styles';
import { extractImageUrls } from '../../../utils/editor';
import FastImage from 'react-native-fast-image';
import { forwardRef } from 'react';
import { View, Text, Alert, TouchableOpacity } from 'react-native';
import { useIntl } from 'react-intl';
import { extractImageUrls } from '../../../utils/editor';
import styles from './styles';
export interface ThumbSelectionModalProps {
thumbUrl:string;
onThumbSelection:(index:number)=>void;
thumbUrl: string;
onThumbSelection: (index: number) => void;
}
const ThumbSelectionModal = ({ onThumbSelection, thumbUrl }:ThumbSelectionModalProps, ref) => {
const ThumbSelectionModal = ({ onThumbSelection, thumbUrl }: ThumbSelectionModalProps, ref) => {
const intl = useIntl();
const [imageUrls, setImageUrls] = useState<string[]>([]);
@ -24,82 +22,74 @@ const ThumbSelectionModal = ({ onThumbSelection, thumbUrl }:ThumbSelectionModalP
//CALLBACK_METHODS
useImperativeHandle(ref, () => ({
show: (postBody:string) => {
console.log("Showing action modal")
show: (postBody: string) => {
console.log('Showing action modal');
const urls = extractImageUrls({body:postBody});
const urls = extractImageUrls({ body: postBody });
if(urls.length < 2){
console.log("Skipping modal show as post images are less than 2");
Alert.alert(
intl.formatMessage({id:'editor.two_thumbs_required'})
)
onThumbSelection(0);
return;
}
setImageUrls(urls);
sheetModalRef.current?.setModalVisible(true);
if (urls.length < 2) {
console.log('Skipping modal show as post images are less than 2');
Alert.alert(intl.formatMessage({ id: 'editor.two_thumbs_required' }));
onThumbSelection(0);
return;
}
setImageUrls(urls);
sheetModalRef.current?.setModalVisible(true);
},
}));
const _onSelection = (index:number) => {
const _onSelection = (index: number) => {
onThumbSelection(index);
sheetModalRef.current?.setModalVisible(false);
}
};
//VIEW_RENDERERS
const _renderImageItem = ({item, index}:{item:string, index:number}) => {
const _onPress = () => {
_onSelection(index);
}
const _renderImageItem = ({ item, index }: { item: string; index: number }) => {
const _onPress = () => {
_onSelection(index);
};
const selectedStyle = item === thumbUrl ? styles.selectedStyle : null
const selectedStyle = item === thumbUrl ? styles.selectedStyle : null;
return (
<TouchableOpacity onPress={() => _onPress()} >
<FastImage
source={{uri:item}}
style={{...styles.thumbStyle, ...selectedStyle}}
resizeMode='cover'
/>
</TouchableOpacity>
)
}
<TouchableOpacity onPress={() => _onPress()}>
<FastImage
source={{ uri: item }}
style={{ ...styles.thumbStyle, ...selectedStyle }}
resizeMode="cover"
/>
</TouchableOpacity>
);
};
const _renderContent = () => {
return (
<View style={{alignItems:'center'}} >
<Text style={styles.title}>{intl.formatMessage({id:'editor.select_thumb'})}</Text>
<FlatList
data={imageUrls}
renderItem={_renderImageItem}
keyExtractor={(item, index)=>`${item}-${index}`}
horizontal={true}
contentContainerStyle={styles.listContainer}
showsHorizontalScrollIndicator={false}
/>
</View>
)
}
return (
<View style={{ alignItems: 'center' }}>
<Text style={styles.title}>{intl.formatMessage({ id: 'editor.select_thumb' })}</Text>
<FlatList
data={imageUrls}
renderItem={_renderImageItem}
keyExtractor={(item, index) => `${item}-${index}`}
horizontal={true}
contentContainerStyle={styles.listContainer}
showsHorizontalScrollIndicator={false}
/>
</View>
);
};
return (
<ActionSheet
ref={sheetModalRef}
gestureEnabled={false}
hideUnderlay
containerStyle={styles.sheetContent}
indicatorColor={EStyleSheet.value('$primaryWhiteLightBackground')}
>
{_renderContent()}
</ActionSheet>
<ActionSheet
ref={sheetModalRef}
gestureEnabled={false}
hideUnderlay
containerStyle={styles.sheetContent}
indicatorColor={EStyleSheet.value('$primaryWhiteLightBackground')}
>
{_renderContent()}
</ActionSheet>
);
};
export default forwardRef(ThumbSelectionModal);
export default forwardRef(ThumbSelectionModal);

View File

@ -172,12 +172,10 @@ class EditorContainer extends Component<any, any> {
this._fetchDraftsForComparison(isReply);
}
this._requestKeyboardFocus();
AppState.addEventListener('change', this._handleAppStateChange);
}
componentDidUpdate(prevProps: Readonly<any>, prevState: Readonly<any>): void {
if (
prevState.rewardType !== this.state.rewardType ||
@ -193,12 +191,12 @@ class EditorContainer extends Component<any, any> {
this._isMounted = false;
}
_handleAppStateChange = (nextAppState:AppStateStatus) => {
_handleAppStateChange = (nextAppState: AppStateStatus) => {
if (this._appState.match(/active|forground/) && nextAppState === 'inactive') {
this._saveCurrentDraft(this._updatedDraftFields);
}
this._appState = nextAppState;
}
};
_getStorageDraft = async (username, isReply, paramDraft) => {
const { drafts } = this.props;

View File

@ -143,23 +143,23 @@ class EditorScreen extends Component {
};
_handleOnSaveButtonPress = () => {
const {draftId, intl} = this.props;
if(draftId){
Alert.alert(
intl.formatMessage({id:'editor.draft_save_title'}),
"",
[{
text:intl.formatMessage({id:'editor.draft_update'}),
onPress:()=>this._saveDraftToDB(),
},{
text:intl.formatMessage({id:'editor.draft_save_new'}),
onPress:()=>this._saveDraftToDB(true)
},{
text:intl.formatMessage({id:'alert.cancel'}),
onPress:()=>{},
style:'cancel'
}]
)
const { draftId, intl } = this.props;
if (draftId) {
Alert.alert(intl.formatMessage({ id: 'editor.draft_save_title' }), '', [
{
text: intl.formatMessage({ id: 'editor.draft_update' }),
onPress: () => this._saveDraftToDB(),
},
{
text: intl.formatMessage({ id: 'editor.draft_save_new' }),
onPress: () => this._saveDraftToDB(true),
},
{
text: intl.formatMessage({ id: 'alert.cancel' }),
onPress: () => {},
style: 'cancel',
},
]);
return;
}
this._saveDraftToDB();
@ -174,7 +174,7 @@ class EditorScreen extends Component {
this.changeTimer = setTimeout(() => {
// saveCurrentDraft(fields);
updateDraftFields(fields)
updateDraftFields(fields);
}, 300);
};
@ -182,7 +182,7 @@ class EditorScreen extends Component {
const { handleOnSubmit, handleSchedulePress } = this.props;
const { fields, scheduledFor } = this.state;
if(scheduledFor && handleSchedulePress){
if (scheduledFor && handleSchedulePress) {
handleSchedulePress(scheduledFor, fields);
return;
}
@ -192,29 +192,28 @@ class EditorScreen extends Component {
}
};
_handleOnThumbSelection = (url:string) => {
_handleOnThumbSelection = (url: string) => {
const { setThumbUrl } = this.props;
if (setThumbUrl) {
setThumbUrl(url);
}
};
_handleScheduleChange = (datetime:string|null) => {
_handleScheduleChange = (datetime: string | null) => {
this.setState({
scheduledFor:datetime,
})
}
scheduledFor: datetime,
});
};
_handleRewardChange = (value) => {
const { handleRewardChange } = this.props;
handleRewardChange(value);
}
};
_handleSettingsPress = () => {
if(this.postOptionsModalRef){
if (this.postOptionsModalRef) {
this.postOptionsModalRef.show();
}
}
};
_handleIsFormValid = (bodyText) => {
const { fields } = this.state;
@ -255,7 +254,7 @@ class EditorScreen extends Component {
});
const jsonMeta = makeJsonMetadata(meta, fields.tags);
fields.meta = jsonMeta;
if (
get(fields, 'body', '').trim() !== get(_fields, 'body', '').trim() ||
get(fields, 'title', '').trim() !== get(_fields, 'title', '').trim() ||
@ -264,7 +263,7 @@ class EditorScreen extends Component {
) {
console.log('jsonMeta : ', jsonMeta);
handleFormChanged();
this._saveCurrentDraft(fields);
}
@ -338,7 +337,7 @@ class EditorScreen extends Component {
});
};
_saveDraftToDB(saveAsNew?:boolean) {
_saveDraftToDB(saveAsNew?: boolean) {
const { saveDraftToDB } = this.props;
const { fields } = this.state;
@ -388,10 +387,15 @@ class EditorScreen extends Component {
} = this.props;
const rightButtonText = intl.formatMessage({
id: isEdit ? 'basic_header.update' : isReply ? 'basic_header.reply' : scheduledFor ? 'basic_header.schedule' : 'basic_header.publish',
id: isEdit
? 'basic_header.update'
: isReply
? 'basic_header.reply'
: scheduledFor
? 'basic_header.schedule'
: 'basic_header.publish',
});
const _renderCommunityModal = () => {
return (
<Modal

View File

@ -45,8 +45,7 @@ export const generatePermlink = (title, random = false) => {
return perm;
};
;export const extractWordAtIndex = (text:string, index:number) => {
export const extractWordAtIndex = (text: string, index: number) => {
const RANGE = 50;
const _start = index - RANGE;
@ -55,32 +54,30 @@ export const generatePermlink = (title, random = false) => {
const _length = text.length;
const textChunk = text.substring(_start > 0 ? _start : 0, _end < _length ? _end : _length);
const indexChunk = index < 50 ? index : (
_length - index < 50 ? textChunk.length - (_length - index) :
RANGE
);
const indexChunk =
index < 50 ? index : _length - index < 50 ? textChunk.length - (_length - index) : RANGE;
console.log('char at index: ', textChunk[indexChunk]);
const END_REGEX = /[\s,]/
const END_REGEX = /[\s,]/;
let word = '';
for(let i = indexChunk; i >= 0 && (!END_REGEX.test(textChunk[i]) || i === indexChunk); i--){
if(textChunk[i]){
for (let i = indexChunk; i >= 0 && (!END_REGEX.test(textChunk[i]) || i === indexChunk); i--) {
if (textChunk[i]) {
word += textChunk[i];
}
}
word = word.split('').reverse().join('');
if(!END_REGEX.test(textChunk[indexChunk])){
for(let i = indexChunk + 1; i < textChunk.length && !END_REGEX.test(textChunk[i]); i++){
if(textChunk[i]){
if (!END_REGEX.test(textChunk[indexChunk])) {
for (let i = indexChunk + 1; i < textChunk.length && !END_REGEX.test(textChunk[i]); i++) {
if (textChunk[i]) {
word += textChunk[i];
}
}
}
return word;
}
};
export const generateReplyPermlink = (toAuthor) => {
if (!toAuthor) {
@ -168,52 +165,55 @@ export const makeJsonMetadataForUpdate = (oldJson, meta, tags) => {
return Object.assign({}, oldJson, mergedMeta, { tags });
};
const extractUrls = (body:string) => {
const extractUrls = (body: string) => {
const urlReg = /(\b(https?|ftp):\/\/[A-Z0-9+&@#/%?=~_|!:,.;-]*[-A-Z0-9+&@#/%=~_|])/gim;
const mUrls = body && body.match(urlReg);
return mUrls || [];
}
};
export const extractImageUrls = ({body, urls}:{body?:string, urls?:string[]}) => {
export const extractImageUrls = ({ body, urls }: { body?: string; urls?: string[] }) => {
const imgReg = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif|heic|webp))/gim;
let imgUrls = [];
const mUrls = urls || extractUrls(body);
mUrls.forEach((url)=>{
mUrls.forEach((url) => {
const isImage = url.match(imgReg);
if (isImage) {
imgUrls.push(url);
}
})
});
return imgUrls;
}
};
export const extractFilenameFromPath = ({path, mimeType}:{path:string, mimeType?:string}) => {
try{
if(!path){
throw new Error("path not provided");
export const extractFilenameFromPath = ({
path,
mimeType,
}: {
path: string;
mimeType?: string;
}) => {
try {
if (!path) {
throw new Error('path not provided');
}
const filenameIndex = path.lastIndexOf('/') + 1;
const extensionIndex = path.lastIndexOf('.');
if(filenameIndex < 0 || extensionIndex <= filenameIndex){
throw new Error("file name not present with extension");
if (filenameIndex < 0 || extensionIndex <= filenameIndex) {
throw new Error('file name not present with extension');
}
return path.substring(path.lastIndexOf('/') + 1);
}catch(err){
} catch (err) {
let _ext = 'jpg';
if(mimeType){
_ext = MimeTypes.extension(mimeType)
if (mimeType) {
_ext = MimeTypes.extension(mimeType);
}
return `${generateRndStr()}.${_ext}`;
}
}
};
export const extractMetadata = (body:string, thumbUrl?:string) => {
export const extractMetadata = (body: string, thumbUrl?: string) => {
const userReg = /(^|\s)(@[a-z][-.a-z\d]+[a-z\d])/gim;
const out = {};
@ -221,16 +221,16 @@ export const extractMetadata = (body:string, thumbUrl?:string) => {
const mUrls = extractUrls(body);
const mUsers = body && body.match(userReg);
const matchedImages = extractImageUrls({urls:mUrls});
const matchedImages = extractImageUrls({ urls: mUrls });
const matchedLinks = [];
const matchedUsers = [];
if (mUrls) {
mUrls.forEach((url)=>{
if(matchedImages.indexOf(url) < 0){
mUrls.forEach((url) => {
if (matchedImages.indexOf(url) < 0) {
matchedLinks.push(url);
}
})
});
}
if (matchedLinks.length) {
@ -238,10 +238,10 @@ export const extractMetadata = (body:string, thumbUrl?:string) => {
}
if (matchedImages.length) {
if(thumbUrl){
matchedImages.sort((item)=>item === thumbUrl ? -1 : 1);
if (thumbUrl) {
matchedImages.sort((item) => (item === thumbUrl ? -1 : 1));
}
out.image = matchedImages;
}
@ -270,4 +270,4 @@ export const createPatch = (text1, text2) => {
return patch;
};
export const delay = ms => new Promise(res => setTimeout(res, ms));
export const delay = (ms) => new Promise((res) => setTimeout(res, ms));

View File

@ -8926,11 +8926,6 @@ react-native-image-pan-zoom@^2.1.9:
resolved "https://registry.yarnpkg.com/react-native-image-pan-zoom/-/react-native-image-pan-zoom-2.1.12.tgz#eb98bf56fb5610379bdbfdb63219cc1baca98fd2"
integrity sha512-BF66XeP6dzuANsPmmFsJshM2Jyh/Mo1t8FsGc1L9Q9/sVP8MJULDabB1hms+eAoqgtyhMr5BuXV3E1hJ5U5H6Q==
react-native-image-size@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/react-native-image-size/-/react-native-image-size-1.1.3.tgz#7d69c2cd4e1d1632947867e47643ed8cabb9de27"
integrity sha512-jJvN6CjXVAm69LAVZNV7m7r50Qk9vuPZwLyrbs/k31/3Xs8bZyVCdvfP44FuBisITn/yFsiOo6i8NPrFBPH20w==
react-native-image-zoom-viewer@^2.2.27:
version "2.2.27"
resolved "https://registry.yarnpkg.com/react-native-image-zoom-viewer/-/react-native-image-zoom-viewer-2.2.27.tgz#fb3314c5dc86ac33da48cb31bf4920d97eecb6eb"