Skip to content

Commit

Permalink
refactor: using gnokey
Browse files Browse the repository at this point in the history
  • Loading branch information
iuricmp committed Oct 4, 2024
1 parent 62b47c3 commit bc61b8a
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 160 deletions.
14 changes: 2 additions & 12 deletions mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,5 @@ You'll need to have a [service account json file](https://developers.google.com/

## Opening the App using Links

Paste these links inside the mobile app Browser to 'wakeup' the app:

`tech.berty.dsocial://?hello=world`

The schema is:

npx uri-scheme open exp+gnokey://somepath/into/app?hello=world --ios

```sh
$ npx uri-scheme open tech.berty.dsocial://?hello=world --ios
```

You can open this app using [Linking](https://docs.expo.dev/guides/linking/).
To understand the URL format, please refer to the 'expo-linking' usage in this project.
129 changes: 32 additions & 97 deletions mobile/app/post/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,136 +3,71 @@ import Layout from "@gno/components/layout";
import Spacer from "@gno/components/spacer";
import Text from "@gno/components/text";
import TextInput from "@gno/components/textinput";
import { useGnoNativeContext } from "@gnolang/gnonative";
import { Stack, useNavigation, useRouter } from "expo-router";
import { useEffect, useState } from "react";
import { KeyboardAvoidingView, Platform } from "react-native";
import { addProgress } from "redux/features/signupSlice";
import { broadcastTxCommit, requestAddressForGnokeyMobile, selectAccount, useAppDispatch, useAppSelector } from "@gno/redux";
import { broadcastTxCommit, hasParam, makeCallTxAndRedirect, selectAccount, selectQueryParams, selectQueryParamsAddress, useAppDispatch, useAppSelector } from "@gno/redux";
import * as Linking from 'expo-linking';

export default function Search() {
const [postContent, setPostContent] = useState("");
const [error, setError] = useState<string | undefined>(undefined);
const [loading, setLoading] = useState(false);

const { gnonative } = useGnoNativeContext();
const navigation = useNavigation();
const router = useRouter();
const dispatch = useAppDispatch();
const account = useAppSelector(selectAccount);

const url = Linking.useURL();
// address from the url to be used in the makeCallTx
const bech32 = useAppSelector(selectQueryParamsAddress);

const queryParams = useAppSelector(selectQueryParams);

useEffect(() => {
const unsubscribe = navigation.addListener("focus", async () => {
setPostContent("");
if ( queryParams && hasParam("tx", queryParams)) {

const signedTx = decodeURIComponent(queryParams.tx as string)
console.log("signedTx: ", signedTx);

try {
if (!account) throw new Error("No active account");
} catch (error: unknown | Error) {
console.log(error);
setLoading(true);
dispatch(broadcastTxCommit(signedTx)).unwrap();
router.push("home");
} catch (error) {
console.error("on broadcastTxCommit", error);
setError("" + error);
} finally {
setLoading(false);
}
});
return unsubscribe;
}, [navigation]);
}
}, [queryParams]);

useEffect(() => {
(async () => {
if (url) {
const { hostname, path, queryParams } = Linking.parse(url);
if (bech32 && typeof bech32 == 'string' && postContent) {
const argsTx = await dispatch(makeCallTxAndRedirect({ bech32, postContent })).unwrap();

console.log("link url", url);
console.log("link hostname", hostname);
console.log("link path", path);
console.log("link queryParams", queryParams);

if (queryParams) {

if (queryParams.address && typeof queryParams.address === "string") {
const address = decodeURIComponent(queryParams.address)
console.log("address: ", address);
await makeCallTx(address)
}

if (queryParams.tx && typeof queryParams.tx === "string") {
const signedTx = decodeURIComponent(queryParams.tx)
console.log("signedTx: ", signedTx);

try {
setLoading(true);
await dispatch(broadcastTxCommit(signedTx)).unwrap();
router.push("home");
} catch (error) {
console.error("on broadcastTxCommit", error);
setError("" + error);
} finally {
setLoading(false);
}
}
}
console.log("Opening Gnokey to sign the transaction, argsTx: ", argsTx.txJson);
}
})()
}, [url]);
}, [bech32]);

