about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'src/state')
-rw-r--r--src/state/index.ts18
-rw-r--r--src/state/lib/api.ts33
-rw-r--r--src/state/models/me.ts5
-rw-r--r--src/state/models/root-store.ts4
-rw-r--r--src/state/models/session.ts186
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
-}),
-}))*/
+}