Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: manage multiple blink accounts in mobile app #3328

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .storybook/views/story-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ const PersistentStateWrapper: React.FC<React.PropsWithChildren> = ({ children })
<PersistentStateContext.Provider
value={{
persistentState: {
schemaVersion: 6,
schemaVersion: 7,
galoyInstance: {
id: "Main",
},
galoyAuthToken: "",
galoyAllAuthTokens: [],
},
updateState: () => {},
resetState: () => {},
Expand Down
15 changes: 8 additions & 7 deletions __tests__/persistent-storage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@ it("returns default when schema is not present", async () => {
expect(state).toEqual(defaultPersistentState)
})

it("migration from 5 to 6", async () => {
const state5 = {
schemaVersion: 5,
it("migration from 6 to 7", async () => {
const state6 = {
schemaVersion: 6,
galoyInstance: { id: "Main" },
galoyAuthToken: "myToken",
}

const state6 = {
schemaVersion: 6,
const state7 = {
schemaVersion: 7,
galoyInstance: { id: "Main" },
galoyAuthToken: "myToken",
galoyAllAuthTokens: ["myToken"],
}

const res = await migrateAndGetPersistentState(state5)
const res = await migrateAndGetPersistentState(state6)

expect(res).toStrictEqual(state6)
expect(res).toStrictEqual(state7)
})
13 changes: 3 additions & 10 deletions app/assets/icons/switch.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions app/components/atomic/galoy-icon/galoy-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import Note from "@app/assets/icons/note.svg"
import People from "@app/assets/icons/people.svg"
import Rank from "@app/assets/icons/rank.svg"
import Refresh from "@app/assets/icons/refresh.svg"
import Switch from "@app/assets/icons/switch.svg"
import { makeStyles, useTheme } from "@rneui/themed"

export const icons = {
Expand Down Expand Up @@ -106,6 +107,7 @@ export const icons = {
"payment-error": PaymentError,
"bell": Bell,
"refresh": Refresh,
"switch": Switch,
} as const

export type IconNamesType = keyof typeof icons
Expand Down
4 changes: 2 additions & 2 deletions app/graphql/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,15 @@ const GaloyClient: React.FC<PropsWithChildren> = ({ children }) => {
if (token) {
authLink = setContext((request, { headers }) => ({
headers: {
...headers,
authorization: getAuthorizationHeader(token),
...headers,
},
}))
} else {
authLink = setContext((request, { headers }) => ({
headers: {
...headers,
authorization: "",
...headers,
},
}))
}
Expand Down
7 changes: 7 additions & 0 deletions app/graphql/generated.gql
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,13 @@ query transactionListForDefaultAccount($first: Int, $after: String, $last: Int,
}
}

query username {
me {
username
__typename
}
}

query walletOverviewScreen {
me {
id
Expand Down
44 changes: 44 additions & 0 deletions app/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2990,6 +2990,11 @@ export type OnChainUsdPaymentSendAsBtcDenominatedMutationVariables = Exact<{

export type OnChainUsdPaymentSendAsBtcDenominatedMutation = { readonly __typename: 'Mutation', readonly onChainUsdPaymentSendAsBtcDenominated: { readonly __typename: 'PaymentSendPayload', readonly status?: PaymentSendResult | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }> } };

export type UsernameQueryVariables = Exact<{ [key: string]: never; }>;


export type UsernameQuery = { readonly __typename: 'Query', readonly me?: { readonly __typename: 'User', readonly username?: string | null } | null };

export type AccountDeleteMutationVariables = Exact<{ [key: string]: never; }>;


Expand Down Expand Up @@ -6748,6 +6753,45 @@ export function useOnChainUsdPaymentSendAsBtcDenominatedMutation(baseOptions?: A
export type OnChainUsdPaymentSendAsBtcDenominatedMutationHookResult = ReturnType<typeof useOnChainUsdPaymentSendAsBtcDenominatedMutation>;
export type OnChainUsdPaymentSendAsBtcDenominatedMutationResult = Apollo.MutationResult<OnChainUsdPaymentSendAsBtcDenominatedMutation>;
export type OnChainUsdPaymentSendAsBtcDenominatedMutationOptions = Apollo.BaseMutationOptions<OnChainUsdPaymentSendAsBtcDenominatedMutation, OnChainUsdPaymentSendAsBtcDenominatedMutationVariables>;
export const UsernameDocument = gql`
query username {
me {
username
}
}
`;

/**
* __useUsernameQuery__
*
* To run a query within a React component, call `useUsernameQuery` and pass it any options that fit your needs.
* When your component renders, `useUsernameQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useUsernameQuery({
* variables: {
* },
* });
*/
export function useUsernameQuery(baseOptions?: Apollo.QueryHookOptions<UsernameQuery, UsernameQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<UsernameQuery, UsernameQueryVariables>(UsernameDocument, options);
}
export function useUsernameLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<UsernameQuery, UsernameQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<UsernameQuery, UsernameQueryVariables>(UsernameDocument, options);
}
export function useUsernameSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions<UsernameQuery, UsernameQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSuspenseQuery<UsernameQuery, UsernameQueryVariables>(UsernameDocument, options);
}
export type UsernameQueryHookResult = ReturnType<typeof useUsernameQuery>;
export type UsernameLazyQueryHookResult = ReturnType<typeof useUsernameLazyQuery>;
export type UsernameSuspenseQueryHookResult = ReturnType<typeof useUsernameSuspenseQuery>;
export type UsernameQueryResult = Apollo.QueryResult<UsernameQuery, UsernameQueryVariables>;
export const AccountDeleteDocument = gql`
mutation accountDelete {
accountDelete {
Expand Down
9 changes: 8 additions & 1 deletion app/hooks/use-app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ export const useAppConfig = () => {
() => ({
token: persistentState.galoyAuthToken,
galoyInstance: resolveGaloyInstanceOrDefault(persistentState.galoyInstance),
allTokens: persistentState.galoyAllAuthTokens,
}),
[persistentState.galoyAuthToken, persistentState.galoyInstance],
[
persistentState.galoyAuthToken,
persistentState.galoyInstance,
persistentState.galoyAllAuthTokens,
],
)

const setGaloyInstance = useCallback(
Expand All @@ -35,6 +40,7 @@ export const useAppConfig = () => {
return {
...state,
galoyAuthToken: token,
galoyAllAuthTokens: [...state.galoyAllAuthTokens, token],
}
return undefined
})
Expand All @@ -50,6 +56,7 @@ export const useAppConfig = () => {
...state,
galoyInstance: instance,
galoyAuthToken: token,
galoyAllAuthTokens: [...state.galoyAllAuthTokens, token],
}
return undefined
})
Expand Down
7 changes: 7 additions & 0 deletions app/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2375,6 +2375,7 @@ const en: BaseTranslation = {
tapToAddPhoneNumber: "Tap to add phone number",
loginMethods: "Login Methods",
level: "Level {level: string}",
switch: "switch",
accountLevel: "Account Level",
upgrade: "Upgrade your account",
logOutAndDeleteLocalData: "Log out and clear all local data",
Expand Down Expand Up @@ -2423,6 +2424,11 @@ const en: BaseTranslation = {
accountId: "Account ID",
copy: "Copy"
},
ProfileScreen: {
addNew : "Add new",
logout: "Logout",
error: "Unable to fetch profiles at this time",
},
TotpRegistrationInitiateScreen: {
title: "Two-factor authentication",
content:
Expand Down Expand Up @@ -2681,6 +2687,7 @@ const en: BaseTranslation = {
phone: "Phone",
phoneNumber: "Phone Number",
preimageProofOfPayment: "Preimage / Proof of Payment",
profile: "Profiles",
rate: "Rate",
reauth: "Your session has expired. Please log in again.",
restart: "Restart",
Expand Down
44 changes: 44 additions & 0 deletions app/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7407,6 +7407,10 @@ type RootTranslation = {
* @param {string} level
*/
level: RequiredParams<'level'>
/**
* s​w​i​t​c​h
*/
'switch': string
/**
* A​c​c​o​u​n​t​ ​L​e​v​e​l
*/
Expand Down Expand Up @@ -7577,6 +7581,20 @@ type RootTranslation = {
*/
copy: string
}
ProfileScreen: {
/**
* A​d​d​ ​n​e​w
*/
addNew: string
/**
* L​o​g​o​u​t
*/
logout: string
/**
* U​n​a​b​l​e​ ​t​o​ ​f​e​t​c​h​ ​p​r​o​f​i​l​e​s​ ​a​t​ ​t​h​i​s​ ​t​i​m​e
*/
error: string
}
TotpRegistrationInitiateScreen: {
/**
* T​w​o​-​f​a​c​t​o​r​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n
Expand Down Expand Up @@ -8395,6 +8413,10 @@ type RootTranslation = {
* P​r​e​i​m​a​g​e​ ​/​ ​P​r​o​o​f​ ​o​f​ ​P​a​y​m​e​n​t
*/
preimageProofOfPayment: string
/**
* P​r​o​f​i​l​e​s
*/
profile: string
/**
* R​a​t​e
*/
Expand Down Expand Up @@ -16418,6 +16440,10 @@ export type TranslationFunctions = {
* Level {level}
*/
level: (arg: { level: string }) => LocalizedString
/**
* switch
*/
'switch': () => LocalizedString
/**
* Account Level
*/
Expand Down Expand Up @@ -16582,6 +16608,20 @@ export type TranslationFunctions = {
*/
copy: () => LocalizedString
}
ProfileScreen: {
/**
* Add new
*/
addNew: () => LocalizedString
/**
* Logout
*/
logout: () => LocalizedString
/**
* Unable to fetch profiles at this time
*/
error: () => LocalizedString
}
TotpRegistrationInitiateScreen: {
/**
* Two-factor authentication
Expand Down Expand Up @@ -17385,6 +17425,10 @@ export type TranslationFunctions = {
* Preimage / Proof of Payment
*/
preimageProofOfPayment: () => LocalizedString
/**
* Profiles
*/
profile: () => LocalizedString
/**
* Rate
*/
Expand Down
9 changes: 8 additions & 1 deletion app/i18n/raw-i18n/source/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2301,6 +2301,7 @@
"tapToAddPhoneNumber": "Tap to add phone number",
"loginMethods": "Login Methods",
"level": "Level {level: string}",
"switch": "switch",
"accountLevel": "Account Level",
"upgrade": "Upgrade your account",
"logOutAndDeleteLocalData": "Log out and clear all local data",
Expand Down Expand Up @@ -2342,6 +2343,11 @@
"accountId": "Account ID",
"copy": "Copy"
},
"ProfileScreen": {
"addNew": "Add new",
"logout": "Logout",
"error": "Unable to fetch profiles at this time"
},
"TotpRegistrationInitiateScreen": {
"title": "Two-factor authentication",
"content": "Scan this QR code with your authenticator app. Alternatively, you can manually copy/paste the secret into your authenticator app."
Expand Down Expand Up @@ -2578,6 +2584,7 @@
"phone": "Phone",
"phoneNumber": "Phone Number",
"preimageProofOfPayment": "Preimage / Proof of Payment",
"profile": "Profiles",
"rate": "Rate",
"reauth": "Your session has expired. Please log in again.",
"restart": "Restart",
Expand Down Expand Up @@ -2728,7 +2735,7 @@
"drivingAdoption": "I'm driving Bitcoin adoption using Blink.",
"connectOnSocial": "Connect on social: ",
"fullDetails": "Full details at ",
"learnMore": "Learn more about Adopting Bitcoin at ",
"learnMore": "Learn more about Adopting Bitcoin at",
"mayChallenge": {
"title": "May Challenge!",
"description": "Grow your inner circle by 6 for a chance to win a set of Pizzas on your next Bitcoin Meetup.",
Expand Down
9 changes: 8 additions & 1 deletion app/navigation/root-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import SendBitcoinCompletedScreen from "@app/screens/send-bitcoin-screen/send-bi
import SendBitcoinConfirmationScreen from "@app/screens/send-bitcoin-screen/send-bitcoin-confirmation-screen"
import SendBitcoinDestinationScreen from "@app/screens/send-bitcoin-screen/send-bitcoin-destination-screen"
import SendBitcoinDetailsScreen from "@app/screens/send-bitcoin-screen/send-bitcoin-details-screen"
import { AccountScreen } from "@app/screens/settings-screen/account"
import { AccountScreen, ProfileScreen } from "@app/screens/settings-screen/account"
import { DefaultWalletScreen } from "@app/screens/settings-screen/default-wallet"
import { DisplayCurrencyScreen } from "@app/screens/settings-screen/display-currency-screen"
import { NotificationSettingsScreen } from "@app/screens/settings-screen/notifications-screen"
Expand Down Expand Up @@ -335,6 +335,13 @@ export const RootStack = () => {
title: LL.common.account(),
}}
/>
<RootNavigator.Screen
name="profileScreen"
component={ProfileScreen}
options={{
title: LL.common.profile(),
}}
/>
<RootNavigator.Screen
name="notificationSettingsScreen"
component={NotificationSettingsScreen}
Expand Down
1 change: 1 addition & 0 deletions app/navigation/stack-param-lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export type RootStackParamList = {
transactionHistory?: undefined
Earn: undefined
accountScreen: undefined
profileScreen: undefined
notificationSettingsScreen: undefined
transactionLimitsScreen: undefined
acceptTermsAndConditions: NewAccountFlowParamsList
Expand Down
Loading
Loading