Skip to content

Commit 720aed0

Browse files
authored
Update loggedIn action to utilize Remote and ChainId (#172)
* fix: update loggedIn action to set remote and chainId received from GnoKey Mobile * refactor to avoid default values
1 parent 208e066 commit 720aed0

File tree

6 files changed

+113
-119
lines changed

6 files changed

+113
-119
lines changed

mobile/.env

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,2 @@
11
# Prod
2-
3-
EXPO_PUBLIC_GNO_REMOTE=https://api.gno.berty.io:443
4-
EXPO_PUBLIC_FAUCET_REMOTE=https://faucetpass.gno.berty.io
5-
EXPO_PUBLIC_INDEXER_REMOTE=https://indexer.gno.berty.io
62
EXPO_PUBLIC_NOTIFICATION_REMOTE=https://notification.gno.berty.io
7-
8-
EXPO_PUBLIC_GNO_CHAIN_ID=dev
9-
10-
# local
11-
12-
# EXPO_PUBLIC_GNO_REMOTE=http://localhost:26657
13-
# EXPO_PUBLIC_FAUCET_REMOTE=http://localhost:5050
14-
# EXPO_PUBLIC_INDEXER_REMOTE=http://localhost:26660
15-
# EXPO_PUBLIC_NOTIFICATION_REMOTE=http://localhost:26661

mobile/app/_layout.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,9 @@ import { LinkingProvider } from "@gno/provider/linking-provider";
1010

1111
const gnoDefaultConfig = {
1212
// @ts-ignore
13-
remote: process.env.EXPO_PUBLIC_GNO_REMOTE!,
13+
remote: '', // It will be set dynamically from linking state
1414
// @ts-ignore
15-
chain_id: process.env.EXPO_PUBLIC_GNO_CHAIN_ID!,
16-
};
17-
18-
const indexerDefaultConfig = {
19-
// @ts-ignore
20-
remote: process.env.EXPO_PUBLIC_INDEXER_REMOTE!,
15+
chain_id: '', // It will be set dynamically from linking state
2116
};
2217

2318
const notificationDefaultConfig = {
@@ -29,7 +24,7 @@ export default function AppLayout() {
2924
return (
3025
<GnoNativeProvider config={gnoDefaultConfig}>
3126
<NotificationProvider config={notificationDefaultConfig}>
32-
<IndexerProvider config={indexerDefaultConfig}>
27+
<IndexerProvider>
3328
<ReduxProvider>
3429
<LinkingProvider>
3530
<ThemeProvider value={DefaultTheme}>

mobile/app/index.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
1-
import { View } from "react-native";
1+
import { Alert, View } from "react-native";
22
import Button from "@gno/components/button";
33
import Layout from "@gno/components/layout";
44
import Ruller from "@gno/components/row/Ruller";
55
import Text from "@gno/components/text";
6-
import { clearLinking, loggedIn, requestLoginForGnokeyMobile, selectAccount, selectBech32AddressSelected, selectLoginLoading, useAppDispatch, useAppSelector } from "@gno/redux";
6+
import {
7+
clearLinking,
8+
loggedIn,
9+
requestLoginForGnokeyMobile,
10+
selectAccount,
11+
selectBech32AddressSelected,
12+
selectLoginLoading,
13+
selectRemoteURL,
14+
useAppDispatch,
15+
useAppSelector,
16+
} from "@gno/redux";
717
import Spacer from "@gno/components/spacer";
818
import * as Application from "expo-application";
919
import { useEffect } from "react";
1020
import { useRouter } from "expo-router";
21+
import { useIndexerContext } from "@gno/provider/indexer-provider";
1122

1223
export default function Root() {
1324
const dispatch = useAppDispatch();
1425
const route = useRouter();
15-
const bech32AddressSelected = useAppSelector(selectBech32AddressSelected)
26+
const bech32AddressSelected = useAppSelector(selectBech32AddressSelected);
27+
const remoteURL = useAppSelector(selectRemoteURL);
1628
const account = useAppSelector(selectAccount);
1729
const loading = useAppSelector(selectLoginLoading);
1830

31+
const { initIndexer } = useIndexerContext();
32+
1933
const appVersion = Application.nativeApplicationVersion;
2034

2135
useEffect(() => {
22-
if (loading) return
36+
if (loading || !bech32AddressSelected || !remoteURL) return;
2337
console.log("bech32AddressSelected on index", bech32AddressSelected);
24-
if (bech32AddressSelected) {
25-
dispatch(loggedIn({ bech32: bech32AddressSelected as string }));
38+
39+
if (remoteURL !== "https://api.gno.berty.io:443") {
40+
Alert.alert(
41+
"Unsupported Remote URL",
42+
"This build supports only https://api.gno.berty.io:443. Please update your remote URL and try again."
43+
);
44+
return;
2645
}
46+
initIndexer("https://indexer.gno.berty.io");
47+
dispatch(loggedIn());
2748
}, [bech32AddressSelected]);
2849

2950
useEffect(() => {

mobile/redux/features/accountSlice.ts

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,24 @@ const initialState: CounterState = {
1414
loading: false,
1515
};
1616

17-
interface LoginParam {
18-
bech32: string;
19-
}
20-
21-
export const loggedIn = createAsyncThunk<User, LoginParam, ThunkExtra>("account/loggedIn", async (param, thunkAPI) => {
17+
export const loggedIn = createAsyncThunk<User, void, ThunkExtra>("account/loggedIn", async (param, thunkAPI) => {
2218
console.log("Logging in", param);
23-
const { bech32 } = param;
2419

20+
const { bech32AddressSelected: bech32, chainId, remoteURL } = (thunkAPI.getState() as RootState).linking;
21+
22+
if (!bech32 || !chainId || !remoteURL) {
23+
throw new Error("No bech32 address, chainId or remoteURL found for login");
24+
}
2525
const gnonative = thunkAPI.extra.gnonative as GnoNativeApi;
2626

27+
await gnonative.setChainID(chainId);
28+
await gnonative.setRemote(remoteURL);
29+
2730
const user: User = {
28-
name: await getAccountName(bech32, gnonative) || 'Unknown',
31+
name: (await getAccountName(bech32, gnonative)) || "Unknown",
2932
address: await gnonative.addressFromBech32(bech32),
3033
bech32,
31-
avatar: await loadBech32AvatarFromChain(bech32, thunkAPI)
34+
avatar: await loadBech32AvatarFromChain(bech32, thunkAPI),
3235
};
3336

3437
return user;
@@ -41,9 +44,9 @@ async function getAccountName(bech32: string, gnonative: GnoNativeApi) {
4144
console.log("GetUserByAddress result:", accountNameStr);
4245
const accountName = accountNameStr.match(/\("(\w+)"/)?.[1];
4346
console.log("GetUserByAddress after regex", accountName);
44-
return accountName
47+
return accountName;
4548
} catch (error) {
46-
console.error("Error getting account name", error);
49+
console.error("Error getting account name", error);
4750
}
4851
return undefined;
4952
}
@@ -55,44 +58,64 @@ interface AvatarCallTxParams {
5558
callbackPath: string;
5659
}
5760

58-
export const avatarTxAndRedirectToSign = createAsyncThunk<void, AvatarCallTxParams, ThunkExtra>("account/avatarTxAndRedirectToSign", async (props, thunkAPI) => {
59-
const { mimeType, base64, callerAddressBech32, callbackPath } = props;
60-
61-
const gnonative = thunkAPI.extra.gnonative;
62-
63-
const gasFee = "1000000ugnot";
64-
const gasWanted = BigInt(10000000);
65-
const args: Array<string> = ["Avatar", String(`data:${mimeType};base64,` + base64)];
66-
const reason = "Upload a new avatar";
67-
// const session = (thunkAPI.getState() as RootState).linking.session;
68-
69-
await makeCallTx({ packagePath: "gno.land/r/demo/profile", fnc: "SetStringField", args, gasFee, gasWanted, callerAddressBech32, reason, callbackPath }, gnonative);
70-
});
71-
72-
export const reloadAvatar = createAsyncThunk<string | undefined, void, ThunkExtra>("account/reloadAvatar", async (param, thunkAPI) => {
73-
74-
const state = await thunkAPI.getState() as CounterState;
75-
// @ts-ignore
76-
const bech32 = state.account?.account?.bech32;
77-
if (bech32) {
78-
return await loadBech32AvatarFromChain(bech32, thunkAPI);
61+
export const avatarTxAndRedirectToSign = createAsyncThunk<void, AvatarCallTxParams, ThunkExtra>(
62+
"account/avatarTxAndRedirectToSign",
63+
async (props, thunkAPI) => {
64+
const { mimeType, base64, callerAddressBech32, callbackPath } = props;
65+
66+
const gnonative = thunkAPI.extra.gnonative;
67+
68+
const gasFee = "1000000ugnot";
69+
const gasWanted = BigInt(10000000);
70+
const args: Array<string> = ["Avatar", String(`data:${mimeType};base64,` + base64)];
71+
const reason = "Upload a new avatar";
72+
// const session = (thunkAPI.getState() as RootState).linking.session;
73+
74+
await makeCallTx(
75+
{
76+
packagePath: "gno.land/r/demo/profile",
77+
fnc: "SetStringField",
78+
args,
79+
gasFee,
80+
gasWanted,
81+
callerAddressBech32,
82+
reason,
83+
callbackPath,
84+
},
85+
gnonative
86+
);
7987
}
80-
return undefined;
81-
});
88+
);
89+
90+
export const reloadAvatar = createAsyncThunk<string | undefined, void, ThunkExtra>(
91+
"account/reloadAvatar",
92+
async (param, thunkAPI) => {
93+
const state = (await thunkAPI.getState()) as CounterState;
94+
// @ts-ignore
95+
const bech32 = state.account?.account?.bech32;
96+
if (bech32) {
97+
return await loadBech32AvatarFromChain(bech32, thunkAPI);
98+
}
99+
return undefined;
100+
}
101+
);
82102

83103
const loadBech32AvatarFromChain = async (bech32: string, thunkAPI: ThunkExtra) => {
84104
const gnonative = thunkAPI.extra.gnonative as GnoNativeApi;
85-
const DEFAULT_AVATAR = "https://www.gravatar.com/avatar/tmp"
105+
const DEFAULT_AVATAR = "https://www.gravatar.com/avatar/tmp";
86106

87107
try {
88108
console.log("Loading avatar for", bech32);
89-
const response = await gnonative.qEval("gno.land/r/demo/profile", `GetStringField("${bech32}","Avatar", "${DEFAULT_AVATAR}")`);
90-
return response.substring(2, response.length - "\" string)".length);
109+
const response = await gnonative.qEval(
110+
"gno.land/r/demo/profile",
111+
`GetStringField("${bech32}","Avatar", "${DEFAULT_AVATAR}")`
112+
);
113+
return response.substring(2, response.length - '" string)'.length);
91114
} catch (error) {
92115
console.error("Error loading avatar", error);
93116
}
94117
return DEFAULT_AVATAR;
95-
}
118+
};
96119

97120
export const accountSlice = createSlice({
98121
name: "account",

mobile/redux/features/linkingSlice.ts

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import { RootState, ThunkExtra } from "redux/redux-provider";
77
interface State {
88
txJsonSigned: string | undefined;
99
bech32AddressSelected: string | undefined;
10-
// session: string | undefined;
10+
chainId: string | undefined;
11+
remoteURL: string | undefined;
1112
}
1213

1314
const initialState: State = {
1415
txJsonSigned: undefined,
1516
bech32AddressSelected: undefined,
16-
// session: undefined,
17+
chainId: undefined,
18+
remoteURL: undefined,
1719
};
1820

1921
export const requestLoginForGnokeyMobile = createAsyncThunk<boolean>("tx/requestLoginForGnokeyMobile", async () => {
@@ -38,7 +40,6 @@ export const postTxAndRedirectToSign = createAsyncThunk<void, MakeTxAndRedirectP
3840
const gasWanted = BigInt(10000000);
3941
const reason = "Post a message";
4042
const callbackPath = "/post";
41-
// const session = (thunkAPI.getState() as RootState).linking.session;
4243

4344
await makeCallTx({ fnc, args, gasFee, gasWanted, callerAddressBech32, reason, callbackPath }, thunkAPI.extra.gnonative);
4445
})
@@ -54,7 +55,6 @@ type MakeCallTxParams = {
5455
callerAddressBech32: string,
5556
reason: string,
5657
callbackPath: string,
57-
// session?: string,
5858
};
5959

6060
export const makeCallTx = async (props: MakeCallTxParams, gnonative: GnoNativeApi): Promise<void> => {
@@ -74,19 +74,6 @@ export const makeCallTx = async (props: MakeCallTxParams, gnonative: GnoNativeAp
7474
url.searchParams.append('client_name', 'dSocial');
7575
url.searchParams.append('reason', reason);
7676
url.searchParams.append('callback', 'tech.berty.dsocial:/' + callbackPath);
77-
// if (session) {
78-
// // Avoid edge case when the session is about to expire
79-
// const sessionInfo = JSON.parse(decodeURIComponent(session))
80-
// const secondsToExpire = (new Date(sessionInfo.expires_at).getTime() - new Date().getTime()) / 1000;
81-
// if (secondsToExpire < 30) {
82-
// url.searchParams.append('session_wanted', 'true');
83-
// } else {
84-
// // TODO: temporarily passing the session key. This should be removed once the session is used to self sign the tx
85-
// url.searchParams.append('session', session);
86-
// }
87-
// } else {
88-
// url.searchParams.append('session_wanted', 'true');
89-
// }
9077

9178
console.log("redirecting to: ", url);
9279
Linking.openURL(url.toString());
@@ -134,7 +121,8 @@ export const linkingSlice = createSlice({
134121

135122
state.bech32AddressSelected = queryParams?.address ? queryParams.address as string : undefined
136123
state.txJsonSigned = queryParams?.tx ? queryParams.tx as string : undefined
137-
// state.session = queryParams?.session ? queryParams.session as string : state.session
124+
state.chainId = queryParams?.chain_id ? queryParams.chain_id as string : undefined
125+
state.remoteURL = queryParams?.remote ? queryParams.remote as string : undefined
138126
},
139127
clearLinking: (state) => {
140128
console.log("clearing linking data");
@@ -144,17 +132,12 @@ export const linkingSlice = createSlice({
144132
selectors: {
145133
selectQueryParamsTxJsonSigned: (state: State) => state.txJsonSigned as string | undefined,
146134
selectBech32AddressSelected: (state: State) => state.bech32AddressSelected as string | undefined,
147-
// selectSessionValidUntil: (state: State) => {
148-
// const session = state.session;
149-
// if (!session) return undefined;
150-
// const sessionInfo = JSON.parse(decodeURIComponent(session));
151-
// return new Date(sessionInfo.expires_at);
152-
// }
153-
},
135+
selectChainId: (state: State) => state.chainId as string | undefined,
136+
selectRemoteURL: (state: State) => state.remoteURL as string | undefined,
137+
},
154138
});
155139

156140
export const { clearLinking, setLinkingData } = linkingSlice.actions;
157141

158-
export const { selectQueryParamsTxJsonSigned, selectBech32AddressSelected,
159-
// selectSessionValidUntil
160-
} = linkingSlice.selectors;
142+
export const { selectQueryParamsTxJsonSigned, selectBech32AddressSelected, selectChainId, selectRemoteURL } =
143+
linkingSlice.selectors;

mobile/src/provider/indexer-provider.tsx

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,33 @@
1-
import { createContext, useContext, useEffect, useState } from "react";
2-
1+
import { createContext, useContext, useState } from "react";
32
import * as Grpc from "@gno/grpc/client";
4-
import { Client } from "@connectrpc/connect";
53

4+
import { Client } from "@connectrpc/connect";
65
import { HelloResponse, HelloStreamResponse, UserAndPostID } from "@buf/gnolang_dsocial-indexer.bufbuild_es/indexerservice_pb";
76
import { IndexerService } from "@buf/gnolang_dsocial-indexer.bufbuild_es/indexerservice_pb";
87

98
export interface IndexerContextProps {
109
getHomePosts: (userPostAddr: string, startIndex: bigint, endIndex: bigint) => Promise<[number, string]>;
1110
hello: (name: string) => Promise<HelloResponse>;
1211
helloStream: (name: string) => Promise<AsyncIterable<HelloStreamResponse>>;
13-
}
14-
15-
interface ConfigProps {
16-
remote: string;
12+
initIndexer: (indexerUrl: string) => void;
13+
indexerInstance?: Client<typeof IndexerService>;
1714
}
1815

1916
interface IndexerProviderProps {
20-
config: ConfigProps;
2117
children: React.ReactNode;
2218
}
2319

2420
const IndexerContext = createContext<IndexerContextProps | null>(null);
2521

26-
const IndexerProvider: React.FC<IndexerProviderProps> = ({ children, config }) => {
22+
const IndexerProvider: React.FC<IndexerProviderProps> = ({ children }) => {
2723
const [clientInstance, setClientInstance] = useState<Client<typeof IndexerService> | undefined>(undefined);
2824

29-
useEffect(() => {
30-
(async () => {
31-
if (clientInstance) {
32-
return; // Prevent re-initialization
33-
}
34-
setClientInstance(initClient(config));
35-
})();
36-
}, []);
37-
38-
const initClient = (config: ConfigProps): Client<typeof IndexerService> => {
39-
if (clientInstance) {
40-
return clientInstance;
25+
const initIndexer = (indexerUrl: string) => {
26+
if (!indexerUrl) {
27+
throw new Error("indexerUrl is required to initialize indexer client.");
4128
}
42-
43-
return Grpc.createIndexerClient(config.remote);
29+
setClientInstance(Grpc.createIndexerClient(indexerUrl));
30+
console.log("Indexer client initialized with URL:", indexerUrl);
4431
};
4532

4633
const getClient = () => {
@@ -87,14 +74,12 @@ const IndexerProvider: React.FC<IndexerProviderProps> = ({ children, config }) =
8774
return client.helloStream({ name });
8875
};
8976

90-
if (!clientInstance) {
91-
return null;
92-
}
93-
9477
const value = {
9578
getHomePosts,
9679
hello,
9780
helloStream,
81+
initIndexer,
82+
indexerInstance: clientInstance,
9883
};
9984

10085
return <IndexerContext.Provider value={value}>{children}</IndexerContext.Provider>;

0 commit comments

Comments
 (0)