diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js index 00c696db..867e3638 100644 --- a/app/mobile/src/context/useCardContext.hook.js +++ b/app/mobile/src/context/useCardContext.hook.js @@ -29,6 +29,7 @@ import { removeContactChannelTopic } from 'api/removeContactChannelTopic'; export function useCardContext() { const [state, setState] = useState({ cards: new Map(), + requestRevision: null, }); const store = useContext(StoreContext); const upload = useContext(UploadContext); @@ -356,6 +357,8 @@ export function useCardContext() { setSession: async (access) => { const { guid, server, appToken } = access; cards.current = new Map(); + const status = await store.actions.getCardRequestStatus(guid); + updateState({ requestRevision: status.revision }); const cardItems = await store.actions.getCardItems(guid); for (item of cardItems) { cards.current.set(item.cardId, { ...item, channels: new Map() }); @@ -370,6 +373,11 @@ export function useCardContext() { curRevision.current = revision; session.current = access; }, + setRequestRevision: async (revision) => { + const { guid } = session.current + await store.actions.setCardRequestStatus(guid, { revision }); + updateState({ requestRevision: revision }); + }, clearSession: () => { session.current = {}; updateState({ account: null }); diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index 55ee5f07..2b9b7aa3 100644 --- a/app/mobile/src/context/useStoreContext.hook.js +++ b/app/mobile/src/context/useStoreContext.hook.js @@ -44,6 +44,14 @@ export function useStoreContext() { const dataId = `${guid}_profile`; await db.current.executeSql("INSERT OR REPLACE INTO app (key, value) values (?, ?);", [dataId, encodeObject(profile)]); }, + getCardRequestStatus: async (guid) => { + const dataId = `${guid}_card_status`; + return await getAppValue(db.current, dataId, {}); + }, + setCardRequestStatus: async (guid, status) => { + const dataId = `${guid}_card_status`; + await db.current.executeSql("INSERT OR REPLACE INTO app (key, value) values (?, ?);", [dataId, encodeObject(status)]); + }, getProfileRevision: async (guid) => { const dataId = `${guid}_profileRevision`; return await getAppValue(db.current, dataId, null); diff --git a/app/mobile/src/session/Session.jsx b/app/mobile/src/session/Session.jsx index 215a0ef3..4910c4e5 100644 --- a/app/mobile/src/session/Session.jsx +++ b/app/mobile/src/session/Session.jsx @@ -23,6 +23,7 @@ import { useChannels } from './channels/useChannels.hook'; import { CommonActions } from '@react-navigation/native'; import { ConversationContext } from 'context/ConversationContext'; import { ProfileIcon } from './profileIcon/ProfileIcon'; +import { CardsIcon } from './cardsIcon/CardsIcon'; const ConversationStack = createStackNavigator(); const ProfileStack = createStackNavigator(); @@ -201,8 +202,8 @@ export function Session() { Profile - - Contacts + + Contacts @@ -337,6 +338,8 @@ export function Session() { ); } + const [cardsActive, setCardsActive] = useState(false); + return ( { state.tabbed === false && ( @@ -349,6 +352,7 @@ export function Session() { )} { state.tabbed === true && ( setCardsActive(e?.data?.state?.index === 2) }} screenOptions={({ route }) => ({ tabBarStyle: styles.tabBar, headerShown: false, @@ -360,7 +364,7 @@ export function Session() { return ; } if (route.name === 'Contacts') { - return ; + return ; } }, tabBarShowLabel: false, diff --git a/app/mobile/src/session/cardsIcon/CardsIcon.jsx b/app/mobile/src/session/cardsIcon/CardsIcon.jsx new file mode 100644 index 00000000..30264b33 --- /dev/null +++ b/app/mobile/src/session/cardsIcon/CardsIcon.jsx @@ -0,0 +1,19 @@ +import { View } from 'react-native'; +import { useCardsIcon } from './useCardsIcon.hook'; +import { styles } from './CardsIcon.styled'; +import Ionicons from '@expo/vector-icons/AntDesign'; + +export function CardsIcon({ size, color, active }) { + + const { state, actions } = useCardsIcon(active); + + return ( + + + { state.curRevision !== state.setRevision && ( + + )} + + ); +} + diff --git a/app/mobile/src/session/cardsIcon/CardsIcon.styled.js b/app/mobile/src/session/cardsIcon/CardsIcon.styled.js new file mode 100644 index 00000000..7d9030d6 --- /dev/null +++ b/app/mobile/src/session/cardsIcon/CardsIcon.styled.js @@ -0,0 +1,14 @@ +import { StyleSheet } from 'react-native'; +import { Colors } from 'constants/Colors'; + +export const styles = StyleSheet.create({ + requested: { + width: 8, + height: 8, + borderRadius: 4, + backgroundColor: Colors.pending, + position: 'absolute', + right: 0, + bottom: 0, + }, +}); diff --git a/app/mobile/src/session/cardsIcon/useCardsIcon.hook.js b/app/mobile/src/session/cardsIcon/useCardsIcon.hook.js new file mode 100644 index 00000000..054aa28d --- /dev/null +++ b/app/mobile/src/session/cardsIcon/useCardsIcon.hook.js @@ -0,0 +1,43 @@ +import { useState, useEffect, useContext } from 'react'; +import { CardContext } from 'context/CardContext'; + +export function useCardsIcon(active) { + + const [state, setState] = useState({ + curRevision: null, + setRevision: null, + }); + + const card = useContext(CardContext); + + const updateState = (value) => { + setState((s) => ({ ...s, ...value })); + } + + useEffect(() => { + if (active && state.curRevision) { + card.actions.setRequestRevision(state.curRevision); + } + }, [active]); + + useEffect(() => { + let revision; + card.state.cards.forEach((contact) => { + if (contact?.detail?.status === 'pending' || contact?.detail?.status === 'requested') { + if (!revision || contact.detailRevision > revision) { + revision = contact.detailRevision; + } + } + }); + if (active && revision !== state.setRevision) { + card.actions.setRequestRevision(state.curRevision); + } + updateState({ setRevision: card.state.requestRevision, curRevision: revision }); + + }, [card]); + + const actions = {}; + + return { state, actions }; +} +