Skip to content

Commit

Permalink
feat: picture upload (#124)
Browse files Browse the repository at this point in the history
* feat: upload profile picture

---------

Signed-off-by: Iuri Pereira <[email protected]>
  • Loading branch information
iuricmp authored Aug 1, 2024
1 parent 3aca6a5 commit 4a0d6b0
Show file tree
Hide file tree
Showing 17 changed files with 790 additions and 592 deletions.
8 changes: 7 additions & 1 deletion mobile/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@
"experiments": {
"tsconfigPaths": true
},
"plugins": ["expo-router"],
"plugins": ["expo-router", [
"expo-image-picker",
{
"photosPermission": "The app accesses your photos to upload an avatar.",
"cameraPermission": "The app accesses your camera to create an avatar."
}
]],
"extra": {
"router": {
"origin": false
Expand Down
66 changes: 36 additions & 30 deletions mobile/app/home/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Alert, StyleSheet, View } from "react-native";
import { Alert, ScrollView, StyleSheet, View } from "react-native";
import { router, useNavigation } from "expo-router";
import { useEffect, useState } from "react";
import { KeyInfo, useGnoNativeContext } from "@gnolang/gnonative";
Expand All @@ -11,6 +11,7 @@ import Text from "@gno/components/text";
import { useSearch } from "@gno/hooks/use-search";
import { useNotificationContext } from "@gno/provider/notification-provider";
import { onboarding } from "redux/features/signupSlice";
import AvatarPicker from "@gno/components/avatar/avatar-picker";
import { ProgressViewModal } from "@gno/components/view/progress";

export default function Page() {
Expand Down Expand Up @@ -103,35 +104,40 @@ export default function Page() {
<>
<Layout.Container>
<Layout.Body>
<>
<AccountBalance activeAccount={activeAccount} />
<Text.Subheadline>Chain ID:</Text.Subheadline>
<Text.Body>{chainID}</Text.Body>
<Text.Subheadline>Remote:</Text.Subheadline>
<Text.Body>{remote}</Text.Body>
<Text.Subheadline>Followers:</Text.Subheadline>
<Text.Body>{followersCount.n_followers}</Text.Body>
<Text.Subheadline>Following:</Text.Subheadline>
<Text.Body>{followersCount.n_following}</Text.Body>
<View></View>
</>
<Layout.Footer>
<ProgressViewModal visible={modalVisible} onRequestClose={() => setModalVisible(false)} />
<Button.TouchableOpacity title="Logs" onPress={() => setModalVisible(true)} variant="primary" />
<Button.TouchableOpacity title="Onboard the current user" onPress={onboard} variant="primary" />
<Button.TouchableOpacity
title="Register to the notification service"
onPress={onPressNotification}
variant="primary"
/>
<Button.TouchableOpacity title="Logout" onPress={onPressLogout} style={styles.logout} variant="primary-red" />
<Button.TouchableOpacity
title="Remove Account"
onPress={onRemoveAccount}
style={styles.logout}
variant="primary-red"
/>
</Layout.Footer>
<ScrollView >
<View style={{ paddingBottom: 20 }}>
<AvatarPicker />
</View>
<>
<AccountBalance activeAccount={activeAccount} />
<Text.Subheadline>Chain ID:</Text.Subheadline>
<Text.Body>{chainID}</Text.Body>
<Text.Subheadline>Remote:</Text.Subheadline>
<Text.Body>{remote}</Text.Body>
<Text.Subheadline>Followers:</Text.Subheadline>
<Text.Body>{followersCount.n_followers}</Text.Body>
<Text.Subheadline>Following:</Text.Subheadline>
<Text.Body>{followersCount.n_following}</Text.Body>
<View></View>
</>
<Layout.Footer>
<ProgressViewModal visible={modalVisible} onRequestClose={() => setModalVisible(false)} />
<Button.TouchableOpacity title="Logs" onPress={() => setModalVisible(true)} variant="primary" />
<Button.TouchableOpacity title="Onboard the current user" onPress={onboard} variant="primary" />
<Button.TouchableOpacity
title="Register to the notification service"
onPress={onPressNotification}
variant="primary"
/>
<Button.TouchableOpacity title="Logout" onPress={onPressLogout} style={styles.logout} variant="primary-red" />
<Button.TouchableOpacity
title="Remove Account"
onPress={onRemoveAccount}
style={styles.logout}
variant="primary-red"
/>
</Layout.Footer>
</ScrollView>
</Layout.Body>
</Layout.Container>
<LoadingModal visible={loading} />
Expand Down
51 changes: 51 additions & 0 deletions mobile/components/avatar/avatar-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState } from 'react';
import { TouchableOpacity } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import { useGnoNativeContext } from '@gnolang/gnonative';
import { compressImage } from '@gno/utils/file-utils';
import { reloadAvatar, saveAvatar, selectAccount, selectAvatar, useAppDispatch, useAppSelector } from "@gno/redux";
import Avatar from './avatar';

const AvatarPicker: React.FC = () => {
const [base64Image, setBase64Image] = useState<string | null>(null);

const { gnonative } = useGnoNativeContext();

const account = useAppSelector(selectAccount);
const avatarBase64 = useAppSelector(selectAvatar);

const dispatch = useAppDispatch();

const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 0.5, // compress image for smaller size
});

if (!result.canceled) {

const imagePath = result.assets[0].uri;
const mimeType = result.assets[0].mimeType;

const imageCompressed = await compressImage(imagePath)
if (!imageCompressed || !mimeType || !imageCompressed.base64) {
console.log("Error compressing image or missing data");
return;
}
await dispatch(saveAvatar({ mimeType, base64: imageCompressed.base64 })).unwrap();

await dispatch(reloadAvatar()).unwrap();
}
}

return (
<TouchableOpacity onPress={pickImage} >
{base64Image ? <Avatar uri={base64Image} /> : null}
{avatarBase64 ? <Avatar uri={avatarBase64} /> : null}
</TouchableOpacity>
)
}

