Add account creation

zio/stable
Paul Frazee 2022-09-27 14:24:47 -05:00
parent c89ec94b17
commit ef4b9cf8d9
17 changed files with 727 additions and 272 deletions

View File

@ -291,8 +291,6 @@ PODS:
- React-jsi (= 0.68.2) - React-jsi (= 0.68.2)
- React-logger (= 0.68.2) - React-logger (= 0.68.2)
- React-perflogger (= 0.68.2) - React-perflogger (= 0.68.2)
- rn-fetch-blob (0.12.0):
- React-Core
- RNCAsyncStorage (1.17.10): - RNCAsyncStorage (1.17.10):
- React-Core - React-Core
- RNCClipboard (1.11.1): - RNCClipboard (1.11.1):
@ -370,7 +368,6 @@ DEPENDENCIES:
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
@ -449,8 +446,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor" :path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
ReactCommon: ReactCommon:
:path: "../node_modules/react-native/ReactCommon" :path: "../node_modules/react-native/ReactCommon"
rn-fetch-blob:
:path: "../node_modules/rn-fetch-blob"
RNCAsyncStorage: RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage" :path: "../node_modules/@react-native-async-storage/async-storage"
RNCClipboard: RNCClipboard:
@ -502,7 +497,6 @@ SPEC CHECKSUMS:
React-RCTVibration: 79040b92bfa9c3c2d2cb4f57e981164ec7ab9374 React-RCTVibration: 79040b92bfa9c3c2d2cb4f57e981164ec7ab9374
React-runtimeexecutor: b960b687d2dfef0d3761fbb187e01812ebab8b23 React-runtimeexecutor: b960b687d2dfef0d3761fbb187e01812ebab8b23
ReactCommon: 095366164a276d91ea704ce53cb03825c487a3f2 ReactCommon: 095366164a276d91ea704ce53cb03825c487a3f2
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
RNGestureHandler: 28ad20bf02257791f7f137b31beef34b9549f54b RNGestureHandler: 28ad20bf02257791f7f137b31beef34b9549f54b

View File

@ -21,6 +21,7 @@
"@react-native-clipboard/clipboard": "^1.10.0", "@react-native-clipboard/clipboard": "^1.10.0",
"@zxing/text-encoding": "^0.9.0", "@zxing/text-encoding": "^0.9.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"email-validator": "^2.0.4",
"lodash.omit": "^4.5.0", "lodash.omit": "^4.5.0",
"mobx": "^6.6.1", "mobx": "^6.6.1",
"mobx-react-lite": "^3.4.0", "mobx-react-lite": "^3.4.0",

View File

