Add account creation
parent
c89ec94b17
commit
ef4b9cf8d9
|
@ -291,8 +291,6 @@ PODS:
|
|||
- React-jsi (= 0.68.2)
|
||||
- React-logger (= 0.68.2)
|
||||
- React-perflogger (= 0.68.2)
|
||||
- rn-fetch-blob (0.12.0):
|
||||
- React-Core
|
||||
- RNCAsyncStorage (1.17.10):
|
||||
- React-Core
|
||||
- RNCClipboard (1.11.1):
|
||||
|
@ -370,7 +368,6 @@ DEPENDENCIES:
|
|||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`)
|
||||
- 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`)"
|
||||
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
|
@ -449,8 +446,6 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native/ReactCommon/runtimeexecutor"
|
||||
ReactCommon:
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
rn-fetch-blob:
|
||||
:path: "../node_modules/rn-fetch-blob"
|
||||
RNCAsyncStorage:
|
||||
:path: "../node_modules/@react-native-async-storage/async-storage"
|
||||
RNCClipboard:
|
||||
|
@ -502,7 +497,6 @@ SPEC CHECKSUMS:
|
|||
React-RCTVibration: 79040b92bfa9c3c2d2cb4f57e981164ec7ab9374
|
||||
React-runtimeexecutor: b960b687d2dfef0d3761fbb187e01812ebab8b23
|
||||
ReactCommon: 095366164a276d91ea704ce53cb03825c487a3f2
|
||||
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
|
||||
RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca
|
||||
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
|
||||
RNGestureHandler: 28ad20bf02257791f7f137b31beef34b9549f54b
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"@react-native-clipboard/clipboard": "^1.10.0",
|
||||
"@zxing/text-encoding": "^0.9.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"email-validator": "^2.0.4",
|
||||
"lodash.omit": "^4.5.0",
|
||||
"mobx": "^6.6.1",
|
||||
"mobx-react-lite": "^3.4.0",
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import {makeAutoObservable} from 'mobx'
|
||||
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 {RootStoreModel} from './root-store'
|
||||
|
||||
export type ServiceDescription = GetAccountsConfig.OutputSchema
|
||||
|
||||
interface SessionData {
|
||||
service: string
|
||||
token: string
|
||||
|
@ -10,8 +13,17 @@ interface SessionData {
|
|||
userdid: string
|
||||
}
|
||||
|
||||
export enum OnboardingStage {
|
||||
Init = 'init',
|
||||
}
|
||||
|
||||
interface OnboardingState {
|
||||
stage: OnboardingStage
|
||||
}
|
||||
|
||||
export class SessionModel {
|
||||
data: SessionData | null = null
|
||||
onboardingState: OnboardingState | null = null
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {
|
||||
makeAutoObservable(this, {
|
||||
|
@ -26,31 +38,51 @@ export class SessionModel {
|
|||
}
|
||||
|
||||
serialize(): unknown {
|
||||
return this.data
|
||||
return {
|
||||
data: this.data,
|
||||
onboardingState: this.onboardingState,
|
||||
}
|
||||
}
|
||||
|
||||
hydrate(v: unknown) {
|
||||
if (isObj(v)) {
|
||||
const data: SessionData = {
|
||||
service: '',
|
||||
token: '',
|
||||
username: '',
|
||||
userdid: '',
|
||||
if (hasProp(v, 'data') && isObj(v.data)) {
|
||||
const data: SessionData = {
|
||||
service: '',
|
||||
token: '',
|
||||
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') {
|
||||
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
|
||||
if (
|
||||
this.data &&
|
||||
hasProp(v, 'onboardingState') &&
|
||||
isObj(v.onboardingState)
|
||||
) {
|
||||
if (
|
||||
hasProp(v.onboardingState, 'stage') &&
|
||||
typeof v.onboardingState === 'string'
|
||||
) {
|
||||
this.onboardingState = v.onboardingState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +132,12 @@ export class SessionModel {
|
|||
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({
|
||||
service,
|
||||
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() {
|
||||
if (this.isAuthed) {
|
||||
this.rootStore.api.todo.adx.deleteSession({}).catch((e: any) => {
|
||||
|
@ -130,4 +198,12 @@ export class SessionModel {
|
|||
}
|
||||
this.clear()
|
||||
}
|
||||
|
||||
setOnboardingStage(stage: OnboardingStage | null) {
|
||||
if (stage === null) {
|
||||
this.onboardingState = null
|
||||
} else {
|
||||
this.onboardingState = {stage}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10253,12 +10253,15 @@ var methodSchemas = [
|
|||
encoding: "application/json",
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["username", "did", "password"],
|
||||
required: ["email", "username", "password"],
|
||||
properties: {
|
||||
email: {
|
||||
type: "string"
|
||||
},
|
||||
username: {
|
||||
type: "string"
|
||||
},
|
||||
did: {
|
||||
inviteCode: {
|
||||
type: "string"
|
||||
},
|
||||
password: {
|
||||
|
@ -10271,10 +10274,16 @@ var methodSchemas = [
|
|||
encoding: "application/json",
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["jwt"],
|
||||
required: ["jwt", "name", "did"],
|
||||
properties: {
|
||||
jwt: {
|
||||
type: "string"
|
||||
},
|
||||
name: {
|
||||
type: "string"
|
||||
},
|
||||
did: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10305,10 +10314,16 @@ var methodSchemas = [
|
|||
encoding: "application/json",
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["jwt"],
|
||||
required: ["jwt", "name", "did"],
|
||||
properties: {
|
||||
jwt: {
|
||||
type: "string"
|
||||
},
|
||||
name: {
|
||||
type: "string"
|
||||
},
|
||||
did: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10359,16 +10374,37 @@ var methodSchemas = [
|
|||
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,
|
||||
id: "todo.adx.getSession",
|
||||
type: "query",
|
||||
description: "Get information about the current session.",
|
||||
parameters: {},
|
||||
input: {
|
||||
encoding: "",
|
||||
schema: {}
|
||||
},
|
||||
output: {
|
||||
encoding: "application/json",
|
||||
schema: {
|
||||
|
@ -11603,6 +11639,14 @@ var AdxNS = class {
|
|||
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) {
|
||||
return this._service.xrpc.call("todo.adx.getSession", params, data, opts);
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,6 +4,7 @@ import * as TodoAdxCreateSession from './types/todo/adx/createSession';
|
|||
import * as TodoAdxDeleteAccount from './types/todo/adx/deleteAccount';
|
||||
import * as TodoAdxDeleteSession from './types/todo/adx/deleteSession';
|
||||
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 TodoAdxRepoBatchWrite from './types/todo/adx/repoBatchWrite';
|
||||
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>;
|
||||
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>;
|
||||
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>;
|
||||
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>;
|
||||
|
|
|
@ -6,12 +6,15 @@ export interface CallOptions {
|
|||
encoding: 'application/json';
|
||||
}
|
||||
export interface InputSchema {
|
||||
email: string;
|
||||
username: string;
|
||||
did: string;
|
||||
inviteCode?: string;
|
||||
password: string;
|
||||
}
|
||||
export interface OutputSchema {
|
||||
jwt: string;
|
||||
name: string;
|
||||
did: string;
|
||||
}
|
||||
export interface Response {
|
||||
success: boolean;
|
||||
|
|
|
@ -11,6 +11,8 @@ export interface InputSchema {
|
|||
}
|
||||
export interface OutputSchema {
|
||||
jwt: string;
|
||||
name: string;
|
||||
did: string;
|
||||
}
|
||||
export interface Response {
|
||||
success: boolean;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -3,11 +3,8 @@ export interface QueryParams {
|
|||
}
|
||||
export interface CallOptions {
|
||||
headers?: Headers;
|
||||
encoding: '';
|
||||
}
|
||||
export interface InputSchema {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export declare type InputSchema = undefined;
|
||||
export interface OutputSchema {
|
||||
name: string;
|
||||
did: string;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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,
|
||||
},
|
||||
})
|
|
@ -1,5 +1,6 @@
|
|||
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 {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
|
||||
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 {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
|
||||
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 {faBell} from '@fortawesome/free-solid-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 {faComment} from '@fortawesome/free-regular-svg-icons/faComment'
|
||||
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 {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 as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart'
|
||||
import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse'
|
||||
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 {faMessage} from '@fortawesome/free-regular-svg-icons/faMessage'
|
||||
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 {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
|
||||
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'
|
||||
|
||||
export function setup() {
|
||||
library.add(
|
||||
faAngleDown,
|
||||
faAngleLeft,
|
||||
faAngleRight,
|
||||
faArrowLeft,
|
||||
|
@ -43,6 +50,7 @@ export function setup() {
|
|||
faArrowUpFromBracket,
|
||||
faArrowUpRightFromSquare,
|
||||
faArrowsRotate,
|
||||
faAt,
|
||||
faBars,
|
||||
faBell,
|
||||
farBell,
|
||||
|
@ -52,12 +60,15 @@ export function setup() {
|
|||
faClone,
|
||||
faComment,
|
||||
faEllipsis,
|
||||
faEnvelope,
|
||||
faExclamation,
|
||||
faGear,
|
||||
faGlobe,
|
||||
faHeart,
|
||||
fasHeart,
|
||||
faHouse,
|
||||
faLink,
|
||||
faLock,
|
||||
faMagnifyingGlass,
|
||||
faMessage,
|
||||
faPenNib,
|
||||
|
@ -68,6 +79,7 @@ export function setup() {
|
|||
faShield,
|
||||
faUser,
|
||||
faUsers,
|
||||
faTicket,
|
||||
faX,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export const colors = {
|
|||
gray4: '#968d8d',
|
||||
gray5: '#645454',
|
||||
|
||||
blue0: '#bfe1ff',
|
||||
blue1: '#8bc7fd',
|
||||
blue2: '#52acfe',
|
||||
blue3: '#0085ff',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useState} from 'react'
|
||||
import React, {useState, useEffect} from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
KeyboardAvoidingView,
|
||||
|
@ -11,85 +11,69 @@ import {
|
|||
} from 'react-native'
|
||||
import Svg, {Circle, Line, Text as SvgText} from 'react-native-svg'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import * as EmailValidator from 'email-validator'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Picker} from '../com/util/Picker'
|
||||
import {s, colors} from '../lib/styles'
|
||||
import {useStores} from '../../state'
|
||||
import {ServiceDescription} from '../../state/models/session'
|
||||
|
||||
enum ScreenState {
|
||||
SigninOrCreateAccount,
|
||||
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 = ({
|
||||
onPressSignin,
|
||||
onPressCreateAccount,
|
||||
}: {
|
||||
onPressSignin: () => void
|
||||
onPressCreateAccount: () => void
|
||||
}) => {
|
||||
const winDim = useWindowDimensions()
|
||||
const halfWidth = winDim.width / 2
|
||||
return (
|
||||
<>
|
||||
<View style={styles.hero}>
|
||||
<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>
|
||||
<Logo />
|
||||
<Text style={styles.title}>Bluesky</Text>
|
||||
<Text style={styles.subtitle}>[ private beta ]</Text>
|
||||
</View>
|
||||
<View style={s.flex1}>
|
||||
<TouchableOpacity style={styles.btn}>
|
||||
<TouchableOpacity style={styles.btn} onPress={onPressCreateAccount}>
|
||||
<Text style={styles.btnLabel}>Create a new account</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.or}>
|
||||
|
@ -155,31 +139,221 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
|
|||
|
||||
return (
|
||||
<KeyboardAvoidingView behavior="padding" style={{flex: 1}}>
|
||||
<View style={styles.smallHero}>
|
||||
<Text style={styles.title}>Bluesky</Text>
|
||||
<Text style={styles.subtitle}>[ private beta ]</Text>
|
||||
<View style={styles.logoHero}>
|
||||
<Logo />
|
||||
</View>
|
||||
<View style={s.flex1}>
|
||||
<View style={styles.group}>
|
||||
<View style={styles.groupTitle}>
|
||||
<Text style={[s.white, s.f18]}>Sign in</Text>
|
||||
<View style={styles.group}>
|
||||
<View style={styles.groupTitle}>
|
||||
<Text style={[s.white, s.f18, s.bold]}>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 style={styles.groupContent}>
|
||||
<View style={[s.mb5]}>
|
||||
) : undefined}
|
||||
<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
|
||||
style={styles.textInput}
|
||||
placeholder="Email or username"
|
||||
style={[styles.textInput]}
|
||||
placeholder="Email address"
|
||||
placeholderTextColor={colors.blue0}
|
||||
autoCapitalize="none"
|
||||
autoFocus
|
||||
value={username}
|
||||
onChangeText={setUsername}
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
editable={!isProcessing}
|
||||
/>
|
||||
</View>
|
||||
<View style={[s.mb5]}>
|
||||
<View style={styles.groupContent}>
|
||||
<FontAwesomeIcon icon="lock" style={styles.groupContentIcon} />
|
||||
<TextInput
|
||||
style={styles.textInput}
|
||||
placeholder="Password"
|
||||
style={[styles.textInput]}
|
||||
placeholder="Choose your password"
|
||||
placeholderTextColor={colors.blue0}
|
||||
autoCapitalize="none"
|
||||
secureTextEntry
|
||||
value={password}
|
||||
|
@ -187,54 +361,94 @@ const Signin = ({onPressBack}: {onPressBack: () => void}) => {
|
|||
editable={!isProcessing}
|
||||
/>
|
||||
</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 style={[s.flexRow, s.pl20, s.pr20]}>
|
||||
<TouchableOpacity onPress={onPressBack}>
|
||||
<Text style={[s.white, s.f18, s.bold, 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>
|
||||
<View style={styles.group}>
|
||||
<View style={styles.groupTitle}>
|
||||
<Text style={[s.white, s.f18, s.bold]}>Choose your username</Text>
|
||||
</View>
|
||||
<View style={styles.groupContent}>
|
||||
<FontAwesomeIcon icon="at" style={styles.groupContentIcon} />
|
||||
<TextInput
|
||||
style={[styles.textInput]}
|
||||
placeholder="eg alice"
|
||||
placeholderTextColor={colors.blue0}
|
||||
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>
|
||||
</View>
|
||||
<View style={styles.groupContent}>
|
||||
<Text style={[s.white, s.p10]}>
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
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(
|
||||
(/*{navigation}: RootTabsScreenProps<'Login'>*/) => {
|
||||
// const store = useStores()
|
||||
const [screenState, setScreenState] = useState<ScreenState>(
|
||||
ScreenState.SigninOrCreateAccount,
|
||||
)
|
||||
const onPressSignin = () => {
|
||||
setScreenState(ScreenState.Signin)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.outer}>
|
||||
{screenState === ScreenState.SigninOrCreateAccount ? (
|
||||
<SigninOrCreateAccount onPressSignin={onPressSignin} />
|
||||
<SigninOrCreateAccount
|
||||
onPressSignin={() => setScreenState(ScreenState.Signin)}
|
||||
onPressCreateAccount={() =>
|
||||
setScreenState(ScreenState.CreateAccount)
|
||||
}
|
||||
/>
|
||||
) : undefined}
|
||||
{screenState === ScreenState.Signin ? (
|
||||
<Signin
|
||||
|
@ -243,6 +457,13 @@ export const Login = observer(
|
|||
}
|
||||
/>
|
||||
) : undefined}
|
||||
{screenState === ScreenState.CreateAccount ? (
|
||||
<CreateAccount
|
||||
onPressBack={() =>
|
||||
setScreenState(ScreenState.SigninOrCreateAccount)
|
||||
}
|
||||
/>
|
||||
) : undefined}
|
||||
</View>
|
||||
)
|
||||
},
|
||||
|
@ -256,9 +477,9 @@ const styles = StyleSheet.create({
|
|||
flex: 2,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
smallHero: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
logoHero: {
|
||||
paddingTop: 30,
|
||||
paddingBottom: 40,
|
||||
},
|
||||
logo: {
|
||||
flexDirection: 'row',
|
||||
|
@ -282,6 +503,7 @@ const styles = StyleSheet.create({
|
|||
paddingVertical: 16,
|
||||
marginBottom: 20,
|
||||
marginHorizontal: 20,
|
||||
backgroundColor: colors.blue3,
|
||||
},
|
||||
btnLabel: {
|
||||
textAlign: 'center',
|
||||
|
@ -307,33 +529,65 @@ const styles = StyleSheet.create({
|
|||
borderRadius: 10,
|
||||
marginBottom: 20,
|
||||
marginHorizontal: 20,
|
||||
backgroundColor: colors.blue3,
|
||||
},
|
||||
groupTitle: {
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 12,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.blue1,
|
||||
},
|
||||
groupContent: {
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 12,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.blue1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
groupContentIcon: {
|
||||
color: 'white',
|
||||
marginLeft: 10,
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
backgroundColor: colors.white,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: colors.blue3,
|
||||
color: colors.white,
|
||||
paddingVertical: 10,
|
||||
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,
|
||||
},
|
||||
pickerIcon: {
|
||||
color: colors.white,
|
||||
},
|
||||
error: {
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.blue1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: 5,
|
||||
backgroundColor: colors.purple3,
|
||||
backgroundColor: colors.blue2,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 4,
|
||||
},
|
||||
errorFloating: {
|
||||
borderWidth: 1,
|
||||
borderColor: colors.blue1,
|
||||
marginBottom: 20,
|
||||
marginHorizontal: 20,
|
||||
borderRadius: 8,
|
||||
},
|
||||
errorIcon: {
|
||||
borderWidth: 1,
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from 'react-native'
|
||||
import {ScreenContainer, Screen} from 'react-native-screens'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
// import Svg, {Polygon} from 'react-native-svg'
|
||||
import {GestureDetector, Gesture} from 'react-native-gesture-handler'
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
|
@ -33,7 +32,7 @@ import {LocationNavigator} from './location-navigator'
|
|||
import {createBackMenu, createForwardMenu} from './history-menu'
|
||||
import {createAccountsMenu} from './accounts-menu'
|
||||
import {createLocationMenu} from './location-menu'
|
||||
import {s, colors, gradients} from '../../lib/styles'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
import {AVIS} from '../../lib/assets'
|
||||
|
||||
const locationIconNeedsNudgeUp = (icon: IconProp) => icon === 'house'
|
||||
|
@ -174,40 +173,8 @@ export const MobileShell: React.FC = observer(() => {
|
|||
<LinearGradient
|
||||
colors={['#007CFF', '#00BCFF']}
|
||||
start={{x: 0, y: 0.8}}
|
||||
end={{x: 1, y: 1}}
|
||||
end={{x: 0, y: 1}}
|
||||
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}>
|
||||
<Login />
|
||||
</SafeAreaView>
|
||||
|
|
82
yarn.lock
82
yarn.lock
|
@ -2195,54 +2195,6 @@
|
|||
dependencies:
|
||||
"@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":
|
||||
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"
|
||||
|
@ -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"
|
||||
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:
|
||||
version "0.10.2"
|
||||
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"
|
||||
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:
|
||||
version "3.3.4"
|
||||
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:
|
||||
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:
|
||||
version "2.0.1"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
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"
|
||||
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:
|
||||
version "3.3.9"
|
||||
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"
|
||||
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:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
|
||||
|
|
Loading…
Reference in New Issue