export default AvatarPicker
26 changes: 26 additions & 0 deletions mobile/components/avatar/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { View, Image, StyleSheet, StyleProp, ImageStyle } from "react-native";

interface Props {
uri: string;
style?: StyleProp<ImageStyle>;
}

const Avatar: React.FC<Props> = ({ uri, style }) => {
return (
<View>
<Image source={{ uri }} style={[styles.image, style]} />
</View>
);
};

const SIZE = 80;

const styles = StyleSheet.create({
image: {
width: SIZE,
height: SIZE,
borderRadius: SIZE,
},
});

export default Avatar;
1 change: 1 addition & 0 deletions mobile/components/avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './avatar';
2 changes: 1 addition & 1 deletion mobile/components/feed/post-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function PostRow({ post, onPress = func, onGnod = func, showFooter = true
<Pressable onPress={() => onPress(post)} style={styles.container}>
<RepostLabel post={post} />
<View style={styles.body}>
<Image source={{ uri: post.user.image }} style={styles.image} />
<Image source={{ uri: post.user.avatar }} style={styles.image} />
<View style={styles.content}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Pressable style={{ flexDirection: "row", alignItems: "center" }} onPress={() => nativgateToAccount(post?.user.name)}>
Expand Down
2 changes: 1 addition & 1 deletion mobile/components/feed/repost-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function RepostRow({ post, onPress = func, showFooter = true }: FeedProps
<View style={styles.body}>
<View style={styles.content}>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Image source={{ uri: post.user.image }} style={styles.image} />
<Image source={{ uri: post.user.avatar }} style={styles.image} />
<Pressable style={{ flexDirection: "row", alignItems: "center", paddingLeft: 8 }} onPress={onPressName}>
<Text.Body style={[{ fontWeight: "bold", fontSize: 16, paddingRight: 8 }]}>@{post.user.name}</Text.Body>
<TimeStampLabel timestamp={post.date} />
Expand Down
1 change: 1 addition & 0 deletions mobile/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./view";
export * from "./avatar";
Loading

0 comments on commit 4a0d6b0

Please sign in to comment.