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