From 4ca0f53da1f65a97830d93f58fc5b93d9ae04e5e Mon Sep 17 00:00:00 2001 From: balzack Date: Wed, 14 Sep 2022 00:27:49 -0700 Subject: [PATCH] syncing profile --- app/mobile/App.js | 27 +++--- app/mobile/src/api/getProfile.js | 4 +- app/mobile/src/api/getProfileImageUrl.js | 4 +- app/mobile/src/api/setProfileData.js | 4 +- app/mobile/src/api/setProfileImage.js | 4 +- app/mobile/src/context/ProfileContext.js | 14 +++ app/mobile/src/context/useAppContext.hook.js | 57 ++++++----- .../src/context/useProfileContext.hook.js | 72 ++++++++++++++ .../src/context/useStoreContext.hook.js | 94 +++++-------------- 9 files changed, 164 insertions(+), 116 deletions(-) create mode 100644 app/mobile/src/context/ProfileContext.js create mode 100644 app/mobile/src/context/useProfileContext.hook.js diff --git a/app/mobile/App.js b/app/mobile/App.js index ecc82175..c72eb11e 100644 --- a/app/mobile/App.js +++ b/app/mobile/App.js @@ -7,23 +7,26 @@ import { Session } from 'src/session/Session'; import { Admin } from 'src/admin/Admin'; import { StoreContextProvider } from 'context/StoreContext'; import { AppContextProvider } from 'context/AppContext'; +import { ProfileContextProvider } from 'context/ProfileContext'; export default function App() { return ( - - - - } /> - } /> - } /> - } /> - } /> - } /> - - - + + + + + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); } diff --git a/app/mobile/src/api/getProfile.js b/app/mobile/src/api/getProfile.js index 5fd6c1e2..7789e053 100644 --- a/app/mobile/src/api/getProfile.js +++ b/app/mobile/src/api/getProfile.js @@ -1,7 +1,7 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function getProfile(token) { - let profile = await fetchWithTimeout(`/profile?agent=${token}`, { method: 'GET' }); +export async function getProfile(server, token) { + let profile = await fetchWithTimeout(`https://${server}/profile?agent=${token}`, { method: 'GET' }); checkResponse(profile) return await profile.json() } diff --git a/app/mobile/src/api/getProfileImageUrl.js b/app/mobile/src/api/getProfileImageUrl.js index 038a7544..46d96a05 100644 --- a/app/mobile/src/api/getProfileImageUrl.js +++ b/app/mobile/src/api/getProfileImageUrl.js @@ -1,4 +1,4 @@ -export function getProfileImageUrl(token, revision) { - return '/profile/image?agent=' + token + "&revision=" + revision +export function getProfileImageUrl(server, token, revision) { + return `https://${server}/profile/image?agent=${token}&revision=${revision}`; } diff --git a/app/mobile/src/api/setProfileData.js b/app/mobile/src/api/setProfileData.js index 00a405fc..e973b035 100644 --- a/app/mobile/src/api/setProfileData.js +++ b/app/mobile/src/api/setProfileData.js @@ -1,8 +1,8 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function setProfileData(token, name, location, description) { +export async function setProfileData(server, token, name, location, description) { let data = { name: name, location: location, description: description }; - let profile = await fetchWithTimeout(`/profile/data?agent=${token}`, { method: 'PUT', body: JSON.stringify(data) }); + let profile = await fetchWithTimeout(`https://${server}/profile/data?agent=${token}`, { method: 'PUT', body: JSON.stringify(data) }); checkResponse(profile) return await profile.json() } diff --git a/app/mobile/src/api/setProfileImage.js b/app/mobile/src/api/setProfileImage.js index 9eb46a11..a40cafe9 100644 --- a/app/mobile/src/api/setProfileImage.js +++ b/app/mobile/src/api/setProfileImage.js @@ -1,7 +1,7 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function setProfileImage(token, image) { - let profile = await fetchWithTimeout(`/profile/image?agent=${token}`, { method: 'PUT', body: JSON.stringify(image) }); +export async function setProfileImage(server, token, image) { + let profile = await fetchWithTimeout(`https://${server}/profile/image?agent=${token}`, { method: 'PUT', body: JSON.stringify(image) }); checkResponse(profile) return await profile.json() } diff --git a/app/mobile/src/context/ProfileContext.js b/app/mobile/src/context/ProfileContext.js new file mode 100644 index 00000000..ef48f314 --- /dev/null +++ b/app/mobile/src/context/ProfileContext.js @@ -0,0 +1,14 @@ +import { createContext } from 'react'; +import { useProfileContext } from './useProfileContext.hook'; + +export const ProfileContext = createContext({}); + +export function ProfileContextProvider({ children }) { + const { state, actions } = useProfileContext(); + return ( + + {children} + + ); +} + diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js index 686f3f36..d6051b50 100644 --- a/app/mobile/src/context/useAppContext.hook.js +++ b/app/mobile/src/context/useAppContext.hook.js @@ -5,25 +5,47 @@ import { setAccountAccess } from 'api/setAccountAccess'; import { addAccount } from 'api/addAccount'; import { getUsername } from 'api/getUsername'; import { StoreContext } from 'context/StoreContext'; +import { ProfileContext } from 'context/ProfileContext'; export function useAppContext() { const [state, setState] = useState({ session: null, disconnected: null, }); - const [appRevision, setAppRevision] = useState(); const store = useContext(StoreContext); + const profile = useContext(ProfileContext); const delay = useRef(2); const ws = useRef(null); - const revision = useRef(null); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - const resetData = () => { - revision.current = null; + useEffect(() => { + init(); + }, []); + + const init = async () => { + const access = await store.actions.init(); + if (access) { + await setSession(access); + } + else { + updateState({ session: false }); + } + } + + const setSession = async (access) => { + profile.actions.setSession(access); + updateState({ session: true }); + setWebsocket(access.server, access.appToken); + } + + const clearSession = async () => { + profile.actions.clearSession(); + updateState({ session: false }); + clearWebsocket(); } const actions = { @@ -32,19 +54,22 @@ export function useAppContext() { create: async (server, username, password, token) => { await addAccount(server, username, password, token); const access = await setLogin(username, server, password) - store.actions.setSession({ ...access, server }); + await setSession({ ...access, server }); + await store.actions.setSession({ ...access, server}); }, access: async (server, token) => { const access = await setAccountAccess(server, token); - store.actions.setSession({ ...access, server }); + await setSession({ ...access, server }); + await store.actions.setSession({ ...access, server}); }, login: async (username, password) => { const acc = username.split('@'); const access = await setLogin(acc[0], acc[1], password) - store.actions.setSession({ ...access, server: acc[1] }); + await setSession({ ...access, server: acc[1] }); + await store.actions.setSession({ ...access, server: acc[1]}); }, logout: async () => { - resetData(); + await clearSession(); await store.actions.clearSession(); }, } @@ -55,7 +80,7 @@ export function useAppContext() { ws.current.onmessage = (ev) => { try { const rev = JSON.parse(ev.data); - setAppRevision(rev); + profile.actions.setRevision(rev.profileRevision); updateState({ disconnected: false }); } catch (err) { @@ -95,20 +120,6 @@ export function useAppContext() { } } - useEffect(() => { - if (store.state.init) { - if (store.state.session) { - const { server, appToken } = store.state.session; - setWebsocket(server, appToken); - updateState({ session: true }); - } - else { - clearWebsocket(); - updateState({ session: false }); - } - } - }, [store.state.session, store.state.init]); - return { state, actions } } diff --git a/app/mobile/src/context/useProfileContext.hook.js b/app/mobile/src/context/useProfileContext.hook.js new file mode 100644 index 00000000..ddb75d03 --- /dev/null +++ b/app/mobile/src/context/useProfileContext.hook.js @@ -0,0 +1,72 @@ +import { useState, useRef, useContext } from 'react'; +import { getProfile } from 'api/getProfile'; +import { setProfileData } from 'api/setProfileData'; +import { setProfileImage } from 'api/setProfileImage'; +import { getProfileImageUrl } from 'api/getProfileImageUrl'; +import { StoreContext } from 'context/StoreContext'; + +export function useProfileContext() { + const [state, setState] = useState({ + profile: {}, + imageUrl: null, + }); + const store = useContext(StoreContext); + + const session = useRef(null); + const curRevision = useRef(null); + const setRevision = useRef(null); + const syncing = useRef(false); + + const updateState = (value) => { + setState((s) => ({ ...s, ...value })) + } + + const sync = async () => { + if (!syncing.current && setRevision.current !== curRevision.current) { + syncing.current = true; + + const revision = curRevision.current; + const { server, appToken, guid } = session.current; + const profile = await getProfile(server, appToken); + await store.actions.setProfile(guid, profile); + await store.actions.setProfileRevision(guid, revision); + updateState({ profile, imageUrl: getProfileImageUrl(server, appToken, revision) }); + setRevision.current = revision; + + syncing.current = false; + sync(); + } + }; + + const actions = { + setSession: async (access) => { + const { guid, server, appToken } = access; + const profile = await store.actions.getProfile(guid); + const revision = await store.actions.getProfileRevision(guid); + updateState({ profile, imageUrl: getProfileImageUrl(server, appToken, revision) }); + setRevision.current = revision; + curRevision.current = revision; + session.current = access; + }, + clearSession: () => { + session.current = {}; + updateState({ profile: null }); + }, + setRevision: (rev) => { + curRevision.current = rev; + sync(); + }, + setProfileData: async (name, location, description) => { + const { server, appToken } = session.current; + await setProfileData(server, appToken, name, location, description); + }, + setProfileImage: async (image) => { + const { server, appToken } = session.current; + await setProfileImage(server, appToken, image); + }, + } + + return { state, actions } +} + + diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index a9164ea2..8009dec4 100644 --- a/app/mobile/src/context/useStoreContext.hook.js +++ b/app/mobile/src/context/useStoreContext.hook.js @@ -4,97 +4,45 @@ import SQLite from "react-native-sqlite-storage"; const DATABAG_DB = 'databag_v001.db'; export function useStoreContext() { - const [state, setState] = useState({ - init: false, - session: null, - sessionId: 0, - profileRevision: null, - cardRevision: null, - channelRevision: null, - accountRevision: null, - }); - + const [state, setState] = useState({}); const db = useRef(null); - const session = useRef(null); - const sessionId = useRef(0); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - useEffect(() => { - initialize(); - }, []); - - useEffect(() => { - if (state.init && state.session && sessionId.current === state.sessionId) { - const revision = { - accountRevision: state.accountRevision, - profileRevision: state.profileRevision, - cardRevision: state.cardRevision, - channelRevision: state.channelRevision, - } - const revisionId = `${session.current.guid}_revision`; - db.current.executeSql(`UPDATE app SET value=? WHERE key='${revisionId}';`, [encodeObject(revision)]); - } - }, [state]); - - const initialize = async () => { - SQLite.DEBUG(false); - SQLite.enablePromise(true); - db.current = await SQLite.openDatabase({ name: DATABAG_DB, location: "default" }); - await db.current.executeSql("CREATE TABLE IF NOT EXISTS app (key text, value text, unique(key));"); - await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values ('session', null);"); - - session.current = await getAppValue(db.current, 'session'); - if (!session.current) { - updateState({ init: true }); - } - else { - const revisionId = `${session.current.guid}_revision`; - const revision = await getAppValue(db.current, revisionId, {}); - updateState({ init: true, session: session.current, ...revision }); - } - }; - const actions = { + init: async () => { + SQLite.DEBUG(false); + SQLite.enablePromise(true); + db.current = await SQLite.openDatabase({ name: DATABAG_DB, location: "default" }); + await db.current.executeSql("CREATE TABLE IF NOT EXISTS app (key text, value text, unique(key));"); + await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values ('session', null);"); + return await getAppValue(db.current, 'session'); + }, setSession: async (access) => { await db.current.executeSql("UPDATE app SET value=? WHERE key='session';", [encodeObject(access)]); - - const revisionId = `${access.guid}_revision`; - const revision = await getAppValue(db.current, revisionId, {}); - - session.current = access; - sessionId.current++; - updateState({ session: access, sessionId: sessionId.current, ...revision }); }, clearSession: async () => { await db.current.executeSql("UPDATE app set value=? WHERE key='session';", [null]); - session.current = null; - updateState({ session: null }); }, - setProfileRevision: (id, profileRevision) => { - if (sessionId.current === id) { - updateState({ profileRevision }); - } + getProfile: async (guid) => { + const dataId = `${guid}_profile`; + return await getAppValue(db.current, dataId, {}); }, - setAccountRevision: (id, accountRevision) => { - if (sessionId.current === id) { - updateState({ accountRevision }); - } + setProfile: async (guid, profile) => { + const dataId = `${guid}_profile`; + await db.current.executeSql("UPDATE app SET value=? WHERE key='?';", [encodeObject(profile)], dataId); }, - setCardRevision: (id, cardRevision) => { - if (sessionId.current === id) { - updateState({ cardRevision }); - } + getProfileRevision: async (guid) => { + const dataId = `${guid}_profileRevision`; + return await getAppValue(db.current, dataId, 0); }, - setChannelRevision: (channelRevision) => { - if (sessionId.current === id) { - updateState({ channelRevision }); - } + setProfileRevision: async (guid, revision) => { + const dataId = `${guid}_profileRevision`; + await db.current.executeSql("UPDATE app SET value=? WHERE key='?';", [encodeObject(revision)], dataId); }, } - return { state, actions } }