Skip to content

Commit

Permalink
102 show user related activities in profile (#713)
Browse files Browse the repository at this point in the history
* Add user Activity on profile view

* Add load more button and functionality

* Remove redundant

* Add mutual activities based on component state

* Add user related activity functionality

* Remove redundant

* Write code cleaner

* Correct typo

* Corrected loading state on Activities, corrected loading more button behaviour

* Correct PAGE_SIZE parameter

* Add mutual activities on Organisation accounts
  • Loading branch information
mikozet authored Aug 22, 2023
1 parent b5a361c commit a667f85
Show file tree
Hide file tree
Showing 11 changed files with 2,797 additions and 1,101 deletions.
3,540 changes: 2,503 additions & 1,037 deletions package-lock.json

Large diffs are not rendered by default.

53 changes: 38 additions & 15 deletions src/components/ActivityStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,20 @@ const ActivityStream = ({

return (
<Fragment>
<ActivityStreamList
activities={activities}
filterType={filterType}
lastSeenAt={lastSeenAt}
lastUpdatedAt={lastUpdatedAt}
/>
{!isLoading && (
<ActivityStreamList
activities={activities}
filterType={filterType}
lastSeenAt={lastSeenAt}
lastUpdatedAt={lastUpdatedAt}
/>
)}
{isLoading && (
<Box mx="auto" my={2} textAlign="center">
<CircularProgress />
</Box>
)}
{isMoreAvailable && onLoadMore && (
{!isLoading && isMoreAvailable && onLoadMore && (
<Box my={2}>
<Button disabled={isLoading} fullWidth isOutline onClick={onLoadMore}>
{translate('ActivityStream.buttonLoadMore')}
Expand Down Expand Up @@ -171,7 +173,19 @@ const ActivityStreamList = ({
return (
<Grid container spacing={2}>
{activities.reduce(
(acc, { data, hash, createdAt, type, isPending, txHash }) => {
(
acc,
{
data,
hash,
createdAt,
type,
isPending,
txHash,
transactionHash,
timestamp,
},
) => {
// Always filter gas transfers
if (
type === ActivityTypes.TRANSFER &&
Expand All @@ -194,18 +208,27 @@ const ActivityStreamList = ({
}

const isSeen =
DateTime.fromISO(lastSeenAt) > DateTime.fromISO(createdAt);
lastSeenAt && createdAt
? DateTime.fromISO(lastSeenAt) > DateTime.fromISO(createdAt)
: true;

const key = hash || transactionHash;

const createdAtDate =
createdAt || DateTime.fromSeconds(timestamp).toISO();

const txHashUpdated = txHash || transactionHash;

const item = (
<Grid item key={hash} xs={12}>
<Grid item key={key} xs={12}>
<ActivityStreamItem
createdAt={createdAt}
createdAt={createdAtDate}
data={data}
isPending={isPending}
isSeen={isSeen}
prefix={info.prefix}
safeAddress={safeAddress}
txHash={txHash}
txHash={txHashUpdated}
type={type}
walletAddress={walletAddress}
/>
Expand Down Expand Up @@ -438,11 +461,11 @@ ActivityStreamList.propTypes = {
};

ActivityStreamItem.propTypes = {
createdAt: PropTypes.string.isRequired,
createdAt: PropTypes.string,
data: PropTypes.object.isRequired,
isPending: PropTypes.bool.isRequired,
isPending: PropTypes.bool,
isSeen: PropTypes.bool.isRequired,
prefix: PropTypes.string,
prefix: PropTypes.string.isRequired,
safeAddress: PropTypes.string.isRequired,
txHash: PropTypes.string.isRequired,
type: PropTypes.symbol.isRequired,
Expand Down
1 change: 1 addition & 0 deletions src/components/ButtonAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const useStyles = makeStyles((theme) => ({
bottom: theme.spacing(2.25),
right: theme.spacing(2.25),
background: 'transparent',
zIndex: theme.zIndex.layer2,

'&:hover': {
'& stop:first-of-type': {
Expand Down
77 changes: 77 additions & 0 deletions src/components/ProfileContentActivity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Box } from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
import { DateTime } from 'luxon';
import PropTypes from 'prop-types';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import ActivityStream from '~/components/ActivityStream';
import { loadMoreActivitiesMutual } from '~/store/activity/actions';
import ActionTypes from '~/store/activity/types';

const useStyles = makeStyles(() => {
return {
activityContainer: {
paddingBottom: '110px',
},
};
});

const ProfileContentActivity = ({ address }) => {
const classes = useStyles();
const dispatch = useDispatch();
const mutualActivities = useSelector(
(state) => state.activity.mutualActivities.activities,
);
const isMoreAvailable = useSelector(
(state) => state.activity.mutualActivities.isMoreAvailable,
);
const isLoadingMore = useSelector(
(state) => state.activity.mutualActivities.isLoadingMore,
);
const mutualAddress = useSelector(
(state) => state.activity.mutualActivities.mutualAddress,
);

useEffect(() => {
if (mutualAddress !== address) {
dispatch({
type: ActionTypes.ACTIVITIES_MUTUAL_RESET,
});

Promise.all([
dispatch({
type: ActionTypes.ACTIVITIES_MUTUAL_ADDRESS_UPDATE,
meta: {
mutualAddress: address,
},
}),
dispatch(loadMoreActivitiesMutual(address, { fromOffsetZero: true })),
]);
}
}, [address, mutualAddress]); //eslint-disable-line react-hooks/exhaustive-deps

const currentTime = DateTime.now().toISO();

const handleLoadMore = () => {
dispatch(loadMoreActivitiesMutual(address));
};

return (
<Box className={classes.activityContainer}>
<ActivityStream
activities={mutualActivities}
isLoading={isLoadingMore}
isMoreAvailable={isMoreAvailable}
lastSeenAt={currentTime}
onLoadMore={handleLoadMore}
/>
</Box>
);
};

ProfileContentActivity.propTypes = {
address: PropTypes.string,
};

export default ProfileContentActivity;
8 changes: 8 additions & 0 deletions src/services/activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ export function formatMessage({
} else if (data.to === process.env.SAFE_FUNDER_ADDRESS) {
// I've paid Gas fees for a transaction
messageId = 'PaidGasCosts';
} else if (data.to === safeAddress) {
// I've received Circles from someone
messageId = 'ReceivedCircles';
addressActor = data.from;
} else {
// I've sent Circles to someone
messageId = 'SentCircles';
addressActor = data.to;
}
} else if (type === ActivityTypes.HUB_TRANSFER) {
addressOrigin = data.from;
Expand Down
10 changes: 9 additions & 1 deletion src/services/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,21 @@ const activity = {
ActivityTypes: core.activity.ActivityTypes,
ActivityFilterTypes: core.activity.ActivityFilterTypes,

getLatest: async (safeAddress, filter, limit, timestamp = 0, offset = 0) => {
getLatest: async (
safeAddress,
filter,
limit,
timestamp = 0,
offset = 0,
otherSafeAddress,
) => {
return await requestCore('activity', 'getLatest', {
filter,
limit,
offset,
safeAddress,
timestamp,
otherSafeAddress,
});
},
};
Expand Down
78 changes: 76 additions & 2 deletions src/store/activity/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import web3 from '~/services/web3';
import { CATEGORIES } from '~/store/activity/reducers';
import ActionTypes from '~/store/activity/types';
import notify, { NotificationsTypes } from '~/store/notifications/actions';
import logError, { translateErrorForUser } from '~/utils/debug';
import { formatCirclesValue } from '~/utils/format';

const { ActivityTypes } = core.activity;
const { ActivityTypes, ActivityFilterTypes } = core.activity;

const PAGE_SIZE = 10;
export const PAGE_SIZE = 10;

export function initializeActivities() {
const lastSeenAt = getLastSeen();
Expand Down Expand Up @@ -102,6 +103,7 @@ export function checkFinishedActivities({
} = {}) {
return async (dispatch, getState) => {
const { safe, activity } = getState();
const { mutualActivities } = activity;

if (!safe.currentAccount) {
return;
Expand Down Expand Up @@ -192,6 +194,19 @@ export function checkFinishedActivities({
}
}
setLastReceivedTransaction(DateTime.now().toISO());

if (
`/profile/${mutualActivities.mutualAddress}` ===
window.location.pathname
) {
dispatch(
loadMoreActivitiesMutual(mutualActivities.mutualAddress, {
fromOffsetZero: true,
withLoader: false,
liveRefresh: true,
}),
);
}
}

dispatch({
Expand Down Expand Up @@ -267,6 +282,65 @@ export function loadMoreActivities(category) {
};
}

export function loadMoreActivitiesMutual(otherSafeAddress, options = {}) {
const {
fromOffsetZero = false,
withLoader = true,
liveRefresh = false,
} = options;
return async (dispatch, getState) => {
const { safe, activity } = getState();
const currentOffset = activity.mutualActivities.offset;

if (!safe.currentAccount) {
return;
}

if (withLoader) {
dispatch({
type: ActionTypes.ACTIVITIES_MUTUAL_LOAD_MORE,
});
}

const offset = fromOffsetZero ? 0 : activity.mutualActivities.offset;

try {
const { activities, lastTimestamp } = await core.activity.getLatest(
safe.currentAccount,
ActivityFilterTypes.DISABLED,
PAGE_SIZE,
activity.lastTimestamp,
offset,
otherSafeAddress,
);

dispatch({
type: ActionTypes.ACTIVITIES_MUTUAL_LOAD_MORE_SUCCESS,
meta: {
activities,
offset: liveRefresh ? currentOffset : currentOffset + PAGE_SIZE,
lastTimestamp,
},
});
} catch (error) {
logError(error);
dispatch(
notify({
text: (
<Typography classes={{ root: 'body4_white' }} variant="body4">
{translateErrorForUser(error)}
</Typography>
),
type: NotificationsTypes.ERROR,
}),
);
dispatch({
type: ActionTypes.ACTIVITIES_MUTUAL_LOAD_MORE_ERROR,
});
}
};
}

export function resetActivities({ isClearingStorage = true } = {}) {
if (isClearingStorage) {
removeLastSeen();
Expand Down
Loading

0 comments on commit a667f85

Please sign in to comment.