useEffect(() => {
const unsubscribe = navigation.addListener("focus", async () => {
setPostContent("");
if (!account) throw new Error("No active account");
});
return unsubscribe;
}, [navigation]);

const requestAddress = async () => {
console.log("requesting address for GnokeyMobile");
// await dispatch(requestAddressForGnokeyMobile()).unwrap();
const callback = encodeURIComponent('tech.berty.dsocial://post');
Linking.openURL(`land.gno.gnokey://toselect?callback=${callback}`);

}

// const broadcastTxCommit = async (signedTx: string) => {

// setLoading(true);
// setError(undefined);
// dispatch(addProgress(`posting a message.`))

// try {
// for await (const response of await gnonative.broadcastTxCommit(signedTx)) {
// const result = JSON.parse(JSON.stringify(response)).result;
// console.log("broadcast result:", result);
// }
// router.push("home");
// } catch (error) {
// dispatch(addProgress(`error on broadcasting a tx: ` + JSON.stringify(error)))
// console.error("on post screen", error);
// setError("" + error);
// } finally {
// setLoading(false);
// }

// }

const makeCallTx = async (bech32: string) => {
setLoading(true);
setError(undefined);
dispatch(addProgress(`address ${bech32} selected. calling makeCallTx.`))

if (!account) throw new Error("No active account"); // never happens, but just in case

try {
const gasFee = "1000000ugnot";
const gasWanted = BigInt(10000000);
const args: Array<string> = [postContent];

const address = await gnonative.addressFromBech32(bech32);
const argsTx = await gnonative.makeCallTx("gno.land/r/berty/social", "PostMessage", args, gasFee, gasWanted, address)

console.log("Opening Gnokey to sign the transaction, argsTx: ", argsTx.txJson);

setTimeout(() =>
Linking.openURL('land.gno.gnokey://tosign?tx=' + encodeURIComponent(argsTx.txJson)), 500)

} catch (error) {
dispatch(addProgress(`error on makeCallTx: ` + JSON.stringify(error)))
console.error("on post screen", error);
setError("" + error);
} finally {
setLoading(false);
}
};

