From 33966512f18292aff9727ceef35303181ed9a379 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Wed, 14 Sep 2022 12:18:16 -0700 Subject: [PATCH] syncing account and profile --- app/mobile/App.js | 31 ++++---- app/mobile/src/api/getAccountStatus.js | 4 +- app/mobile/src/api/setAccountLogin.js | 4 +- app/mobile/src/api/setAccountSearchable.js | 4 +- app/mobile/src/context/AccountContext.js | 14 ++++ .../src/context/useAccountContext.hook.js | 75 +++++++++++++++++++ app/mobile/src/context/useAppContext.hook.js | 9 ++- .../src/context/useProfileContext.hook.js | 19 +++-- .../src/context/useStoreContext.hook.js | 36 ++++++++- 9 files changed, 164 insertions(+), 32 deletions(-) create mode 100644 app/mobile/src/context/AccountContext.js create mode 100644 app/mobile/src/context/useAccountContext.hook.js diff --git a/app/mobile/App.js b/app/mobile/App.js index c72eb11e..b81fad99 100644 --- a/app/mobile/App.js +++ b/app/mobile/App.js @@ -7,26 +7,29 @@ import { Session } from 'src/session/Session'; import { Admin } from 'src/admin/Admin'; import { StoreContextProvider } from 'context/StoreContext'; import { AppContextProvider } from 'context/AppContext'; +import { AccountContextProvider } from 'context/AccountContext'; import { ProfileContextProvider } from 'context/ProfileContext'; export default function App() { return ( - - - - - } /> - } /> - } /> - } /> - } /> - } /> - - - - + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + + + + + ); } diff --git a/app/mobile/src/api/getAccountStatus.js b/app/mobile/src/api/getAccountStatus.js index 3f1f0fa6..4c7a7f4b 100644 --- a/app/mobile/src/api/getAccountStatus.js +++ b/app/mobile/src/api/getAccountStatus.js @@ -1,7 +1,7 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function getAccountStatus(token) { - let status = await fetchWithTimeout('/account/status?agent=' + token, { method: 'GET' }); +export async function getAccountStatus(server, token) { + let status = await fetchWithTimeout(`https://${server}/account/status?agent=${token}`, { method: 'GET' }); checkResponse(status); return await status.json() } diff --git a/app/mobile/src/api/setAccountLogin.js b/app/mobile/src/api/setAccountLogin.js index 021dbc38..9365469c 100644 --- a/app/mobile/src/api/setAccountLogin.js +++ b/app/mobile/src/api/setAccountLogin.js @@ -1,10 +1,10 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; import base64 from 'react-native-base64' -export async function setAccountLogin(token, username, password) { +export async function setAccountLogin(server, token, username, password) { let headers = new Headers() headers.append('Credentials', 'Basic ' + base64.encode(username + ":" + password)); - let res = await fetchWithTimeout(`/account/login?agent=${token}`, { method: 'PUT', headers }) + let res = await fetchWithTimeout(`https://${server}/account/login?agent=${token}`, { method: 'PUT', headers }) checkResponse(res); } diff --git a/app/mobile/src/api/setAccountSearchable.js b/app/mobile/src/api/setAccountSearchable.js index 84f357e5..57990d1a 100644 --- a/app/mobile/src/api/setAccountSearchable.js +++ b/app/mobile/src/api/setAccountSearchable.js @@ -1,7 +1,7 @@ import { checkResponse, fetchWithTimeout } from './fetchUtil'; -export async function setAccountSearchable(token, flag) { - let res = await fetchWithTimeout('/account/searchable?agent=' + token, { method: 'PUT', body: JSON.stringify(flag) }) +export async function setAccountSearchable(server, token, flag) { + let res = await fetchWithTimeout(`https://${server}/account/searchable?agent=${token}`, { method: 'PUT', body: JSON.stringify(flag) }) checkResponse(res); } diff --git a/app/mobile/src/context/AccountContext.js b/app/mobile/src/context/AccountContext.js new file mode 100644 index 00000000..e4c34613 --- /dev/null +++ b/app/mobile/src/context/AccountContext.js @@ -0,0 +1,14 @@ +import { createContext } from 'react'; +import { useAccountContext } from './useAccountContext.hook'; + +export const AccountContext = createContext({}); + +export function AccountContextProvider({ children }) { + const { state, actions } = useAccountContext(); + return ( + + {children} + + ); +} + diff --git a/app/mobile/src/context/useAccountContext.hook.js b/app/mobile/src/context/useAccountContext.hook.js new file mode 100644 index 00000000..5e2c7031 --- /dev/null +++ b/app/mobile/src/context/useAccountContext.hook.js @@ -0,0 +1,75 @@ +import { useState, useRef, useContext } from 'react'; +import { StoreContext } from 'context/StoreContext'; +import { setAccountSearchable } from 'api/setAccountSearchable'; +import { getAccountStatus } from 'api/getAccountStatus'; +import { setAccountLogin } from 'api/setAccountLogin'; + +export function useAccountContext() { + const [state, setState] = useState({ + status: {}, + }); + 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; + + try { + const revision = curRevision.current; + const { server, appToken, guid } = session.current; + const status = await getAccountStatus(server, appToken); + await store.actions.setAccountStatus(guid, status); + await store.actions.setAccountRevision(guid, revision); + updateState({ status }); + setRevision.current = revision; + } + catch(err) { + console.log(err); + } + + syncing.current = false; + sync(); + } + }; + + const actions = { + setSession: async (access) => { + const { guid, server, appToken } = access; + const status = await store.actions.getAccountStatus(guid); + const revision = await store.actions.getAccountRevision(guid); + updateState({ status }); + setRevision.current = revision; + curRevision.current = revision; + session.current = access; + }, + clearSession: () => { + session.current = {}; + updateState({ account: null }); + }, + setRevision: (rev) => { + curRevision.current = rev; + sync(); + }, + setSearchable: async (flag) => { + const { server, appToken } = access; + await setAccountSearchable(server, appToken, flag); + }, + setLogin: async (username, password) => { + const { server, appToken } = access; + await setAccountLogin(server, appToken, username, password); + }, + } + + return { state, actions } +} + + diff --git a/app/mobile/src/context/useAppContext.hook.js b/app/mobile/src/context/useAppContext.hook.js index d6051b50..60b9c4d9 100644 --- a/app/mobile/src/context/useAppContext.hook.js +++ b/app/mobile/src/context/useAppContext.hook.js @@ -5,6 +5,7 @@ import { setAccountAccess } from 'api/setAccountAccess'; import { addAccount } from 'api/addAccount'; import { getUsername } from 'api/getUsername'; import { StoreContext } from 'context/StoreContext'; +import { AccountContext } from 'context/AccountContext'; import { ProfileContext } from 'context/ProfileContext'; export function useAppContext() { @@ -13,6 +14,7 @@ export function useAppContext() { disconnected: null, }); const store = useContext(StoreContext); + const account = useContext(AccountContext); const profile = useContext(ProfileContext); const delay = useRef(2); @@ -37,12 +39,14 @@ export function useAppContext() { } const setSession = async (access) => { - profile.actions.setSession(access); + await account.actions.setSession(access); + await profile.actions.setSession(access); updateState({ session: true }); setWebsocket(access.server, access.appToken); } const clearSession = async () => { + account.actions.clearSession(); profile.actions.clearSession(); updateState({ session: false }); clearWebsocket(); @@ -80,7 +84,8 @@ export function useAppContext() { ws.current.onmessage = (ev) => { try { const rev = JSON.parse(ev.data); - profile.actions.setRevision(rev.profileRevision); + profile.actions.setRevision(rev.profile); + account.actions.setRevision(rev.account); updateState({ disconnected: false }); } catch (err) { diff --git a/app/mobile/src/context/useProfileContext.hook.js b/app/mobile/src/context/useProfileContext.hook.js index ddb75d03..b3f92c42 100644 --- a/app/mobile/src/context/useProfileContext.hook.js +++ b/app/mobile/src/context/useProfileContext.hook.js @@ -25,13 +25,18 @@ export function useProfileContext() { 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; + try { + 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; + } + catch(err) { + console.log(err); + } syncing.current = false; sync(); diff --git a/app/mobile/src/context/useStoreContext.hook.js b/app/mobile/src/context/useStoreContext.hook.js index 8009dec4..89b68342 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_v001.db'; +const DATABAG_DB = 'databag_v005.db'; export function useStoreContext() { const [state, setState] = useState({}); @@ -26,13 +26,15 @@ export function useStoreContext() { clearSession: async () => { await db.current.executeSql("UPDATE app set value=? WHERE key='session';", [null]); }, + getProfile: async (guid) => { const dataId = `${guid}_profile`; return await getAppValue(db.current, dataId, {}); }, setProfile: async (guid, profile) => { const dataId = `${guid}_profile`; - await db.current.executeSql("UPDATE app SET value=? WHERE key='?';", [encodeObject(profile)], dataId); + await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values (?, null);", [dataId]); + await db.current.executeSql("UPDATE app SET value=? WHERE key=?;", [encodeObject(profile), dataId]); }, getProfileRevision: async (guid) => { const dataId = `${guid}_profileRevision`; @@ -40,8 +42,28 @@ export function useStoreContext() { }, setProfileRevision: async (guid, revision) => { const dataId = `${guid}_profileRevision`; - await db.current.executeSql("UPDATE app SET value=? WHERE key='?';", [encodeObject(revision)], dataId); + await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values (?, 0);", [dataId]); + await db.current.executeSql("UPDATE app SET value=? WHERE key=?;", [encodeObject(revision), dataId]); }, + + getAccountStatus: async (guid) => { + const dataId = `${guid}_status`; + return await getAppValue(db.current, dataId, {}); + }, + setAccountStatus: async (guid, status) => { + const dataId = `${guid}_status`; + await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values (?, null);", [dataId]); + await db.current.executeSql("UPDATE app SET value=? WHERE key=?;", [encodeObject(status), dataId]); + }, + getAccountRevision: async (guid) => { + const dataId = `${guid}_accountRevision`; + return await getAppValue(db.current, dataId, 0); + }, + setAccountRevision: async (guid, revision) => { + const dataId = `${guid}_accountRevision`; + await db.current.executeSql("INSERT OR IGNORE INTO app (key, value) values (?, 0);", [dataId]); + await db.current.executeSql("UPDATE app SET value=? WHERE key=?;", [encodeObject(revision), dataId]); + }, } return { state, actions } } @@ -67,6 +89,14 @@ function hasResult(res) { return true; } +function executeSql(sql: SQLite.SQLiteDatabase, query, params, uset) { + return new Promise((resolve, reject) => { + sql.executeSql(query, params, (tx, results) => { + resolve(results); + }); + }); +} + async function getAppValue(sql: SQLite.SQLiteDatabase, id: string, unset) { const res = await sql.executeSql(`SELECT * FROM app WHERE key='${id}';`); if (hasResult(res)) {