diff options
Diffstat (limited to 'src/state')
-rw-r--r-- | src/state/index.ts | 18 | ||||
-rw-r--r-- | src/state/lib/api.ts | 33 | ||||
-rw-r--r-- | src/state/models/me.ts | 5 | ||||
-rw-r--r-- | src/state/models/root-store.ts | 4 | ||||
-rw-r--r-- | src/state/models/session.ts | 186 |
5 files changed, 130 insertions, 116 deletions
diff --git a/src/state/index.ts b/src/state/index.ts index 25787ea36..c833ad9f6 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -8,13 +8,15 @@ import * as storage from './lib/storage' import {ShellModel} from './models/shell' const ROOT_STATE_STORAGE_KEY = 'root' +const DEFAULT_SERVICE = 'http://localhost:2583' export async function setupState() { let rootStore: RootStoreModel let data: any - const api = AdxApi.service(`http://localhost:2583`) - await libapi.setup(api) + libapi.doPolyfill() + + const api = AdxApi.service(DEFAULT_SERVICE) rootStore = new RootStoreModel(api) try { data = (await storage.load(ROOT_STATE_STORAGE_KEY)) || {} @@ -29,17 +31,7 @@ export async function setupState() { storage.save(ROOT_STATE_STORAGE_KEY, snapshot) }) - // TODO - rootStore.session.setAuthed(true) - // if (env.authStore) { - // const isAuthed = await auth.isAuthed(env.authStore) - // rootStore.session.setAuthed(isAuthed) - - // // handle redirect from auth - // if (await auth.initialLoadUcanCheck(env.authStore)) { - // rootStore.session.setAuthed(true) - // } - // } + await rootStore.session.setup() await rootStore.me.load() console.log(rootStore.me) diff --git a/src/state/lib/api.ts b/src/state/lib/api.ts index 8695d81bf..96a7fd755 100644 --- a/src/state/lib/api.ts +++ b/src/state/lib/api.ts @@ -3,13 +3,13 @@ * models live. They are made available to every model via dependency injection. */ -import RNFetchBlob from 'rn-fetch-blob' // import {ReactNativeStore} from './auth' -import AdxApi, {ServiceClient} from '../../third-party/api' +import AdxApi from '../../third-party/api' +import {ServiceClient} from '../../third-party/api/src/index' import {AdxUri} from '../../third-party/uri' import * as storage from './storage' -export async function setup(adx: ServiceClient) { +export function doPolyfill() { AdxApi.xrpc.fetch = fetchHandler } @@ -121,32 +121,31 @@ async function fetchHandler( reqHeaders: Record<string, string>, reqBody: any, ): Promise<FetchHandlerResponse> { - reqHeaders['Authorization'] = 'did:test:alice' // DEBUG - const reqMimeType = reqHeaders['Content-Type'] || reqHeaders['content-type'] if (reqMimeType && reqMimeType.startsWith('application/json')) { reqBody = JSON.stringify(reqBody) } - const res = await RNFetchBlob.fetch( - /** @ts-ignore method coersion, it's fine -prf */ - reqMethod, - reqUri, - reqHeaders, - reqBody, - ) + const res = await fetch(reqUri, { + method: reqMethod, + headers: reqHeaders, + body: reqBody, + }) - const resStatus = res.info().status - const resHeaders = (res.info().headers || {}) as Record<string, string> + const resStatus = res.status + const resHeaders: Record<string, string> = {} + res.headers.forEach((value: string, key: string) => { + resHeaders[key] = value + }) const resMimeType = resHeaders['Content-Type'] || resHeaders['content-type'] let resBody if (resMimeType) { if (resMimeType.startsWith('application/json')) { - resBody = res.json() + resBody = await res.json() } else if (resMimeType.startsWith('text/')) { - resBody = res.text() + resBody = await res.text() } else { - resBody = res.base64() + throw new Error('TODO: non-textual response body') } } return { diff --git a/src/state/models/me.ts b/src/state/models/me.ts index 1ec418b78..fb5445cbe 100644 --- a/src/state/models/me.ts +++ b/src/state/models/me.ts @@ -14,9 +14,8 @@ export class MeModel { async load() { const sess = this.rootStore.session if (sess.isAuthed) { - // TODO - this.did = 'did:test:alice' - this.name = 'alice.todo' + this.did = sess.userdid || '' + this.name = sess.username const profile = await this.rootStore.api.todo.social.getProfile({ user: this.did, }) diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts index ea3da1859..59bed4a6a 100644 --- a/src/state/models/root-store.ts +++ b/src/state/models/root-store.ts @@ -4,7 +4,7 @@ import {makeAutoObservable} from 'mobx' import AdxApi from '../../third-party/api' -import {ServiceClient} from '../../third-party/api/src/index' +import type {ServiceClient} from '../../third-party/api/src/index' import {createContext, useContext} from 'react' import {isObj, hasProp} from '../lib/type-guards' import {SessionModel} from './session' @@ -13,7 +13,7 @@ import {ShellModel} from './shell' import {MeModel} from './me' export class RootStoreModel { - session = new SessionModel() + session = new SessionModel(this) nav = new NavigationModel() shell = new ShellModel() me = new MeModel(this) diff --git a/src/state/models/session.ts b/src/state/models/session.ts index 7c7602066..71f3cd638 100644 --- a/src/state/models/session.ts +++ b/src/state/models/session.ts @@ -1,109 +1,133 @@ import {makeAutoObservable} from 'mobx' +import AdxApi from '../../third-party/api' import {isObj, hasProp} from '../lib/type-guards' -// import {UserConfig} from '../../api' -// import * as auth from '../lib/auth' +import {RootStoreModel} from './root-store' + +interface SessionData { + service: string + token: string + username: string + userdid: string +} export class SessionModel { - isAuthed = false + data: SessionData | null = null - constructor() { + constructor(public rootStore: RootStoreModel) { makeAutoObservable(this, { + rootStore: false, serialize: false, hydrate: false, }) } + get isAuthed() { + return this.data !== null + } + serialize(): unknown { - return { - isAuthed: this.isAuthed, - } + return this.data } hydrate(v: unknown) { if (isObj(v)) { - if (hasProp(v, 'isAuthed') && typeof v.isAuthed === 'boolean') { - this.isAuthed = v.isAuthed + const data: SessionData = { + service: '', + token: '', + username: '', + userdid: '', + } + if (hasProp(v, 'service') && typeof v.service === 'string') { + data.service = v.service + } + if (hasProp(v, 'token') && typeof v.token === 'string') { + data.token = v.token + } + if (hasProp(v, 'username') && typeof v.username === 'string') { + data.username = v.username + } + if (hasProp(v, 'userdid') && typeof v.userdid === 'string') { + data.userdid = v.userdid + } + if (data.service && data.token && data.username && data.userdid) { + this.data = data } } } - setAuthed(v: boolean) { - this.isAuthed = v + clear() { + console.log('clear()') + this.data = null } -} -// TODO -/*login: flow(function* () { - /*self.uiIsProcessing = true - self.uiError = undefined - try { - if (!self.env.authStore) { - throw new Error('Auth store not initialized') + setState(data: SessionData) { + this.data = data + } + + private configureApi(): boolean { + if (!this.data) { + return false } - const res = yield auth.requestAppUcan(self.env.authStore) - self.isAuthed = res - self.uiIsProcessing = false - return res - } catch (e: any) { - console.error('Failed to request app ucan', e) - self.uiError = e.toString() - self.uiIsProcessing = false - return false + + try { + const serviceUri = new URL(this.data.service) + this.rootStore.api.xrpc.uri = serviceUri + } catch (e) { + console.error( + `Invalid service URL: ${this.data.service}. Resetting session.`, + ) + console.error(e) + this.clear() + return false + } + + this.rootStore.api.setHeader('Authorization', `Bearer ${this.data.token}`) + return true } -}), -logout: flow(function* () { - self.uiIsProcessing = true - self.uiError = undefined - try { - if (!self.env.authStore) { - throw new Error('Auth store not initialized') + + async setup(): Promise<void> { + if (!this.configureApi()) { + return } - const res = yield auth.logout(self.env.authStore) - self.isAuthed = false - self.uiIsProcessing = false - return res - } catch (e: any) { - console.error('Failed to log out', e) - self.uiError = e.toString() - self.uiIsProcessing = false - return false + + try { + const sess = await this.rootStore.api.todo.adx.getSession({}) + if (sess.success && this.data && this.data.userdid === sess.data.did) { + return // success + } + } catch (e: any) {} + + this.clear() // invalid session cached } -}), -loadAccount: flow(function* () { - self.uiIsProcessing = true - self.uiError = undefined - try { - // const cfg = yield UserConfig.hydrate({ - // serverUrl: self.serverUrl, - // secretKeyStr: self.secretKeyStr, - // rootAuthToken: self.rootAuthToken, - // }) - // self.env.api.setUserCfg(cfg) - self.isAuthed = true - self.uiIsProcessing = false - return true - } catch (e: any) { - console.error('Failed to create test account', e) - self.uiError = e.toString() - self.uiIsProcessing = false - return false + + async login({ + service, + username, + password, + }: { + service: string + username: string + password: string + }) { + const api = AdxApi.service(service) + const res = await api.todo.adx.createSession({}, {username, password}) + if (res.data.jwt) { + this.setState({ + service: service, + token: res.data.jwt, + username: res.data.name, + userdid: res.data.did, + }) + this.configureApi() + } } -}), -createTestAccount: flow(function* (_serverUrl: string) { - self.uiIsProcessing = true - self.uiError = undefined - try { - // const cfg = yield UserConfig.createTest(serverUrl) - // const state = yield cfg.serialize() - // self.serverUrl = state.serverUrl - // self.secretKeyStr = state.secretKeyStr - // self.rootAuthToken = state.rootAuthToken - self.isAuthed = true - // self.env.api.setUserCfg(cfg) - } catch (e: any) { - console.error('Failed to create test account', e) - self.uiError = e.toString() + + async logout() { + if (this.isAuthed) { + this.rootStore.api.todo.adx.deleteSession({}).catch((e: any) => { + console.error('(Minor issue) Failed to delete session on the server', e) + }) + } + this.clear() } - self.uiIsProcessing = false -}), -}))*/ +} |