return (
<>
<Stack.Screen
Expand Down
2 changes: 1 addition & 1 deletion mobile/redux/features/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./accountSlice";
export * from "./profileSlice";
export * from "./replySlice";
export * from "./txSlice";
export * from "./linkingSlice";
72 changes: 72 additions & 0 deletions mobile/redux/features/linkingSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { MakeTxResponse } from "@gnolang/gnonative";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { set } from "date-fns";
import * as Linking from 'expo-linking';
import { RootState, ThunkExtra } from "redux/redux-provider";

interface State {
linkingParsedURl: Linking.ParsedURL | undefined;
queryParams: Linking.QueryParams | undefined;
}

const initialState: State = {
linkingParsedURl: undefined,
queryParams: undefined,
};

export const hasParam = (param: string, queryParams: Linking.QueryParams | undefined): boolean => {
return Boolean(queryParams && queryParams[param] !== undefined);
}

export const requestAddressForGnokeyMobile = createAsyncThunk<boolean>("tx/requestAddressForGnokeyMobile", async () => {
console.log("requesting address for GnokeyMobile");
const callback = encodeURIComponent('tech.berty.dsocial://post');
return await Linking.openURL(`land.gno.gnokey://toselect?callback=${callback}`);
});

export const makeCallTxAndRedirect = createAsyncThunk<MakeTxResponse, { bech32: string, postContent: string }, ThunkExtra>("tx/makeCallTx", async ({ bech32, postContent }, thunkAPI) => {
console.log("making a tx to: ", bech32);

const gnonative = thunkAPI.extra.gnonative;
const address = await gnonative.addressFromBech32(bech32);
const gasFee = "1000000ugnot";
const gasWanted = BigInt(10000000);
const args: Array<string> = [postContent];

const res = await gnonative.makeCallTx("gno.land/r/berty/social", "PostMessage", args, gasFee, gasWanted, address)

setTimeout(() =>
Linking.openURL('land.gno.gnokey://tosign?tx=' + encodeURIComponent(res.txJson)), 500)

return res
})

export const broadcastTxCommit = createAsyncThunk<void, string, ThunkExtra>("tx/broadcastTxCommit", async (signedTx, thunkAPI) => {
console.log("broadcasting tx: ", signedTx);

const gnonative = thunkAPI.extra.gnonative;
await gnonative.broadcastTxCommit(signedTx);
});

/**
* Slice to handle linking between the app and the GnokeyMobile app
*/
export const linkingSlice = createSlice({
name: "linking",
initialState,
reducers: {
setLinkingParsedURL: (state, action) => {
state.linkingParsedURl = action.payload;
state.queryParams = action.payload?.queryParams;
}
},
selectors: {
selectQueryParams: (state: State) => state.queryParams,
selectLinkingParsedURL: (state: State) => state.linkingParsedURl,
selectQueryParamsAddress: (state: State) => state.linkingParsedURl?.queryParams?.address,
},
});

export const { setLinkingParsedURL } = linkingSlice.actions;

export const { selectLinkingParsedURL, selectQueryParams, selectQueryParamsAddress } = linkingSlice.selectors;
30 changes: 0 additions & 30 deletions mobile/redux/features/txSlice.ts

This file was deleted.

20 changes: 12 additions & 8 deletions mobile/redux/redux-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import { accountSlice, profileSlice, replySlice, txSlice } from "./features";
import { accountSlice, profileSlice, replySlice, linkingSlice } from "./features";
import { GnoNativeApi, useGnoNativeContext } from "@gnolang/gnonative";
import { signUpSlice } from "./features/signupSlice";
import { useSearch, UseSearchReturnType } from "@gno/hooks/use-search";
Expand All @@ -17,6 +17,16 @@ export interface ThunkExtra {
extra: { gnonative: GnoNativeApi; search: UseSearchReturnType; push: UseNotificationReturnType, userCache: ReturnType<typeof useUserCache> };
}

const reducer = {
[accountSlice.reducerPath]: accountSlice.reducer,
[profileSlice.reducerPath]: profileSlice.reducer,
[replySlice.reducerPath]: replySlice.reducer,
[signUpSlice.reducerPath]: signUpSlice.reducer,
[linkingSlice.reducerPath]: linkingSlice.reducer,
}

export type RootState = typeof reducer

const ReduxProvider: React.FC<Props> = ({ children }) => {
// Exposing GnoNative API to reduxjs/toolkit
const { gnonative } = useGnoNativeContext();
Expand All @@ -25,13 +35,7 @@ const ReduxProvider: React.FC<Props> = ({ children }) => {
const userCache= useUserCache();

const store = configureStore({
reducer: {
[accountSlice.reducerPath]: accountSlice.reducer,
[profileSlice.reducerPath]: profileSlice.reducer,
[replySlice.reducerPath]: replySlice.reducer,
[signUpSlice.reducerPath]: signUpSlice.reducer,
[txSlice.reducerPath]: txSlice.reducer,
},
reducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
Expand Down
17 changes: 5 additions & 12 deletions mobile/src/provider/linking-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
import * as Linking from 'expo-linking';
import { useEffect } from 'react';
import { useAppDispatch, setLinkingParsedURL } from "@gno/redux";


const LinkingProvider = ({ children }: { children: React.ReactNode }) => {
const url = Linking.useURL();

// const dispatch = useAppDispatch();
const dispatch = useAppDispatch();

useEffect(() => {
if (url) {
const { hostname, path, queryParams } = Linking.parse(url);
const linkingParsedURL = Linking.parse(url);
const { hostname, path, queryParams } = linkingParsedURL;

console.log("link url", url);
console.log("link hostname", hostname);
console.log("link path", path);
console.log("link queryParams", queryParams);

if (queryParams) {

// if (queryParams.tx && typeof queryParams.tx === "string") {
// dispatch(setTxInput({ txInput: queryParams.tx }));
// }

// if (queryParams.callback && typeof queryParams.callback === "string") {
// dispatch(setCallback({ callback: decodeURIComponent(queryParams.callback) }));
// }
}
dispatch(setLinkingParsedURL(linkingParsedURL))
}
}, [url]);

Expand Down

0 comments on commit bc61b8a

Please sign in to comment.