Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accordion component for activity log #693

Merged
merged 6 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 64 additions & 56 deletions components/Card/ActivityLogCard.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,54 @@
import React, { useEffect } from "react";
import { Pressable, Text, View } from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import React, { useState, useEffect } from "react";
import { Text, View } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { useIsFocused } from "@react-navigation/native";

import ExpandableCard from "components/Card/ExpandableCard";
import PrefetchCacheRecord from "components/PrefetchCacheRecord";
import Card from "components/Card/Card";

import useI18N from "hooks/use-i18n";
import useActivityLog from "hooks/use-activity-log";
import useBottomSheet from "hooks/use-bottom-sheet";

import useStyles from "hooks/use-styles";
import useType from "hooks/use-type";

import { ScreenConstants } from "constants";

import { localStyles } from "./ActivityLogCard.styles";
import RenderActivityLog from "components/RenderActivityLog";

const ActivityLogCard = ({ preview, refreshing }) => {
// NOTE: invoking this hook causes the desired re-render onBack()
useIsFocused();

const [accordionState, setAccordionState] = useState([]);
const navigation = useNavigation();
const { styles, globalStyles } = useStyles(localStyles);
const { getTabScreenFromType } = useType();
const { collapse } = useBottomSheet();
const { i18n, numberFormat } = useI18N();

useEffect(() => {
if (groupedActivityLog) {
setAccordionState(
new Array(Object.keys(groupedActivityLog).length).fill(false)
);
}
}, [groupedActivityLog]);

const handleAccordionChange = (groupIndex) => {
const updatedAccordionState = accordionState.map((item, index) =>
index === groupIndex ? !item : item
);
setAccordionState(updatedAccordionState);
};

// TODO: FilterList
const renderActivityLog = (log, idx) => {
const _key = `${log.object_id}-${idx}`;
const renderActivityLog = (logs, idx) => {
return (
<View
key={_key}
style={[globalStyles.columnContainer, styles.activityView]}
>
<Pressable
onPress={() => {
const type = log?.post_type;
const tabScreen = getTabScreenFromType(type);
navigation.jumpTo(tabScreen, {
screen: ScreenConstants.DETAILS,
id: log?.object_id,
name: log?.object_name,
type,
});
collapse();
}}
>
<Text style={styles.activityLink}>{log?.object_name}</Text>
</Pressable>
<Text style={styles.activityText}>{log?.object_note}</Text>
{
// Prefetch any posts in the ActivityLog so that the records
// are available if the user goes OFFLINE.
}
{log?.object_id && log?.post_type && (
<PrefetchCacheRecord id={log.object_id} type={log.post_type} />
)}
</View>
<RenderActivityLog
key={logs?.[0] ?? Math.random()}
logs={logs}
index={idx}
accordionState={accordionState}
handleAccordionChange={handleAccordionChange}
/>
);
};

Expand All @@ -70,41 +60,59 @@ const ActivityLogCard = ({ preview, refreshing }) => {
mutate,
} = useActivityLog();

let groupedActivityLog = {};

// Group by name
activityLog?.forEach((element) => {
let makeKey = element.object_name;
if (!groupedActivityLog[makeKey]) {
groupedActivityLog[makeKey] = [];
}

groupedActivityLog[makeKey].push({
...element,
});
});

// force data refresh on reload
useEffect(() => {
if (refreshing && mutate) mutate();
}, [refreshing]);

const renderExpandedCard = () => (
<ScrollView
style={{
padding: 10,
}}
>
{activityLog?.map((log, idx) => renderActivityLog(log, idx))}
</ScrollView>
);
const renderExpandedCard = () => {
navigation.navigate(ScreenConstants.ALL_ACTIVITY_LOGS, {
paramsData: {
title: `${title} (${numberFormat(activityLog?.length)})`,
data: groupedActivityLog,
},
});
};

const renderPartialCard = () => (
<>
<View>{activityLog?.slice(0, preview ?? 3)?.map(renderActivityLog)}</View>
{activityLog?.length > 1 && (
<View>
{Object.entries(groupedActivityLog)
.slice(0, preview)
.map(renderActivityLog)}
</View>
{Object.keys(groupedActivityLog).length > 1 && (
<View style={styles.etcetera}>
<Text>...</Text>
</View>
)}
</>
);

if (!activityLog || activityLog?.length === 0) return null;

const title = i18n.t("global.activityLog");
if (!activityLog) return null;

return (
<ExpandableCard
<Card
border
title={title}
count={numberFormat(activityLog?.length)}
renderPartialCard={renderPartialCard}
renderExpandedCard={renderExpandedCard}
title={`${title} (${numberFormat(activityLog?.length)})`}
body={renderPartialCard()}
onPress={() => renderExpandedCard()}
/>
);
};
Expand Down
3 changes: 2 additions & 1 deletion components/Card/ActivityLogCard.styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ThemeConstants } from "constants";
export const localStyles = ({ theme, isRTL, isIOS }) => ({
activityView: {
marginVertical: 5,
alignItems: "flex-start",
// alignItems: "flex-start",
paddingHorizontal: 10,
},
// TODO: move to use-styles?
Expand All @@ -19,6 +19,7 @@ export const localStyles = ({ theme, isRTL, isIOS }) => ({
activityText: {
color: "grey",
fontStyle: "italic",
paddingVertical: 2,
},
etcetera: {
alignItems: "center",
Expand Down
3 changes: 3 additions & 0 deletions components/Icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ export const CheckIcon = ({ onPress, style }) => (
export const ChevronDownIcon = ({ onPress, style }) => (
<MaterialCommunityIcon name="chevron-down" onPress={onPress} style={style} />
);
export const ChevronUpIcon = ({ onPress, style }) => (
<MaterialCommunityIcon name="chevron-up" onPress={onPress} style={style} />
);
export const ChevronBackIcon = ({ onPress, style }) => (
<MaterialCommunityIcon name="chevron-left" onPress={onPress} style={style} />
);
Expand Down
97 changes: 97 additions & 0 deletions components/RenderActivityLog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//import liraries
import React from "react";
import { View, Text, Pressable } from "react-native";
import { useNavigation } from "@react-navigation/native";

import { ScreenConstants } from "constants";

import PrefetchCacheRecord from "components/PrefetchCacheRecord";
import { ChevronDownIcon, ChevronUpIcon } from "components/Icon";
import { localStyles } from "./RenderActivityLog.styles";
import useStyles from "hooks/use-styles";
import useType from "hooks/use-type";

// create a component
const RenderActivityLog = ({
logs,
index,
accordionState,
handleAccordionChange,
}) => {
const { styles, globalStyles } = useStyles(localStyles);
const { getTabScreenFromType } = useType();
const navigation = useNavigation();

if (!logs || logs.length === 0 || accordionState.length === 0) return null;

return (
<>
<View
style={{
...globalStyles.rowContainer,
justifyContent: "space-between",
marginVertical: 5,
}}
>
<Pressable
onPress={() => {
if (logs?.[1][0]) {
const type = logs[1][0]?.post_type;
const tabScreen = getTabScreenFromType(type);
navigation.jumpTo(tabScreen, {
screen: ScreenConstants.DETAILS,
id: logs[1][0]?.object_id,
name: logs[1][0]?.object_name,
type,
});
}
}}
>
<Text style={styles.activityLink}>{logs?.[0] ?? " "}</Text>
</Pressable>
<Pressable
onPress={() => {
handleAccordionChange(index);
}}
>
{accordionState[index] ? <ChevronUpIcon /> : <ChevronDownIcon />}
</Pressable>
</View>

{accordionState[index] ? (
logs?.[1]?.map((log, index) => (
<>
{/* Show all the entries. */}
<Text key={`${log.object_id}-${index}`} style={styles.activityText}>
{log?.object_note}
</Text>
{
// Prefetch any posts in the ActivityLog so that the records
// are available if the user goes OFFLINE.
}
{log?.object_id && log?.post_type && (
<PrefetchCacheRecord id={log.object_id} type={log.post_type} />
)}
</>
))
) : (
<>
{/* Show only one entry. */}
<Text style={styles.activityText}>{logs?.[1][0]?.object_note}</Text>
{
// Prefetch any posts in the ActivityLog so that the records
// are available if the user goes OFFLINE.
}
{logs?.[1][0]?.object_id && logs?.[1][0]?.post_type && (
<PrefetchCacheRecord
id={logs[1][0].object_id}
type={logs[1][0].post_type}
/>
)}
</>
)}
</>
);
};

export default RenderActivityLog;
24 changes: 24 additions & 0 deletions components/RenderActivityLog.styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ThemeConstants } from "constants";

export const localStyles = ({ theme, isRTL, isIOS }) => ({
activityView: {
marginVertical: 5,
// alignItems: "flex-start",
paddingHorizontal: 10,
},
// TODO: move to use-styles?
activityLink: {
color:
ThemeConstants.DARK === theme.mode
? theme.text.primary
: theme.brand.primary,
fontWeight: "bold",
textDecorationLine: "underline",
marginVertical: 2,
},
activityText: {
color: "grey",
fontStyle: "italic",
paddingVertical: 2,
},
});
1 change: 1 addition & 0 deletions constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ export const ScreenConstants = Object.freeze({
MY_USER: "MY_USER",
NOTIFICATIONS: "NOTIFICATIONS",
PIN: "PIN",
ALL_ACTIVITY_LOGS: "ALL_ACTIVITY_LOGS",
});

export const NotificationActionConstants = Object.freeze({
Expand Down
5 changes: 5 additions & 0 deletions navigation/TabNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
UserIcon,
} from "components/Icon";
import HomeScreen from "screens/HomeScreen";
import AllActivityLogsScreen from "screens/AllActivityLogsScreen";
import MoreScreen from "screens/MoreScreen";
import PINScreen from "screens/PINScreen";
import CreateScreen from "screens/Posts/CreateScreen";
Expand Down Expand Up @@ -146,6 +147,10 @@ const TabNavigator = ({ navigation }) => {
return (
<Stack.Navigator screenOptions={screenOptions}>
<Stack.Screen name={TabScreenConstants.HOME} component={HomeScreen} />
<Stack.Screen
name={ScreenConstants.ALL_ACTIVITY_LOGS}
component={AllActivityLogsScreen}
/>
<Stack.Screen
name={TabScreenConstants.CONTACTS}
component={PostStack}
Expand Down
Loading