Skip to content

Commit

Permalink
feat: follow
Browse files Browse the repository at this point in the history
  • Loading branch information
iuricmp committed Oct 15, 2024
1 parent 0a2c8b4 commit 9e89bb3
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 95 deletions.
32 changes: 27 additions & 5 deletions mobile/app/[account]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { router, useLocalSearchParams, useNavigation } from "expo-router";
import { AccountView } from "@gno/components/view";
import { useSearch } from "@gno/hooks/use-search";
import { Following, Post, User } from "@gno/types";
import { setPostToReply, useAppSelector } from "@gno/redux";
import { broadcastTxCommit, clearLinking, selectQueryParamsTxJsonSigned, setPostToReply, useAppSelector } from "@gno/redux";
import { selectAccount } from "redux/features/accountSlice";
import { setFollows } from "redux/features/profileSlice";
import { followAndRedirectToSign, setFollows } from "redux/features/profileSlice";
import { useFeed } from "@gno/hooks/use-feed";
import { useUserCache } from "@gno/hooks/use-user-cache";
import ErrorView from "@gno/components/view/account/no-account-view";
Expand All @@ -30,6 +30,28 @@ export default function Page() {
const dispatch = useDispatch();

const currentUser = useAppSelector(selectAccount);
const txJsonSigned = useAppSelector(selectQueryParamsTxJsonSigned);


useEffect(() => {

(async () => {
if (txJsonSigned) {
console.log("txJsonSigned: ", txJsonSigned);

const signedTx = decodeURIComponent(txJsonSigned as string)
try {
await dispatch(broadcastTxCommit(signedTx)).unwrap();
} catch (error) {
console.error("on broadcastTxCommit", error);
}

dispatch(clearLinking());
fetchData();
}
})();

}, [txJsonSigned]);

useEffect(() => {
const unsubscribe = navigation.addListener("focus", async () => {
Expand All @@ -41,6 +63,8 @@ export default function Page() {
const fetchData = async () => {
if (!accountName) return;

console.log("fetching data for account: ", currentUser?.bech32);

try {
setLoading("Loading account...");
const response = await search.getJsonUserByName(accountName);
Expand Down Expand Up @@ -98,9 +122,7 @@ export default function Page() {
};

const onPressFollow = async (address: string, callerAddress: Uint8Array) => {
await search.Follow(address, callerAddress);

fetchData();
await dispatch(followAndRedirectToSign({ address, callerAddress })).unwrap();
};

const onPressUnfollow = async (address: string, callerAddress: Uint8Array) => {
Expand Down
19 changes: 6 additions & 13 deletions mobile/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Button from "@gno/components/button";
import Layout from "@gno/components/layout";
import Ruller from "@gno/components/row/Ruller";
import Text from "@gno/components/text";
import { clearLinking, loggedIn, requestLoginForGnokeyMobile, selectAccount, selectPath, selectQueryParamsAddress, useAppDispatch, useAppSelector } from "@gno/redux";
import { clearLinking, loggedIn, requestLoginForGnokeyMobile, selectBech32AddressSelected, useAppDispatch, useAppSelector } from "@gno/redux";
import Spacer from "@gno/components/spacer";
import * as Application from "expo-application";
import { useEffect } from "react";
Expand All @@ -12,25 +12,18 @@ import { useRouter } from "expo-router";
export default function Root() {
const dispatch = useAppDispatch();
const route = useRouter();
const path = useAppSelector(selectPath)
const bech32 = useAppSelector(selectQueryParamsAddress)
const account = useAppSelector(selectAccount)
const bech32AddressSelected = useAppSelector(selectBech32AddressSelected)

const appVersion = Application.nativeApplicationVersion;

useEffect(() => {
if (account) {
route.replace("/home");
}
}, [account]);

useEffect(() => {
if (path === "login-callback" && bech32) {
dispatch(loggedIn({ bech32: bech32 as string }));
console.log("bech32AddressSelected on index", bech32AddressSelected);
if (bech32AddressSelected) {
dispatch(loggedIn({ bech32: bech32AddressSelected as string }));
dispatch(clearLinking());
setTimeout(() => route.replace("/home"), 500);
}
}, [path, bech32]);
}, [bech32AddressSelected]);



Expand Down
10 changes: 5 additions & 5 deletions mobile/app/post/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import TextInput from "@gno/components/textinput";
import { Stack, useNavigation, useRouter } from "expo-router";
import { useEffect, useState } from "react";
import { KeyboardAvoidingView, Platform } from "react-native";
import { broadcastTxCommit, hasParam, makeCallTxAndRedirectToSign, selectAccount, selectQueryParams, useAppDispatch, useAppSelector } from "@gno/redux";
import { broadcastTxCommit, makeCallTxAndRedirectToSign, selectAccount, selectQueryParamsTxJsonSigned, useAppDispatch, useAppSelector } from "@gno/redux";

export default function Search() {
const [postContent, setPostContent] = useState("");
Expand All @@ -18,13 +18,13 @@ export default function Search() {
const dispatch = useAppDispatch();
const account = useAppSelector(selectAccount);

const queryParams = useAppSelector(selectQueryParams);
const txJsonSigned = useAppSelector(selectQueryParamsTxJsonSigned);

// hook to handle the signed tx from the Gnokey and broadcast it
useEffect(() => {
if (queryParams && hasParam("tx", queryParams)) {
if (txJsonSigned) {

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

try {
Expand All @@ -38,7 +38,7 @@ export default function Search() {
setLoading(false);
}
}
}, [queryParams]);
}, [txJsonSigned]);

useEffect(() => {
const unsubscribe = navigation.addListener("focus", async () => {
Expand Down
72 changes: 23 additions & 49 deletions mobile/redux/features/linkingSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,18 @@ import * as Linking from 'expo-linking';
import { ThunkExtra } from "redux/redux-provider";

interface State {
linkingParsedURl: Linking.ParsedURL | undefined;
queryParams: Linking.QueryParams | undefined;
path: string | undefined;
hostname: string | undefined;
txJsonSigned: string | undefined;
bech32AddressSelected: string | undefined;
}

const initialState: State = {
linkingParsedURl: undefined,
queryParams: undefined,
path: undefined,
hostname: undefined,
txJsonSigned: undefined,
bech32AddressSelected: undefined,
};

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

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

Expand All @@ -41,7 +33,7 @@ export const makeCallTxAndRedirectToSign = createAsyncThunk<MakeTxResponse, Make
const gasFee = "1000000ugnot";
const gasWanted = BigInt(10000000);

const res = await thunkAPI.dispatch(makeCallTx({packagePath, fnc, args, gasFee, gasWanted, callerAddressBech32 })).unwrap();
const res = await thunkAPI.dispatch(makeCallTx({ packagePath, fnc, args, gasFee, gasWanted, callerAddressBech32 })).unwrap();

setTimeout(() => {
const params = [`tx=${encodeURIComponent(res.txJson)}`, `address=${callerAddressBech32}`, `client_name=dSocial`, `reason=Post a message`];
Expand All @@ -51,7 +43,7 @@ export const makeCallTxAndRedirectToSign = createAsyncThunk<MakeTxResponse, Make
return res;
})

type MakeTxParams = {
type MakeCallTxParams = {
packagePath: string,
fnc: string,
args: string[],
Expand All @@ -62,8 +54,8 @@ type MakeTxParams = {
callerAddressBech32: string,
};

export const makeCallTx = createAsyncThunk<MakeTxResponse, MakeTxParams, ThunkExtra>("tx/makeCallTx", async (props, thunkAPI) => {
const {packagePath, fnc, callerAddressBech32, gasFee, gasWanted, args } = props;
export const makeCallTx = createAsyncThunk<MakeTxResponse, MakeCallTxParams, ThunkExtra>("tx/makeCallTx", async (props, thunkAPI) => {
const { packagePath, fnc, callerAddressBech32, gasFee, gasWanted, args } = props;

console.log("making a tx for: ", callerAddressBech32);

Expand All @@ -77,52 +69,34 @@ export const broadcastTxCommit = createAsyncThunk<void, string, ThunkExtra>("tx/
console.log("broadcasting tx: ", signedTx);

const gnonative = thunkAPI.extra.gnonative;
await gnonative.broadcastTxCommit(signedTx);
const res = await gnonative.broadcastTxCommit(signedTx);
console.log("broadcasted tx: ", res);
});

type SetLinkingResponse = Partial<State>;

export const setLinkingParsedURL = createAsyncThunk<SetLinkingResponse, Linking.ParsedURL, ThunkExtra>("tx/setLinkingParsedURL", async (linkingParsedURl, thunkAPI) => {
const { hostname, path, queryParams } = linkingParsedURl;

return {
linkingParsedURl,
queryParams: queryParams || undefined,
path: path || undefined,
hostname: hostname || undefined,
}
})

/**
* Slice to handle linking between the app and the GnokeyMobile app
*/
export const linkingSlice = createSlice({
name: "linking",
initialState,
extraReducers: (builder) => {
builder.addCase(setLinkingParsedURL.fulfilled, (state, action) => {
state.linkingParsedURl = action.payload.linkingParsedURl;
state.queryParams = action.payload.queryParams;
state.path = action.payload.path;
state.hostname = action.payload.hostname;
})
},
reducers: {
setLinkingData: (state, action) => {
const queryParams = action.payload.queryParams

state.bech32AddressSelected = queryParams?.address ? queryParams.address as string : undefined
state.txJsonSigned = queryParams?.tx ? queryParams.tx as string : undefined
},
clearLinking: (state) => {
state.linkingParsedURl = undefined;
state.queryParams = undefined;
state.path = undefined;
state.hostname = undefined;
console.log("clearing linking data");
state = { ...initialState };
}
},
selectors: {
selectPath: (state: State) => state.path,
selectQueryParams: (state: State) => state.queryParams,
selectLinkingParsedURL: (state: State) => state.linkingParsedURl,
selectQueryParamsAddress: (state: State) => state.linkingParsedURl?.queryParams?.address as string | undefined,
selectQueryParamsTxJsonSigned: (state: State) => state.txJsonSigned as string | undefined,
selectBech32AddressSelected: (state: State) => state.bech32AddressSelected as string | undefined,
},
});

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

export const { selectLinkingParsedURL, selectQueryParams, selectQueryParamsAddress, selectPath } = linkingSlice.selectors;
export const { selectQueryParamsTxJsonSigned, selectBech32AddressSelected } = linkingSlice.selectors;
22 changes: 22 additions & 0 deletions mobile/redux/features/profileSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { makeCallTx } from "./linkingSlice";
import { Following } from "@gno/types";
import { ThunkExtra } from "redux/redux-provider";
import * as Linking from 'expo-linking';

export interface ProfileState {
following: Following[];
Expand All @@ -18,6 +21,25 @@ interface FollowsProps {
followers: Following[];
}

export const followAndRedirectToSign = createAsyncThunk<void, { address: string, callerAddress: Uint8Array }, ThunkExtra>("profile/follow", async ({ address, callerAddress }, thunkAPI) => {
console.log("Follow user: %s", address);
const gnonative = thunkAPI.extra.gnonative;

const packagePath = "gno.land/r/berty/social";
const fnc = "Follow";
const args: Array<string> = [address];
const gasFee = "1000000ugnot";
const gasWanted = BigInt(10000000);
const callerAddressBech32 = await gnonative.addressToBech32(callerAddress);

const res = await thunkAPI.dispatch(makeCallTx({ packagePath, fnc, args, gasFee, gasWanted, callerAddressBech32 })).unwrap();

setTimeout(() => {
const params = [`tx=${encodeURIComponent(res.txJson)}`, `address=${callerAddressBech32}`, 'client_name=dSocial', 'reason=Folow a user', `callback=${encodeURIComponent('tech.berty.dsocial://account')}`];
Linking.openURL('land.gno.gnokey://tosign?' + params.join('&'))
}, 500)
});

export const setFollows = createAsyncThunk("profile/setFollows", async ({ following, followers }: FollowsProps, _) => {
return { following, followers };
});
Expand Down
15 changes: 0 additions & 15 deletions mobile/src/hooks/use-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,6 @@ const MAX_RESULT = 10;
export const useSearch = () => {
const { gnonative } = useGnoNativeContext();

async function Follow(address: string, callerAddress: Uint8Array) {

try {
const gasFee = "1000000ugnot";
const gasWanted = BigInt(10000000);
const args: Array<string> = [address];
for await (const response of await gnonative.call("gno.land/r/berty/social", "Follow", args, gasFee, gasWanted, callerAddress)) {
console.log("response: ", JSON.stringify(response));
}
} catch (error) {
console.error("error registering account", error);
}
}

async function Unfollow(address: string, callerAddress: Uint8Array) {

try {
Expand Down Expand Up @@ -103,7 +89,6 @@ export const useSearch = () => {
GetJsonFollowersCount,
GetJsonFollowing,
GetJsonFollowers,
Follow,
Unfollow,
};
};
Expand Down
11 changes: 3 additions & 8 deletions mobile/src/provider/linking-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Linking from 'expo-linking';
import { useEffect } from 'react';
import { useAppDispatch, setLinkingParsedURL } from "@gno/redux";
import { setLinkingData, useAppDispatch } from "@gno/redux";


const LinkingProvider = ({ children }: { children: React.ReactNode }) => {
Expand All @@ -12,14 +12,9 @@ const LinkingProvider = ({ children }: { children: React.ReactNode }) => {
(async () => {
if (url) {
const linkingParsedURL = Linking.parse(url);
const { hostname, path, queryParams } = linkingParsedURL;
console.log("link url received", url);

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

await dispatch(setLinkingParsedURL(linkingParsedURL)).unwrap();
await dispatch(setLinkingData(linkingParsedURL));
}
})();
}, [url]);
Expand Down

0 comments on commit 9e89bb3

Please sign in to comment.