From 43bf6d0d04c2eca33e7b776a80cf93b8ea821233 Mon Sep 17 00:00:00 2001 From: balzack Date: Tue, 25 Oct 2022 00:06:39 -0700 Subject: [PATCH] support resyncing of contacts --- .../ios/Databag.xcodeproj/project.pbxproj | 4 +- app/mobile/src/context/useCardContext.hook.js | 117 +++++++++++------- .../src/context/useStoreContext.hook.js | 2 +- .../src/session/cards/cardItem/CardItem.jsx | 5 +- .../session/cards/cardItem/CardItem.styled.js | 6 + app/mobile/src/session/cards/useCards.hook.js | 3 +- app/mobile/src/session/contact/Contact.jsx | 13 +- .../src/session/contact/Contact.styled.js | 10 ++ .../src/session/contact/useContact.hook.js | 14 ++- 9 files changed, 117 insertions(+), 57 deletions(-) diff --git a/app/mobile/ios/Databag.xcodeproj/project.pbxproj b/app/mobile/ios/Databag.xcodeproj/project.pbxproj index b7f1dd43..3eaf1fd6 100644 --- a/app/mobile/ios/Databag.xcodeproj/project.pbxproj +++ b/app/mobile/ios/Databag.xcodeproj/project.pbxproj @@ -310,7 +310,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = 3P65PQ7SUR; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -348,7 +348,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = 3P65PQ7SUR; INFOPLIST_FILE = Databag/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Databag; diff --git a/app/mobile/src/context/useCardContext.hook.js b/app/mobile/src/context/useCardContext.hook.js index 6ff98401..fe36a78e 100644 --- a/app/mobile/src/context/useCardContext.hook.js +++ b/app/mobile/src/context/useCardContext.hook.js @@ -1,6 +1,7 @@ import { useState, useRef, useContext } from 'react'; import { StoreContext } from 'context/StoreContext'; import { UploadContext } from 'context/UploadContext'; +import { getCard } from 'api/getCard'; import { getCards } from 'api/getCards'; import { getCardProfile } from 'api/getCardProfile'; import { setCardProfile } from 'api/setCardProfile'; @@ -42,15 +43,16 @@ export function useCardContext() { const syncing = useRef(false); const cards = useRef(new Map()); const cardChannels = useRef(new Map()); + const resync = useRef([]); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - const getCard = (cardId) => { + const getCardEntry = (cardId) => { const card = cards.current.get(cardId); if (!card) { - throw new Error('cared not found'); + throw new Error('card not found'); } return card; } @@ -213,12 +215,12 @@ export function useCardContext() { } const sync = async () => { - if (!syncing.current && setRevision.current !== curRevision.current) { + if (!syncing.current && (setRevision.current !== curRevision.current || resync.current.length > 0)) { syncing.current = true; + const { server, appToken, guid } = session.current; try { const revision = curRevision.current; - const { server, appToken, guid } = session.current; // get and store const delta = await getCards(server, appToken, setRevision.current); @@ -254,41 +256,7 @@ export function useCardContext() { } } - const status = await store.actions.getCardItemStatus(guid, card.id); - const cardServer = status.profile.node; - const cardToken = status.profile.guid + '.' + status.detail.token; - if (status.detail.status === 'connected') { - try { - const { notifiedView, notifiedProfile, notifiedArticle, notifiedChannel } = card.data; - if (status.notifiedView !== notifiedView) { - await store.actions.clearCardChannelItems(guid, card.id); - await updateCardChannelItems(card.id, cardServer, cardToken, notifiedView, null); - await store.actions.setCardItemNotifiedChannel(guid, card.id, notifiedChannel); - await store.actions.setCardItemNotifiedView(guid, card.id, notifiedView); - clearCardChannel(card.id); - } - else { - if (status.notifiedChannel != notifiedChannel) { - await updateCardChannelItems(card.id, cardServer, cardToken, status.notifiedView, status.notifiedChannel) - await store.actions.setCardItemNotifiedChannel(guid, card.id, notifiedChannel); - } - } - if (status.notifiedProfile != notifiedProfile) { - const message = await getContactProfile(cardServer, cardToken); - await setCardProfile(server, appToken, card.id, message); - await store.actions.setCardItemNotifiedProfile(guid, card.id, notifiedProfile); - } - if (status.offsync) { - await store.actions.clearCardItemOffsync(guid, card.id); - setCardOffsync(card.id, false); - } - } - catch(err) { - console.log("card1:", err); - await store.actions.setCardItemOffsync(guid, card.id); - setCardOffsync(card.id, true); - } - } + await syncCard(card); } else { //TODO clear card channel topics @@ -307,12 +275,65 @@ export function useCardContext() { return; } + if (resync.current.length > 0) { + const ids = resync.current; + resync.current = []; + + for(let i = 0; i < ids.length; i++) { + const item = cards.current.get(ids[i]); + if (item) { + const card = await getCard(server, appToken, ids[i]); + await syncCard(card); + } + } + } + updateState({ cards: cards.current }); syncing.current = false; sync(); } }; + const syncCard = async (card) => { + + const { server, appToken, guid } = session.current; + const status = await store.actions.getCardItemStatus(guid, card.id); + const cardServer = status.profile.node; + const cardToken = status.profile.guid + '.' + status.detail.token; + if (status.detail.status === 'connected') { + try { + const { notifiedView, notifiedProfile, notifiedArticle, notifiedChannel } = card.data; + if (status.notifiedView !== notifiedView) { + await store.actions.clearCardChannelItems(guid, card.id); + await updateCardChannelItems(card.id, cardServer, cardToken, notifiedView, null); + await store.actions.setCardItemNotifiedChannel(guid, card.id, notifiedChannel); + await store.actions.setCardItemNotifiedView(guid, card.id, notifiedView); + clearCardChannel(card.id); + } + else { + if (status.notifiedChannel != notifiedChannel) { + await updateCardChannelItems(card.id, cardServer, cardToken, status.notifiedView, status.notifiedChannel) + await store.actions.setCardItemNotifiedChannel(guid, card.id, notifiedChannel); + } + } + if (status.notifiedProfile != notifiedProfile) { + const message = await getContactProfile(cardServer, cardToken); + await setCardProfile(server, appToken, card.id, message); + await store.actions.setCardItemNotifiedProfile(guid, card.id, notifiedProfile); + } + if (status.offsync) { + await store.actions.clearCardItemOffsync(guid, card.id); + setCardOffsync(card.id, 0); + } + } + catch(err) { + console.log("card1:", err); + await store.actions.setCardItemOffsync(guid, card.id); + setCardOffsync(card.id, 1); + } + } + } + const updateCardChannelItems = async (cardId, cardServer, cardToken, notifiedView, notifiedChannel) => { const { guid } = session.current; const delta = await getContactChannels(cardServer, cardToken, notifiedView, notifiedChannel); @@ -501,19 +522,19 @@ export function useCardContext() { return await store.actions.clearCardChannelTopicItems(guid, cardId, channelId); }, getChannelTopic: async (cardId, channelId, topicId) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); return await getContactChannelTopic(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId); }, getChannelTopics: async (cardId, channelId, revision) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); return await getContactChannelTopics(profile.node, `${profile.guid}.${detail.token}`, channelId, revision); }, getChannelTopicAssetUrl: (cardId, channelId, topicId, assetId) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); return getContactChannelTopicAssetUrl(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId, assetId); }, addChannelTopic: async (cardId, channelId, message, files) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); const node = profile.node; const token = `${profile.guid}.${detail.token}`; if (files?.length > 0) { @@ -535,17 +556,21 @@ export function useCardContext() { } }, setChannelTopicSubject: async (cardId, channelId, topicId, data) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); return await setContactChannelTopicSubject(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId, data); }, removeChannel: async (cardId, channelId) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); return await removeContactChannel(profile.node, `${profile.guid}.${detail.token}`, channelId); }, removeChannelTopic: async (cardId, channelId, topicId) => { - const { detail, profile } = getCard(cardId); + const { detail, profile } = getCardEntry(cardId); return await removeContactChannelTopic(profile.node, `${profile.guid}.${detail.token}`, channelId, topicId); }, + resync: (cardId) => { + resync.current.push(cardId); + sync(); + }, } return { state, actions } diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index 4af3da1e..0d5235cb 100644 --- a/app/mobile/src/context/useStoreContext.hook.js +++ b/app/mobile/src/context/useStoreContext.hook.js @@ -1,7 +1,7 @@ import { useEffect, useState, useRef, useContext } from 'react'; import SQLite from "react-native-sqlite-storage"; -const DATABAG_DB = 'databag_v046.db'; +const DATABAG_DB = 'databag_v047.db'; export function useStoreContext() { const [state, setState] = useState({}); diff --git a/app/mobile/src/session/cards/cardItem/CardItem.jsx b/app/mobile/src/session/cards/cardItem/CardItem.jsx index 04efed97..fefa9315 100644 --- a/app/mobile/src/session/cards/cardItem/CardItem.jsx +++ b/app/mobile/src/session/cards/cardItem/CardItem.jsx @@ -20,7 +20,10 @@ export function CardItem({ item, openContact }) { { item.name } { item.handle } - { item.status === 'connected' && ( + { item.status === 'connected' && item.offsync === 1 && ( + + )} + { item.status === 'connected' && item.offsync !== 1 && ( )} { item.status === 'requested' && ( diff --git a/app/mobile/src/session/cards/cardItem/CardItem.styled.js b/app/mobile/src/session/cards/cardItem/CardItem.styled.js index 337cef53..b316c21a 100644 --- a/app/mobile/src/session/cards/cardItem/CardItem.styled.js +++ b/app/mobile/src/session/cards/cardItem/CardItem.styled.js @@ -48,6 +48,12 @@ export const styles = StyleSheet.create({ borderRadius: 4, backgroundColor: Colors.connecting, }, + offsync: { + width: 8, + height: 8, + borderRadius: 4, + backgroundColor: Colors.error, + }, pending: { width: 8, height: 8, diff --git a/app/mobile/src/session/cards/useCards.hook.js b/app/mobile/src/session/cards/useCards.hook.js index 5c509034..b542d7e4 100644 --- a/app/mobile/src/session/cards/useCards.hook.js +++ b/app/mobile/src/session/cards/useCards.hook.js @@ -31,7 +31,7 @@ export function useCards() { const setCardItem = (item) => { const { profile, detail } = item; - + return { cardId: item.cardId, name: profile.name, @@ -39,6 +39,7 @@ export function useCards() { status: detail.status, offsync: item.offsync, blocked: item.blocked, + offsync: item.offsync, updated: detail.statusUpdated, logo: profile.imageSet ? card.actions.getCardLogo(item.cardId, profile.revision) : 'avatar', } diff --git a/app/mobile/src/session/contact/Contact.jsx b/app/mobile/src/session/contact/Contact.jsx index 895bffdb..e0567f5c 100644 --- a/app/mobile/src/session/contact/Contact.jsx +++ b/app/mobile/src/session/contact/Contact.jsx @@ -9,7 +9,18 @@ import Colors from 'constants/Colors'; export function ContactTitle({ contact, closeContact }) { const { state, actions } = useContact(contact, closeContact); - return ({ `${state.handle}@${state.node}` }); + + return ( + + + { `${state.handle}@${state.node}` } + + { state.offsync === 1 && ( + + )} + + + ); } export function Contact({ contact, closeContact }) { diff --git a/app/mobile/src/session/contact/Contact.styled.js b/app/mobile/src/session/contact/Contact.styled.js index 13e8c006..fe53e452 100644 --- a/app/mobile/src/session/contact/Contact.styled.js +++ b/app/mobile/src/session/contact/Contact.styled.js @@ -18,6 +18,16 @@ export const styles = StyleSheet.create({ title: { fontSize: 18, }, + resync: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + icon: { + width: 32, + paddingLeft: 8 + }, drawer: { paddingTop: 16, }, diff --git a/app/mobile/src/session/contact/useContact.hook.js b/app/mobile/src/session/contact/useContact.hook.js index 3be55fb6..4e55689a 100644 --- a/app/mobile/src/session/contact/useContact.hook.js +++ b/app/mobile/src/session/contact/useContact.hook.js @@ -18,7 +18,8 @@ export function useContact(contact, close) { status: null, cardId: null, guid: null, - busy: false + busy: false, + offsync: false, }); const dimensions = useWindowDimensions(); @@ -42,10 +43,10 @@ export function useContact(contact, close) { if (contact?.card) { const selected = card.state.cards.get(contact.card); if (selected) { - const { profile, detail, cardId } = selected; + const { offsync, profile, detail, cardId } = selected; const { name, handle, node, location, description, guid, imageSet, revision } = profile; const logo = imageSet ? card.actions.getCardLogo(cardId, revision) : 'avatar'; - updateState({ name, handle, node, location, description, logo, cardId, guid, status: detail.status }); + updateState({ offsync, name, handle, node, location, description, logo, cardId, guid, status: detail.status }); stateSet = true; } } @@ -56,12 +57,12 @@ export function useContact(contact, close) { const { cardId, profile, detail } = selected; const { name, handle, node, location, description, guid, imageSet, revision } = profile; const logo = imageSet ? card.actions.getCardLogo(cardId, revision) : 'avatar'; - updateState({ name, handle, node, location, description, logo, cardId, guid, status: detail.status }); + updateState({ offsync, name, handle, node, location, description, logo, cardId, guid, status: detail.status }); stateSet = true; } else { const { name, handle, node, location, description, logo, guid } = contact.account; - updateState({ name, handle, node, location, description, logo, guid, cardId: null, status: null }); + updateState({ offsync: false, name, handle, node, location, description, logo, guid, cardId: null, status: null }); stateSet = true; } } @@ -176,6 +177,9 @@ export function useContact(contact, close) { close(); }); }, + resync: () => { + card.actions.resync(contact.card); + }, }; return { state, actions };