1import * as Types from './types/settings' 2import HiddenString from '../util/hidden-string' 3import {TypedState} from './reducer' 4import * as WaitingConstants from './waiting' 5import * as ChatConstants from './chat2' 6import {getMeta} from './chat2/meta' 7import * as RPCTypes from './types/rpc-gen' 8import {RPCError} from 'util/errors' 9import {ContactResponse} from 'expo-contacts' 10import * as RPCChatTypes from './types/rpc-chat-gen' 11import * as Styles from '../styles' 12 13export const makeEmailRow = (): Types.EmailRow => ({ 14 email: '', 15 isPrimary: false, 16 isVerified: false, 17 lastVerifyEmailDate: 0, 18 visibility: 0, 19}) 20 21export const makePhoneRow = (): Types.PhoneRow => ({ 22 displayNumber: '', 23 e164: '', 24 searchable: false, 25 superseded: false, 26 verified: false, 27}) 28 29export const toPhoneRow = (p: RPCTypes.UserPhoneNumber) => { 30 const {e164ToDisplay} = require('../util/phone-numbers') 31 return { 32 ...makePhoneRow(), 33 displayNumber: e164ToDisplay(p.phoneNumber), 34 e164: p.phoneNumber, 35 searchable: p.visibility === RPCTypes.IdentityVisibility.public, 36 superseded: p.superseded, 37 verified: p.verified, 38 } 39} 40 41export const makeState = (): Types.State => ({ 42 allowDeleteAccount: false, 43 chat: { 44 contactSettings: { 45 error: '', 46 settings: undefined, 47 }, 48 unfurl: { 49 unfurlWhitelist: [], 50 }, 51 }, 52 contacts: { 53 alreadyOnKeybase: [], 54 importError: '', 55 importPromptDismissed: false, 56 permissionStatus: 'unknown', 57 waitingToShowJoinedModal: false, 58 }, 59 email: { 60 error: '', 61 newEmail: '', 62 }, 63 feedback: {}, 64 invites: { 65 acceptedInvites: [], 66 pendingInvites: [], 67 }, 68 notifications: { 69 allowEdit: false, 70 groups: new Map(), 71 }, 72 password: { 73 newPassword: new HiddenString(''), 74 newPasswordConfirm: new HiddenString(''), 75 rememberPassword: true, 76 }, 77 phoneNumbers: { 78 addedPhone: false, 79 error: '', 80 pendingVerification: '', 81 }, 82}) 83 84export const getPushTokenForLogSend = (state: TypedState) => ({pushToken: state.push.token}) 85 86export const getExtraChatLogsForLogSend = (state: TypedState) => { 87 const chat = state.chat2 88 const c = ChatConstants.getSelectedConversation() 89 if (c) { 90 const metaMap = getMeta(state, c) 91 return { 92 badgeMap: chat.badgeMap.get(c), 93 editingMap: chat.editingMap.get(c), 94 messageMap: [...(chat.messageMap.get(c) || new Map()).values()].map(m => ({ 95 a: m.author, 96 i: m.id, 97 o: m.ordinal, 98 out: (m.type === 'text' || m.type === 'attachment') && m.outboxID, 99 s: (m.type === 'text' || m.type === 'attachment') && m.submitState, 100 t: m.type, 101 })), 102 messageOrdinals: chat.messageOrdinals.get(c), 103 metaMap: { 104 channelname: 'x', 105 conversationIDKey: metaMap.conversationIDKey, 106 description: 'x', 107 inboxVersion: metaMap.inboxVersion, 108 isMuted: metaMap.isMuted, 109 membershipType: metaMap.membershipType, 110 notificationsDesktop: metaMap.notificationsDesktop, 111 notificationsGlobalIgnoreMentions: metaMap.notificationsGlobalIgnoreMentions, 112 notificationsMobile: metaMap.notificationsMobile, 113 offline: metaMap.offline, 114 participants: 'x', 115 rekeyers: metaMap.rekeyers && metaMap.rekeyers.size, 116 resetParticipants: metaMap.resetParticipants && metaMap.resetParticipants.size, 117 retentionPolicy: metaMap.retentionPolicy, 118 snippet: 'x', 119 snippetDecoration: RPCChatTypes.SnippetDecoration.none, 120 supersededBy: metaMap.supersededBy, 121 supersedes: metaMap.supersedes, 122 teamRetentionPolicy: metaMap.teamRetentionPolicy, 123 teamType: metaMap.teamType, 124 teamname: metaMap.teamname, 125 timestamp: metaMap.timestamp, 126 tlfname: metaMap.tlfname, 127 trustedState: metaMap.trustedState, 128 wasFinalizedBy: metaMap.wasFinalizedBy, 129 }, 130 pendingOutboxToOrdinal: chat.pendingOutboxToOrdinal.get(c), 131 quote: chat.quote, 132 unreadMap: chat.unreadMap.get(c), 133 } 134 } 135 return {} 136} 137 138export const makePhoneError = (e: RPCError) => { 139 switch (e.code) { 140 case RPCTypes.StatusCode.scphonenumberwrongverificationcode: 141 return 'Incorrect code, please try again.' 142 case RPCTypes.StatusCode.scphonenumberunknown: 143 return e.desc 144 case RPCTypes.StatusCode.scphonenumberalreadyverified: 145 return 'This phone number is already verified.' 146 case RPCTypes.StatusCode.scphonenumberverificationcodeexpired: 147 return 'Verification code expired, resend and try again.' 148 case RPCTypes.StatusCode.scratelimit: 149 return 'Sorry, tried too many guesses in a short period of time. Please try again later.' 150 default: 151 return e.message 152 } 153} 154 155// Get phone number in e.164, or null if we can't parse it. 156export const getE164 = (phoneNumber: string, countryCode?: string) => { 157 const {phoneUtil, ValidationResult, PhoneNumberFormat} = require('../util/phone-numbers') 158 try { 159 const parsed = countryCode ? phoneUtil.parse(phoneNumber, countryCode) : phoneUtil.parse(phoneNumber) 160 const reason = phoneUtil.isPossibleNumberWithReason(parsed) 161 if (reason !== ValidationResult.IS_POSSIBLE) { 162 return null 163 } 164 return phoneUtil.format(parsed, PhoneNumberFormat.E164) as string 165 } catch (e) { 166 return null 167 } 168} 169 170export const nativeContactsToContacts = (contacts: ContactResponse, countryCode: string) => { 171 return contacts.data.reduce<Array<RPCTypes.Contact>>((ret, contact) => { 172 const {name, phoneNumbers = [], emails = []} = contact 173 174 const components = phoneNumbers.reduce<RPCTypes.ContactComponent[]>((res, pn) => { 175 const formatted = getE164(pn.number || '', pn.countryCode || countryCode) 176 if (formatted) { 177 res.push({ 178 label: pn.label, 179 phoneNumber: formatted, 180 }) 181 } 182 return res 183 }, []) 184 components.push(...emails.map(e => ({email: e.email, label: e.label}))) 185 if (components.length) { 186 ret.push({components, name}) 187 } 188 189 return ret 190 }, []) 191} 192 193export const makeAddEmailError = (err: RPCError): string => { 194 switch (err.code) { 195 case RPCTypes.StatusCode.scratelimit: 196 return "Sorry, you've added too many email addresses lately. Please try again later." 197 case RPCTypes.StatusCode.scemailtaken: 198 return 'This email is already claimed by another user.' 199 case RPCTypes.StatusCode.scemaillimitexceeded: 200 return 'You have too many emails, delete one and try again.' 201 case RPCTypes.StatusCode.scinputerror: 202 return 'Invalid email.' 203 } 204 return err.message 205} 206export const securityGroup = 'security' 207export const soundGroup = 'sound' 208export const traceInProgressKey = 'settings:traceInProgress' 209export const traceInProgress = (state: TypedState) => WaitingConstants.anyWaiting(state, traceInProgressKey) 210export const processorProfileInProgressKey = 'settings:processorProfileInProgress' 211export const processorProfileInProgress = (state: TypedState) => 212 WaitingConstants.anyWaiting(state, processorProfileInProgressKey) 213export const importContactsConfigKey = (username: string) => `ui.importContacts.${username}` 214 215export const settingsSubNavWidth = Styles.isTablet 216 ? Styles.globalStyles.mediumSubNavWidth 217 : Styles.globalStyles.shortSubNavWidth 218 219export const refreshNotificationsWaitingKey = 'settingsTabs.refreshNotifications' 220export const chatUnfurlWaitingKey = 'settings:chatUnfurlWaitingKey' 221export const contactSettingsLoadWaitingKey = 'settings:contactSettingsLoadWaitingKey' 222export const contactSettingsSaveWaitingKey = 'settings:contactSettingsSaveWaitingKey' 223export const setLockdownModeWaitingKey = 'settings:setLockdownMode' 224export const loadLockdownModeWaitingKey = 'settings:loadLockdownMode' 225export const checkPasswordWaitingKey = 'settings:checkPassword' 226export const dontUseWaitingKey = 'settings:settingsPage' 227export const sendFeedbackWaitingKey = 'settings:sendFeedback' 228export const addPhoneNumberWaitingKey = 'settings:addPhoneNumber' 229export const resendVerificationForPhoneWaitingKey = 'settings:resendVerificationForPhone' 230export const verifyPhoneNumberWaitingKey = 'settings:verifyPhoneNumber' 231export const importContactsWaitingKey = 'settings:importContacts' 232export const addEmailWaitingKey = 'settings:addEmail' 233export const loadSettingsWaitingKey = 'settings:loadSettings' 234export const settingsWaitingKey = 'settings:generic' 235 236export const aboutTab = 'settingsTabs.aboutTab' 237export const advancedTab = 'settingsTabs.advancedTab' 238export const chatTab = 'settingsTabs.chatTab' 239export const cryptoTab = 'settingsTabs:cryptoTab' 240export const devicesTab = 'settingsTabs.devicesTab' 241export const displayTab = 'settingsTabs.displayTab' 242export const feedbackTab = 'settingsTabs.feedbackTab' 243export const foldersTab = 'settingsTabs.foldersTab' 244export const fsTab = 'settingsTabs.fsTab' 245export const gitTab = 'settingsTabs.gitTab' 246export const invitationsTab = 'settingsTabs.invitationsTab' 247export const accountTab = 'settingsTabs.accountTab' 248export const notificationsTab = 'settingsTabs.notificationsTab' 249export const passwordTab = 'settingsTabs.password' 250export const screenprotectorTab = 'settingsTabs.screenprotector' 251export const logOutTab = 'settingsTabs.logOutTab' 252export const updatePaymentTab = 'settingsTabs.updatePaymentTab' 253export const walletsTab = 'settingsTabs.walletsTab' 254export const contactsTab = 'settingsTabs.contactsTab' 255export const whatsNewTab = 'settingsTabs.whatsNewTab' 256 257export type SettingsTab = 258 | typeof accountTab 259 | typeof updatePaymentTab 260 | typeof invitationsTab 261 | typeof notificationsTab 262 | typeof advancedTab 263 | typeof feedbackTab 264 | typeof aboutTab 265 | typeof devicesTab 266 | typeof displayTab 267 | typeof gitTab 268 | typeof foldersTab 269 | typeof fsTab 270 | typeof logOutTab 271 | typeof screenprotectorTab 272 | typeof passwordTab 273 | typeof walletsTab 274 | typeof chatTab 275 | typeof cryptoTab 276 | typeof contactsTab 277 | typeof whatsNewTab 278