@ -1,8 +1,11 @@
import {makeAutoObservable} from 'mobx' import {makeAutoObservable} from 'mobx'
import AdxApi from '../../third-party/api' import AdxApi from '../../third-party/api'
import type * as GetAccountsConfig from '../../third-party/api/src/types/todo/adx/getAccountsConfig'
import {isObj, hasProp} from '../lib/type-guards' import {isObj, hasProp} from '../lib/type-guards'
import {RootStoreModel} from './root-store' import {RootStoreModel} from './root-store'
export type ServiceDescription = GetAccountsConfig.OutputSchema
interface SessionData { interface SessionData {
service: string service: string
token: string token: string
@ -10,8 +13,17 @@ interface SessionData {
userdid: string userdid: string
} }
export enum OnboardingStage {
Init = 'init',
}
interface OnboardingState {
stage: OnboardingStage
}
export class SessionModel { export class SessionModel {
data: SessionData | null = null data: SessionData | null = null
onboardingState: OnboardingState | null = null
constructor(public rootStore: RootStoreModel) { constructor(public rootStore: RootStoreModel) {
makeAutoObservable(this, { makeAutoObservable(this, {
@ -26,31 +38,51 @@ export class SessionModel {
} }
serialize(): unknown { serialize(): unknown {
return this.data return {
data: this.data,
onboardingState: this.onboardingState,
}
} }
hydrate(v: unknown) { hydrate(v: unknown) {
if (isObj(v)) { if (isObj(v)) {
const data: SessionData = { if (hasProp(v, 'data') && isObj(v.data)) {
service: '', const data: SessionData = {
token: '', service: '',
username: '', token: '',
userdid: '', username: '',
userdid: '',
}
if (hasProp(v.data, 'service') && typeof v.data.service === 'string') {
data.service = v.data.service
}
if (hasProp(v.data, 'token') && typeof v.data.token === 'string') {
data.token = v.data.token
}
if (
hasProp(v.data, 'username') &&
typeof v.data.username === 'string'
) {
data.username = v.data.username
}
if (hasProp(v.data, 'userdid') && typeof v.data.userdid === 'string') {
data.userdid = v.data.userdid
}
if (data.service && data.token && data.username && data.userdid) {
this.data = data
}
} }
if (hasProp(v, 'service') && typeof v.service === 'string') { if (
data.service = v.service this.data &&
} hasProp(v, 'onboardingState') &&
if (hasProp(v, 'token') && typeof v.token === 'string') { isObj(v.onboardingState)
data.token = v.token ) {
} if (
if (hasProp(v, 'username') && typeof v.username === 'string') { hasProp(v.onboardingState, 'stage') &&
data.username = v.username typeof v.onboardingState === 'string'
} ) {
if (hasProp(v, 'userdid') && typeof v.userdid === 'string') { this.onboardingState = v.onboardingState
data.userdid = v.userdid }
}
if (data.service && data.token && data.username && data.userdid) {
this.data = data
} }
} }
} }
@ -100,6 +132,12 @@ export class SessionModel {
this.clear() // invalid session cached this.clear() // invalid session cached
} }
async describeService(service: string): Promise<ServiceDescription> {
const api = AdxApi.service(service)
const res = await api.todo.adx.getAccountsConfig({})
return res.data
}
async login({ async login({
service, service,
username, username,
@ -122,6 +160,36 @@ export class SessionModel {
} }
} }
async createAccount({
service,
email,
password,
username,
inviteCode,
}: {
service: string
email: string
password: string
username: string
inviteCode?: string
}) {
const api = AdxApi.service(service)
const res = await api.todo.adx.createAccount(
{},
{username, password, email, inviteCode},
)
if (res.data.jwt) {
this.setState({
service: service,
token: res.data.jwt,
username: res.data.name,
userdid: res.data.did,
})
this.setOnboardingStage(OnboardingStage.Init)
this.configureApi()
}
}
async logout() { async logout() {
if (this.isAuthed) { if (this.isAuthed) {
this.rootStore.api.todo.adx.deleteSession({}).catch((e: any) => { this.rootStore.api.todo.adx.deleteSession({}).catch((e: any) => {
@ -130,4 +198,12 @@ export class SessionModel {
} }
this.clear() this.clear()
} }
setOnboardingStage(stage: OnboardingStage | null) {
if (stage === null) {
this.onboardingState = null
} else {
this.onboardingState = {stage}
}
}
} }

View File

@ -10253,12 +10253,15 @@ var methodSchemas = [
encoding: "application/json", encoding: "application/json",
schema: { schema: {
type: "object", type: "object",
required: ["username", "did", "password"], required: ["email", "username", "password"],
properties: { properties: {
email: {
type: "string"
},
username: { username: {
type: "string" type: "string"
}, },
did: { inviteCode: {
type: "string" type: "string"
}, },
password: { password: {
@ -10271,10 +10274,16 @@ var methodSchemas = [
encoding: "application/json", encoding: "application/json",
schema: { schema: {
type: "object", type: "object",
required: ["jwt"], required: ["jwt", "name", "did"],
properties: { properties: {
jwt: { jwt: {
type: "string" type: "string"
},
name: {
type: "string"
},
did: {
type: "string"
} }
} }
} }
@ -10305,10 +10314,16 @@ var methodSchemas = [
encoding: "application/json", encoding: "application/json",
schema: { schema: {
type: "object", type: "object",
required: ["jwt"], required: ["jwt", "name", "did"],
properties: { properties: {
jwt: { jwt: {
type: "string" type: "string"
},
name: {
type: "string"
},
did: {
type: "string"
} }
} }
} }
@ -10359,16 +10374,37 @@ var methodSchemas = [
schema: {} schema: {}
} }
}, },
{
lexicon: 1,
id: "todo.adx.getAccountsConfig",
type: "query",
description: "Get a document describing the service's accounts configuration.",
parameters: {},
output: {
encoding: "application/json",
schema: {
type: "object",
required: ["availableUserDomains"],
properties: {
inviteCodeRequired: {
type: "boolean"
},
availableUserDomains: {
type: "array",
items: {
type: "string"
}
}
}
}
}
},
{ {
lexicon: 1, lexicon: 1,
id: "todo.adx.getSession", id: "todo.adx.getSession",
type: "query", type: "query",
description: "Get information about the current session.", description: "Get information about the current session.",
parameters: {}, parameters: {},
input: {
encoding: "",
schema: {}
},
output: { output: {
encoding: "application/json", encoding: "application/json",
schema: { schema: {
@ -11603,6 +11639,14 @@ var AdxNS = class {
getAccount(params, data, opts) { getAccount(params, data, opts) {
return this._service.xrpc.call("todo.adx.getAccount", params, data, opts); return this._service.xrpc.call("todo.adx.getAccount", params, data, opts);
} }
getAccountsConfig(params, data, opts) {
return this._service.xrpc.call(
"todo.adx.getAccountsConfig",
params,
data,
opts
);
}
getSession(params, data, opts) { getSession(params, data, opts) {
return this._service.xrpc.call("todo.adx.getSession", params, data, opts); return this._service.xrpc.call("todo.adx.getSession", params, data, opts);
} }

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,7 @@ import * as TodoAdxCreateSession from './types/todo/adx/createSession';
import * as TodoAdxDeleteAccount from './types/todo/adx/deleteAccount'; import * as TodoAdxDeleteAccount from './types/todo/adx/deleteAccount';
import * as TodoAdxDeleteSession from './types/todo/adx/deleteSession'; import * as TodoAdxDeleteSession from './types/todo/adx/deleteSession';
import * as TodoAdxGetAccount from './types/todo/adx/getAccount'; import * as TodoAdxGetAccount from './types/todo/adx/getAccount';
import * as TodoAdxGetAccountsConfig from './types/todo/adx/getAccountsConfig';
import * as TodoAdxGetSession from './types/todo/adx/getSession'; import * as TodoAdxGetSession from './types/todo/adx/getSession';
import * as TodoAdxRepoBatchWrite from './types/todo/adx/repoBatchWrite'; import * as TodoAdxRepoBatchWrite from './types/todo/adx/repoBatchWrite';
import * as TodoAdxRepoCreateRecord from './types/todo/adx/repoCreateRecord'; import * as TodoAdxRepoCreateRecord from './types/todo/adx/repoCreateRecord';
@ -59,6 +60,7 @@ export declare class AdxNS {
deleteAccount(params: TodoAdxDeleteAccount.QueryParams, data?: TodoAdxDeleteAccount.InputSchema, opts?: TodoAdxDeleteAccount.CallOptions): Promise<TodoAdxDeleteAccount.Response>; deleteAccount(params: TodoAdxDeleteAccount.QueryParams, data?: TodoAdxDeleteAccount.InputSchema, opts?: TodoAdxDeleteAccount.CallOptions): Promise<TodoAdxDeleteAccount.Response>;
deleteSession(params: TodoAdxDeleteSession.QueryParams, data?: TodoAdxDeleteSession.InputSchema, opts?: TodoAdxDeleteSession.CallOptions): Promise<TodoAdxDeleteSession.Response>; deleteSession(params: TodoAdxDeleteSession.QueryParams, data?: TodoAdxDeleteSession.InputSchema, opts?: TodoAdxDeleteSession.CallOptions): Promise<TodoAdxDeleteSession.Response>;
getAccount(params: TodoAdxGetAccount.QueryParams, data?: TodoAdxGetAccount.InputSchema, opts?: TodoAdxGetAccount.CallOptions): Promise<TodoAdxGetAccount.Response>; getAccount(params: TodoAdxGetAccount.QueryParams, data?: TodoAdxGetAccount.InputSchema, opts?: TodoAdxGetAccount.CallOptions): Promise<TodoAdxGetAccount.Response>;
getAccountsConfig(params: TodoAdxGetAccountsConfig.QueryParams, data?: TodoAdxGetAccountsConfig.InputSchema, opts?: TodoAdxGetAccountsConfig.CallOptions): Promise<TodoAdxGetAccountsConfig.Response>;
getSession(params: TodoAdxGetSession.QueryParams, data?: TodoAdxGetSession.InputSchema, opts?: TodoAdxGetSession.CallOptions): Promise<TodoAdxGetSession.Response>; getSession(params: TodoAdxGetSession.QueryParams, data?: TodoAdxGetSession.InputSchema, opts?: TodoAdxGetSession.CallOptions): Promise<TodoAdxGetSession.Response>;
repoBatchWrite(params: TodoAdxRepoBatchWrite.QueryParams, data?: TodoAdxRepoBatchWrite.InputSchema, opts?: TodoAdxRepoBatchWrite.CallOptions): Promise<TodoAdxRepoBatchWrite.Response>; repoBatchWrite(params: TodoAdxRepoBatchWrite.QueryParams, data?: TodoAdxRepoBatchWrite.InputSchema, opts?: TodoAdxRepoBatchWrite.CallOptions): Promise<TodoAdxRepoBatchWrite.Response>;
repoCreateRecord(params: TodoAdxRepoCreateRecord.QueryParams, data?: TodoAdxRepoCreateRecord.InputSchema, opts?: TodoAdxRepoCreateRecord.CallOptions): Promise<TodoAdxRepoCreateRecord.Response>; repoCreateRecord(params: TodoAdxRepoCreateRecord.QueryParams, data?: TodoAdxRepoCreateRecord.InputSchema, opts?: TodoAdxRepoCreateRecord.CallOptions): Promise<TodoAdxRepoCreateRecord.Response>;

View File

@ -6,12 +6,15 @@ export interface CallOptions {
encoding: 'application/json'; encoding: 'application/json';
} }
export interface InputSchema { export interface InputSchema {
email: string;
username: string; username: string;
did: string; inviteCode?: string;
password: string; password: string;
} }
export interface OutputSchema { export interface OutputSchema {
jwt: string; jwt: string;
name: string;
did: string;
} }
export interface Response { export interface Response {
success: boolean; success: boolean;

View File

@ -11,6 +11,8 @@ export interface InputSchema {
} }
export interface OutputSchema { export interface OutputSchema {
jwt: string; jwt: string;
name: string;
did: string;
} }
export interface Response { export interface Response {
success: boolean; success: boolean;

View File

@ -0,0 +1,17 @@
import { Headers } from '@adxp/xrpc';
export interface QueryParams {
}
export interface CallOptions {
headers?: Headers;
}
export declare type InputSchema = undefined;
export interface OutputSchema {
inviteCodeRequired?: boolean;
availableUserDomains: string[];
}
export interface Response {
success: boolean;
error: boolean;
headers: Headers;
data: OutputSchema;
}

View File

@ -3,11 +3,8 @@ export interface QueryParams {
} }
export interface CallOptions { export interface CallOptions {
headers?: Headers; headers?: Headers;
encoding: '';
}
export interface InputSchema {
[k: string]: unknown;
} }
export declare type InputSchema = undefined;
export interface OutputSchema { export interface OutputSchema {
name: string; name: string;
did: string; did: string;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,155 @@
import React, {useRef} from 'react'
import {
StyleProp,
StyleSheet,
Text,
TextStyle,
TouchableOpacity,
TouchableWithoutFeedback,
View,
ViewStyle,
} from 'react-native'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import RootSiblings from 'react-native-root-siblings'
import {colors} from '../../lib/styles'
interface PickerItem {
value: string
label: string
}
interface PickerOpts {
style?: StyleProp<ViewStyle>
labelStyle?: StyleProp<TextStyle>
iconStyle?: FontAwesomeIconStyle
items: PickerItem[]
value: string
onChange: (value: string) => void
enabled?: boolean
}
const MENU_WIDTH = 200
export function Picker({
style,
labelStyle,
iconStyle,
items,
value,
onChange,
enabled,
}: PickerOpts) {
const ref = useRef<View>(null)
const valueLabel = items.find(item => item.value === value)?.label || value
const onPress = () => {
if (!enabled) {
return
}
ref.current?.measure(
(
_x: number,
_y: number,
width: number,
height: number,
pageX: number,
pageY: number,
) => {
createDropdownMenu(pageX, pageY + height, MENU_WIDTH, items, onChange)
},
)
}
return (
<TouchableWithoutFeedback onPress={onPress}>
<View style={[styles.outer, style]} ref={ref}>
<View style={styles.label}>
<Text style={labelStyle}>{valueLabel}</Text>
</View>
<FontAwesomeIcon icon="angle-down" style={[styles.icon, iconStyle]} />
</View>
</TouchableWithoutFeedback>
)
}
function createDropdownMenu(
x: number,
y: number,
width: number,
items: PickerItem[],
onChange: (value: string) => void,
): RootSiblings {
const onPressItem = (index: number) => {
sibling.destroy()
onChange(items[index].value)
}
const onOuterPress = () => sibling.destroy()
const sibling = new RootSiblings(
(
<>
<TouchableWithoutFeedback onPress={onOuterPress}>
<View style={styles.bg} />
</TouchableWithoutFeedback>
<View style={[styles.menu, {left: x, top: y, width}]}>
{items.map((item, index) => (
<TouchableOpacity
key={index}
style={[styles.menuItem, index !== 0 && styles.menuItemBorder]}
onPress={() => onPressItem(index)}>
<Text style={styles.menuItemLabel}>{item.label}</Text>
</TouchableOpacity>
))}
</View>
</>
),
)
return sibling
}
const styles = StyleSheet.create({
outer: {
flexDirection: 'row',
alignItems: 'center',
},
label: {
marginRight: 5,
},
icon: {},
bg: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: '#000',
opacity: 0.1,
},
menu: {
position: 'absolute',
backgroundColor: '#fff',
borderRadius: 14,
opacity: 1,
paddingVertical: 6,
},
menuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 6,
paddingLeft: 15,
paddingRight: 30,
},
menuItemBorder: {
borderTopWidth: 1,
borderTopColor: colors.gray2,
marginTop: 4,
paddingTop: 12,
},
menuItemIcon: {
marginLeft: 6,
marginRight: 8,
},
menuItemLabel: {
fontSize: 15,
},
})

View File

@ -1,5 +1,6 @@
import {library} from '@fortawesome/fontawesome-svg-core' import {library} from '@fortawesome/fontawesome-svg-core'
import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown'
import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft' import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft'
import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight' import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft' import {faArrowLeft} from '@fortawesome/free-solid-svg-icons/faArrowLeft'
@ -7,6 +8,7 @@ import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons'
import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket' import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket'
import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare' import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
import {faArrowsRotate} from '@fortawesome/free-solid-svg-icons/faArrowsRotate' import {faArrowsRotate} from '@fortawesome/free-solid-svg-icons/faArrowsRotate'
import {faAt} from '@fortawesome/free-solid-svg-icons/faAt'
import {faBars} from '@fortawesome/free-solid-svg-icons/faBars' import {faBars} from '@fortawesome/free-solid-svg-icons/faBars'
import {faBell} from '@fortawesome/free-solid-svg-icons/faBell' import {faBell} from '@fortawesome/free-solid-svg-icons/faBell'
import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell' import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell'
@ -16,12 +18,15 @@ import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'
import {faClone} from '@fortawesome/free-regular-svg-icons/faClone' import {faClone} from '@fortawesome/free-regular-svg-icons/faClone'
import {faComment} from '@fortawesome/free-regular-svg-icons/faComment' import {faComment} from '@fortawesome/free-regular-svg-icons/faComment'
import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis' import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis'
import {faEnvelope} from '@fortawesome/free-solid-svg-icons/faEnvelope'
import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation' import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation'
import {faGear} from '@fortawesome/free-solid-svg-icons/faGear' import {faGear} from '@fortawesome/free-solid-svg-icons/faGear'
import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe'
import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart' import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart'
import {faHeart as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart' import {faHeart as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart'
import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse' import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse'
import {faLink} from '@fortawesome/free-solid-svg-icons/faLink' import {faLink} from '@fortawesome/free-solid-svg-icons/faLink'
import {faLock} from '@fortawesome/free-solid-svg-icons/faLock'
import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons/faMagnifyingGlass' import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons/faMagnifyingGlass'
import {faMessage} from '@fortawesome/free-regular-svg-icons/faMessage' import {faMessage} from '@fortawesome/free-regular-svg-icons/faMessage'
import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib' import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib'
@ -32,10 +37,12 @@ import {faShield} from '@fortawesome/free-solid-svg-icons/faShield'
import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet' import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet'
import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
import {faX} from '@fortawesome/free-solid-svg-icons/faX' import {faX} from '@fortawesome/free-solid-svg-icons/faX'
export function setup() { export function setup() {
library.add( library.add(
faAngleDown,
faAngleLeft, faAngleLeft,
faAngleRight, faAngleRight,
faArrowLeft, faArrowLeft,
@ -43,6 +50,7 @@ export function setup() {
faArrowUpFromBracket, faArrowUpFromBracket,
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faArrowsRotate, faArrowsRotate,
faAt,
faBars, faBars,
faBell, faBell,
farBell, farBell,
@ -52,12 +60,15 @@ export function setup() {
faClone, faClone,
faComment, faComment,
faEllipsis, faEllipsis,
faEnvelope,
faExclamation, faExclamation,
faGear, faGear,
faGlobe,
faHeart, faHeart,
fasHeart, fasHeart,
faHouse, faHouse,
faLink, faLink,
faLock,
faMagnifyingGlass, faMagnifyingGlass,
faMessage, faMessage,
faPenNib, faPenNib,
@ -68,6 +79,7 @@ export function setup() {
faShield, faShield,
faUser, faUser,
faUsers, faUsers,
faTicket,
faX, faX,
) )
} }

View File

@ -11,6 +11,7 @@ export const colors = {
gray4: '#968d8d', gray4: '#968d8d',
gray5: '#645454', gray5: '#645454',
blue0: '#bfe1ff',
blue1: '#8bc7fd', blue1: '#8bc7fd',
blue2: '#52acfe', blue2: '#52acfe',
blue3: '#0085ff', blue3: '#0085ff',

View File

@ -1,4 +1,4 @@
import React, {useState} from 'react' import React, {useState, useEffect} from 'react'
import { import {
ActivityIndicator, ActivityIndicator,
KeyboardAvoidingView, KeyboardAvoidingView,
@ -11,85 +11,69 @@ import {
} from 'react-native' } from 'react-native'
import Svg, {Circle, Line, Text as SvgText} from 'react-native-svg' import Svg, {Circle, Line, Text as SvgText} from 'react-native-svg'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import * as EmailValidator from 'email-validator'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {Picker} from '../com/util/Picker'
import {s, colors} from '../lib/styles' import {s, colors} from '../lib/styles'
import {useStores} from '../../state' import {useStores} from '../../state'
import {ServiceDescription} from '../../state/models/session'
enum ScreenState { enum ScreenState {
SigninOrCreateAccount, SigninOrCreateAccount,
Signin, Signin,
CreateAccount,
}
const Logo = () => {
return (
<View style={styles.logo}>
<Svg width="100" height="100">
<Circle
cx="50"
cy="50"
r="46"
fill="none"
stroke="white"
strokeWidth={2}
/>
<Line stroke="white" strokeWidth={1} x1="30" x2="30" y1="0" y2="100" />
<Line stroke="white" strokeWidth={1} x1="74" x2="74" y1="0" y2="100" />
<Line stroke="white" strokeWidth={1} x1="0" x2="100" y1="22" y2="22" />
<Line stroke="white" strokeWidth={1} x1="0" x2="100" y1="74" y2="74" />
<SvgText
fill="none"
stroke="white"
strokeWidth={2}
fontSize="60"
fontWeight="bold"
x="52"
y="70"
textAnchor="middle">
B
</SvgText>
</Svg>
</View>
)
} }
const SigninOrCreateAccount = ({ const SigninOrCreateAccount = ({
onPressSignin, onPressSignin,
onPressCreateAccount,
}: { }: {
onPressSignin: () => void onPressSignin: () => void
onPressCreateAccount: () => void
}) => { }) => {
const winDim = useWindowDimensions() const winDim = useWindowDimensions()
const halfWidth = winDim.width / 2 const halfWidth = winDim.width / 2
return ( return (
<> <>
<View style={styles.hero}> <View style={styles.hero}>
<View style={styles.logo}> <Logo />
<Svg width="100" height="100">
<Circle
cx="50"
cy="50"
r="46"
fill="none"
stroke="white"
strokeWidth={2}
/>
<Line
stroke="white"
strokeWidth={1}
x1="30"
x2="30"
y1="0"
y2="100"
/>
<Line
stroke="white"
strokeWidth={1}
x1="74"
x2="74"
y1="0"
y2="100"
/>
<Line
stroke="white"
strokeWidth={1}
x1="0"
x2="100"
y1="22"
y2="22"
/>
<Line
stroke="white"
strokeWidth={1}
x1="0"
x2="100"
y1="74"
y2="74"
/>
<SvgText
fill="none"
stroke="white"
strokeWidth={2}
fontSize="60"
fontWeight="bold"
x="52"
y="70"
textAnchor="middle">
B
</SvgText>
</Svg>
</View>
<Text style={styles.title}>Bluesky</Text> <Text style={styles.title}>Bluesky</Text>
<Text style={styles.subtitle}>[ private beta ]</Text> <Text style={styles.subtitle}>[ private beta ]</Text>
</View> </View>
<View style={s.flex1}> <View style={s.flex1}>
<TouchableOpacity style={styles.btn}> <TouchableOpacity style={styles.btn} onPress={onPressCreateAccount}>
<Text style={styles.btnLabel}>Create a new account</Text> <Text style={styles.btnLabel}>Create a new account</Text>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.or}> <View style={styles.or}>
@ -155,31 +139,221 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
return ( return (
<KeyboardAvoidingView behavior="padding" style={{flex: 1}}> <KeyboardAvoidingView behavior="padding" style={{flex: 1}}>
<View style={styles.smallHero}> <View style={styles.logoHero}>
<Text style={styles.title}>Bluesky</Text> <Logo />
<Text style={styles.subtitle}>[ private beta ]</Text>
</View> </View>
<View style={s.flex1}> <View style={styles.group}>
<View style={styles.group}> <View style={styles.groupTitle}>
<View style={styles.groupTitle}> <Text style={[s.white, s.f18, s.bold]}>Sign in</Text>
<Text style={[s.white, s.f18]}>Sign in</Text> </View>
{error ? (
<View style={styles.error}>
<View style={styles.errorIcon}>
<FontAwesomeIcon icon="exclamation" style={s.white} size={10} />
</View>
<View style={s.flex1}>
<Text style={[s.white, s.bold]}>{error}</Text>
</View>
</View> </View>
<View style={styles.groupContent}> ) : undefined}
<View style={[s.mb5]}> <View style={styles.groupContent}>
<FontAwesomeIcon icon="envelope" style={styles.groupContentIcon} />
<TextInput
style={styles.textInput}
placeholder="Email or username"
placeholderTextColor={colors.blue0}
autoCapitalize="none"
autoFocus
value={username}
onChangeText={setUsername}
editable={!isProcessing}
/>
</View>
<View style={styles.groupContent}>
<FontAwesomeIcon icon="lock" style={styles.groupContentIcon} />
<TextInput
style={styles.textInput}
placeholder="Password"
placeholderTextColor={colors.blue0}
autoCapitalize="none"
secureTextEntry
value={password}
onChangeText={setPassword}
editable={!isProcessing}
/>
</View>
</View>
<View style={[s.flexRow, s.pl20, s.pr20]}>
<TouchableOpacity onPress={onPressBack}>
<Text style={[s.white, s.f18, s.pl5]}>Back</Text>
</TouchableOpacity>
<View style={s.flex1} />
<TouchableOpacity onPress={onPressNext}>
{isProcessing ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text>
)}
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
)
}
const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
const store = useStores()
const [isProcessing, setIsProcessing] = useState<boolean>(false)
const [error, setError] = useState<string>('')
const [serviceDescription, setServiceDescription] = useState<
ServiceDescription | undefined
>(undefined)
const [userDomain, setUserDomain] = useState<string>('')
const [inviteCode, setInviteCode] = useState<string>('')
const [email, setEmail] = useState<string>('')
const [password, setPassword] = useState<string>('')
const [username, setUsername] = useState<string>('')
useEffect(() => {
if (serviceDescription || error) {
return
}
store.session.describeService('http://localhost:2583/').then(
desc => {
setServiceDescription(desc)
setUserDomain(desc.availableUserDomains[0])
},
err => {
console.error(err)
setError(
'Unable to contact your service. Please check your Internet connection.',
)
},
)
}, [])
const onPressNext = async () => {
if (!email) {
return setError('Please enter your email.')
}
if (!EmailValidator.validate(email)) {
return setError('Your email appears to be invalid.')
}
if (!password) {
return setError('Please choose your password.')
}
if (!username) {
return setError('Please choose your username.')
}
setError('')
setIsProcessing(true)
try {
await store.session.createAccount({
service: 'http://localhost:2583/',
email,
username: `${username}.${userDomain}`,
password,
inviteCode,
})
} catch (e: any) {
const errMsg = e.toString()
console.log(e)
setIsProcessing(false)
// if (errMsg.includes('Authentication Required')) {
// setError('Invalid username or password')
// } else if (errMsg.includes('Network request failed')) {
// setError(
// 'Unable to contact your service. Please check your Internet connection.',
// )
// } else {
setError(errMsg.replace(/^Error:/, ''))
// }
}
}
const InitialLoadView = () => (
<>
{error ? (
<>
<View style={[styles.error, styles.errorFloating]}>
<View style={styles.errorIcon}>
<FontAwesomeIcon icon="exclamation" style={s.white} size={10} />
</View>
<View style={s.flex1}>
<Text style={[s.white, s.bold]}>{error}</Text>
</View>
</View>
<View style={[s.flexRow, s.pl20, s.pr20]}>
<TouchableOpacity onPress={onPressBack}>
<Text style={[s.white, s.f18, s.pl5]}>Back</Text>
</TouchableOpacity>
</View>
</>
) : (
<ActivityIndicator color="#fff" />
)}
</>
)
return (
<KeyboardAvoidingView behavior="padding" style={{flex: 1}}>
<View style={styles.logoHero}>
<Logo />
</View>
{serviceDescription ? (
<>
{error ? (
<View style={[styles.error, styles.errorFloating]}>
<View style={styles.errorIcon}>
<FontAwesomeIcon icon="exclamation" style={s.white} size={10} />
</View>
<View style={s.flex1}>
<Text style={[s.white, s.bold]}>{error}</Text>
</View>
</View>
) : undefined}
<View style={styles.group}>
<View style={styles.groupTitle}>
<Text style={[s.white, s.f18, s.bold]}>Create a new account</Text>
</View>
{serviceDescription?.inviteCodeRequired ? (
<View style={styles.groupContent}>
<FontAwesomeIcon
icon="ticket"
style={styles.groupContentIcon}
/>
<TextInput
style={[styles.textInput]}
placeholder="Invite code"
placeholderTextColor={colors.blue0}
autoCapitalize="none"
autoFocus
value={inviteCode}
onChangeText={setInviteCode}
editable={!isProcessing}
/>
</View>
) : undefined}
<View style={styles.groupContent}>
<FontAwesomeIcon
icon="envelope"
style={styles.groupContentIcon}
/>
<TextInput <TextInput
style={styles.textInput} style={[styles.textInput]}
placeholder="Email or username" placeholder="Email address"
placeholderTextColor={colors.blue0}
autoCapitalize="none" autoCapitalize="none"
autoFocus value={email}
value={username} onChangeText={setEmail}
onChangeText={setUsername}
editable={!isProcessing} editable={!isProcessing}
/> />
</View> </View>
<View style={[s.mb5]}> <View style={styles.groupContent}>
<FontAwesomeIcon icon="lock" style={styles.groupContentIcon} />
<TextInput <TextInput
style={styles.textInput} style={[styles.textInput]}
placeholder="Password" placeholder="Choose your password"
placeholderTextColor={colors.blue0}
autoCapitalize="none" autoCapitalize="none"
secureTextEntry secureTextEntry
value={password} value={password}
@ -187,54 +361,94 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
editable={!isProcessing} editable={!isProcessing}
/> />
</View> </View>
{error ? (
<View style={styles.error}>
<View style={styles.errorIcon}>
<FontAwesomeIcon
icon="exclamation"
style={s.white}
size={10}
/>
</View>
<View style={s.flex1}>
<Text style={[s.white, s.bold]}>{error}</Text>
</View>
</View>
) : undefined}
</View> </View>
</View> <View style={styles.group}>
<View style={[s.flexRow, s.pl20, s.pr20]}> <View style={styles.groupTitle}>
<TouchableOpacity onPress={onPressBack}> <Text style={[s.white, s.f18, s.bold]}>Choose your username</Text>
<Text style={[s.white, s.f18, s.bold, s.pl5]}>Back</Text> </View>
</TouchableOpacity> <View style={styles.groupContent}>
<View style={s.flex1} /> <FontAwesomeIcon icon="at" style={styles.groupContentIcon} />
<TouchableOpacity onPress={onPressNext}> <TextInput
{isProcessing ? ( style={[styles.textInput]}
<ActivityIndicator color="#fff" /> placeholder="eg alice"
) : ( placeholderTextColor={colors.blue0}
<Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text> autoCapitalize="none"
value={username}
onChangeText={v => setUsername(cleanUsername(v))}
editable={!isProcessing}
/>
</View>
{serviceDescription.availableUserDomains.length > 1 && (
<View style={styles.groupContent}>
<FontAwesomeIcon icon="globe" style={styles.groupContentIcon} />
<Picker
style={styles.picker}
labelStyle={styles.pickerLabel}
iconStyle={styles.pickerIcon}
value={userDomain}
items={serviceDescription.availableUserDomains.map(d => ({
label: `.${d}`,
value: d,
}))}
onChange={itemValue => setUserDomain(itemValue)}
enabled={!isProcessing}
/>
</View>
)} )}
</TouchableOpacity> <View style={styles.groupContent}>
</View> <Text style={[s.white, s.p10]}>
</View> Your full username will be{' '}
<Text style={s.bold}>
@{username}.{userDomain}
</Text>
</Text>
</View>
</View>
<View style={[s.flexRow, s.pl20, s.pr20]}>
<TouchableOpacity onPress={onPressBack}>
<Text style={[s.white, s.f18, s.pl5]}>Back</Text>
</TouchableOpacity>
<View style={s.flex1} />
<TouchableOpacity onPress={onPressNext}>
{isProcessing ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text>
)}
</TouchableOpacity>
</View>
</>
) : (
<InitialLoadView />
)}
</KeyboardAvoidingView> </KeyboardAvoidingView>
) )
} }
function cleanUsername(v: string): string {
v = v.trim()
if (v.length > 63) {
v = v.slice(0, 63)
}
return v.toLowerCase().replace(/[^a-z0-9-]/g, '')
}
export const Login = observer( export const Login = observer(
(/*{navigation}: RootTabsScreenProps<'Login'>*/) => { (/*{navigation}: RootTabsScreenProps<'Login'>*/) => {
// const store = useStores() // const store = useStores()
const [screenState, setScreenState] = useState<ScreenState>( const [screenState, setScreenState] = useState<ScreenState>(
ScreenState.SigninOrCreateAccount, ScreenState.SigninOrCreateAccount,
) )
const onPressSignin = () => {
setScreenState(ScreenState.Signin)
}
return ( return (
<View style={styles.outer}> <View style={styles.outer}>
{screenState === ScreenState.SigninOrCreateAccount ? ( {screenState === ScreenState.SigninOrCreateAccount ? (
<SigninOrCreateAccount onPressSignin={onPressSignin} /> <SigninOrCreateAccount
onPressSignin={() => setScreenState(ScreenState.Signin)}
onPressCreateAccount={() =>
setScreenState(ScreenState.CreateAccount)
}
/>
) : undefined} ) : undefined}
{screenState === ScreenState.Signin ? ( {screenState === ScreenState.Signin ? (
<Signin <Signin
@ -243,6 +457,13 @@ export const Login = observer(
} }
/> />
) : undefined} ) : undefined}
{screenState === ScreenState.CreateAccount ? (
<CreateAccount
onPressBack={() =>
setScreenState(ScreenState.SigninOrCreateAccount)
}
/>
) : undefined}
</View> </View>
) )
}, },
@ -256,9 +477,9 @@ const styles = StyleSheet.create({
flex: 2, flex: 2,
justifyContent: 'center', justifyContent: 'center',
}, },
smallHero: { logoHero: {
flex: 1, paddingTop: 30,
justifyContent: 'center', paddingBottom: 40,
}, },
logo: { logo: {
flexDirection: 'row', flexDirection: 'row',
@ -282,6 +503,7 @@ const styles = StyleSheet.create({
paddingVertical: 16, paddingVertical: 16,
marginBottom: 20, marginBottom: 20,
marginHorizontal: 20, marginHorizontal: 20,
backgroundColor: colors.blue3,
}, },
btnLabel: { btnLabel: {
textAlign: 'center', textAlign: 'center',
@ -307,33 +529,65 @@ const styles = StyleSheet.create({
borderRadius: 10, borderRadius: 10,
marginBottom: 20, marginBottom: 20,
marginHorizontal: 20, marginHorizontal: 20,
backgroundColor: colors.blue3,
}, },
groupTitle: { groupTitle: {
paddingVertical: 8, paddingVertical: 8,
paddingHorizontal: 12, paddingHorizontal: 12,
borderBottomWidth: 1,
borderBottomColor: colors.blue1,
}, },
groupContent: { groupContent: {
paddingVertical: 8, borderTopWidth: 1,
paddingHorizontal: 12, borderTopColor: colors.blue1,
flexDirection: 'row',
alignItems: 'center',
},
groupContentIcon: {
color: 'white',
marginLeft: 10,
}, },
textInput: { textInput: {
flex: 1,
width: '100%', width: '100%',
backgroundColor: colors.white, backgroundColor: colors.blue3,
paddingHorizontal: 8, color: colors.white,
paddingVertical: 8, paddingVertical: 10,
borderRadius: 4, paddingHorizontal: 12,
fontSize: 18,
borderRadius: 10,
},
picker: {
flex: 1,
width: '100%',
backgroundColor: colors.blue3,
color: colors.white,
paddingVertical: 10,
paddingHorizontal: 12,
fontSize: 18,
borderRadius: 10,
},
pickerLabel: {
color: colors.white,
fontSize: 18, fontSize: 18,
}, },
pickerIcon: {
color: colors.white,
},
error: { error: {
borderTopWidth: 1,
borderTopColor: colors.blue1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginTop: 5, marginTop: 5,
backgroundColor: colors.purple3, backgroundColor: colors.blue2,
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 5, paddingVertical: 5,
borderRadius: 4, },
errorFloating: {
borderWidth: 1,
borderColor: colors.blue1,
marginBottom: 20,
marginHorizontal: 20,
borderRadius: 8,
}, },
errorIcon: { errorIcon: {
borderWidth: 1, borderWidth: 1,

View File

@ -12,7 +12,6 @@ import {
} from 'react-native' } from 'react-native'
import {ScreenContainer, Screen} from 'react-native-screens' import {ScreenContainer, Screen} from 'react-native-screens'
import LinearGradient from 'react-native-linear-gradient' import LinearGradient from 'react-native-linear-gradient'
// import Svg, {Polygon} from 'react-native-svg'
import {GestureDetector, Gesture} from 'react-native-gesture-handler' import {GestureDetector, Gesture} from 'react-native-gesture-handler'
import Animated, { import Animated, {
useSharedValue, useSharedValue,
@ -33,7 +32,7 @@ import {LocationNavigator} from './location-navigator'
import {createBackMenu, createForwardMenu} from './history-menu' import {createBackMenu, createForwardMenu} from './history-menu'
import {createAccountsMenu} from './accounts-menu' import {createAccountsMenu} from './accounts-menu'
import {createLocationMenu} from './location-menu' import {createLocationMenu} from './location-menu'
import {s, colors, gradients} from '../../lib/styles' import {s, colors} from '../../lib/styles'
import {AVIS} from '../../lib/assets' import {AVIS} from '../../lib/assets'
const locationIconNeedsNudgeUp = (icon: IconProp) => icon === 'house' const locationIconNeedsNudgeUp = (icon: IconProp) => icon === 'house'
@ -174,40 +173,8 @@ export const MobileShell: React.FC = observer(() => {
<LinearGradient <LinearGradient
colors={['#007CFF', '#00BCFF']} colors={['#007CFF', '#00BCFF']}
start={{x: 0, y: 0.8}} start={{x: 0, y: 0.8}}
end={{x: 1, y: 1}} end={{x: 0, y: 1}}
style={styles.outerContainer}> style={styles.outerContainer}>
{
undefined /* TODO want this? <Svg height={winDim.height} width={winDim.width} style={s.absolute}>
<Polygon
points={`
${winDim.width},0
${winDim.width - 250},0
0,${winDim.height - 140}
0,${winDim.height}
${winDim.width},${winDim.height}`}
fill="#fff"
fillOpacity="0.04"
/>
<Polygon
points={`
${winDim.width},0
${winDim.width - 100},0
0,${winDim.height - 60}
0,${winDim.height}
${winDim.width},${winDim.height}`}
fill="#fff"
fillOpacity="0.04"
/>
<Polygon
points={`
${winDim.width},100
0,${winDim.height}
${winDim.width},${winDim.height}`}
fill="#fff"
fillOpacity="0.04"
/>
</Svg>*/
}
<SafeAreaView style={styles.innerContainer}> <SafeAreaView style={styles.innerContainer}>
<Login /> <Login />
</SafeAreaView> </SafeAreaView>

View File

@ -2195,54 +2195,6 @@
dependencies: dependencies:
"@sinonjs/commons" "^1.7.0" "@sinonjs/commons" "^1.7.0"
"@stablelib/binary@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f"
integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==
dependencies:
"@stablelib/int" "^1.0.1"
"@stablelib/ed25519@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@stablelib/ed25519/-/ed25519-1.0.3.tgz#f8fdeb6f77114897c887bb6a3138d659d3f35996"
integrity sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==
dependencies:
"@stablelib/random" "^1.0.2"
"@stablelib/sha512" "^1.0.1"
"@stablelib/wipe" "^1.0.1"
"@stablelib/hash@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/hash/-/hash-1.0.1.tgz#3c944403ff2239fad8ebb9015e33e98444058bc5"
integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==
"@stablelib/int@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008"
integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==
"@stablelib/random@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c"
integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==
dependencies:
"@stablelib/binary" "^1.0.1"
"@stablelib/wipe" "^1.0.1"
"@stablelib/sha512@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/sha512/-/sha512-1.0.1.tgz#6da700c901c2c0ceacbd3ae122a38ac57c72145f"
integrity sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==
dependencies:
"@stablelib/binary" "^1.0.1"
"@stablelib/hash" "^1.0.1"
"@stablelib/wipe" "^1.0.1"
"@stablelib/wipe@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36"
integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==
"@surma/rollup-plugin-off-main-thread@^2.2.3": "@surma/rollup-plugin-off-main-thread@^2.2.3":
version "2.2.3" version "2.2.3"
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053" resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
@ -4947,6 +4899,11 @@ electron-to-chromium@^1.4.251:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.257.tgz#895dc73c6bb58d1235dc80879ecbca0bcba96e2c" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.257.tgz#895dc73c6bb58d1235dc80879ecbca0bcba96e2c"
integrity sha512-C65sIwHqNnPC2ADMfse/jWTtmhZMII+x6ADI9gENzrOiI7BpxmfKFE84WkIEl5wEg+7+SfIkwChDlsd1Erju2A== integrity sha512-C65sIwHqNnPC2ADMfse/jWTtmhZMII+x6ADI9gENzrOiI7BpxmfKFE84WkIEl5wEg+7+SfIkwChDlsd1Erju2A==
email-validator@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed"
integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==
emittery@^0.10.2: emittery@^0.10.2:
version "0.10.2" version "0.10.2"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933"
@ -8722,11 +8679,6 @@ multicast-dns@^7.2.5:
dns-packet "^5.2.2" dns-packet "^5.2.2"
thunky "^1.0.2" thunky "^1.0.2"
multiformats@^9.4.2:
version "9.9.0"
resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37"
integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==
nanoid@^3.3.1, nanoid@^3.3.4: nanoid@^3.3.1, nanoid@^3.3.4:
version "3.3.4" version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
@ -9037,11 +8989,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies: dependencies:
wrappy "1" wrappy "1"
one-webcrypto@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/one-webcrypto/-/one-webcrypto-1.0.3.tgz#f951243cde29b79b6745ad14966fc598a609997c"
integrity sha512-fu9ywBVBPx0gS9K0etIROTiCkvI5S1TDjFsYFb3rC1ewFxeOqsbzq7aIMBHsYfrTHBcGXJaONXXjTl8B01cW1Q==
onetime@^2.0.0: onetime@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
@ -10930,7 +10877,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7: semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
version "7.3.7" version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@ -11904,16 +11851,6 @@ ua-parser-js@^0.7.30:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
ucans@0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/ucans/-/ucans-0.9.1.tgz#d4ed0ed61d11ef13925512d365b26c5c9451b852"
integrity sha512-Vr2z5cy3YcPDhK9RY5VOfoqrXEml3GmZCGovFhLOIVji5PPiR/pkA2ME9jGWqLBQ1mj3494aBjxcMu4DreaAcg==
dependencies:
"@stablelib/ed25519" "^1.0.2"
one-webcrypto "^1.0.3"
semver "^7.3.6"
uint8arrays "^3.0.0"
uglify-es@^3.1.9: uglify-es@^3.1.9:
version "3.3.9" version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
@ -11922,13 +11859,6 @@ uglify-es@^3.1.9:
commander "~2.13.0" commander "~2.13.0"
source-map "~0.6.1" source-map "~0.6.1"
uint8arrays@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.0.tgz#8186b8eafce68f28bd29bd29d683a311778901e2"
integrity sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==
dependencies:
multiformats "^9.4.2"
unbox-primitive@^1.0.2: unbox-primitive@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"