From 3993175a466d4273373591a765bfc2f6a6e89ef4 Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Tue, 20 Apr 2021 15:23:32 -0700 Subject: TMA-794, On press view and popup styling and partial functionality --- src/components/common/BadgeDetailView.tsx | 231 ++++++++++++++++++++++++++++++ src/components/profile/ProfileHeader.tsx | 69 ++++++++- src/constants/api.ts | 2 + src/screens/badge/BadgeSelection.tsx | 2 +- src/services/SuggestedPeopleService.ts | 32 +++++ 5 files changed, 331 insertions(+), 5 deletions(-) create mode 100644 src/components/common/BadgeDetailView.tsx (limited to 'src') diff --git a/src/components/common/BadgeDetailView.tsx b/src/components/common/BadgeDetailView.tsx new file mode 100644 index 00000000..1dc50c32 --- /dev/null +++ b/src/components/common/BadgeDetailView.tsx @@ -0,0 +1,231 @@ +import {useNavigation} from '@react-navigation/core'; +import React, {useState} from 'react'; +import {FlatList, Image, Modal, StyleSheet, Text, View} from 'react-native'; +import {TouchableOpacity} from 'react-native-gesture-handler'; +import LinearGradient from 'react-native-linear-gradient'; +import {BADGE_GRADIENT_FIRST} from '../../constants'; +import CloseIcon from '../../assets/ionicons/close-outline.svg'; +import {UniversityBadge, UniversityType} from '../../types'; +import {getUniversityBadge, normalize} from '../../utils'; +import {removeBadgesService} from '../../services'; +import {useSelector} from 'react-redux'; +import {RootState} from '../../store/rootreducer'; + +interface BadgeDetailModalProps { + isEditable: boolean; + setBadgeViewVisible: Function; + selectedBadges: UniversityBadge[]; + name?: string; + userId: string; +} + +const BadgeDetailView: React.FC = ({ + name, + isEditable = true, + setBadgeViewVisible, + selectedBadges, +}) => { + const navigation = useNavigation(); + const [removableBadges, setRemovableBadges] = useState([]); + + const { + user: {userId = ''}, + } = useSelector((state: RootState) => state.user); + + const removeBadgeCell = async () => { + await removeBadgesService(removableBadges, userId); + }; + + const badgeEditCell = ({item: {id, name, badgeImage}}) => { + return ( + { + setBadgeViewVisible(false); + navigation.navigate('MutualBadgeHolders', { + badge_id: id, + badge_title: name, + badge_img: badgeImage, + }); + }}> + + + + + {isEditable && ( + { + selectedBadges = selectedBadges.filter((b) => b.id === id); + const obj = {id, name}; + removableBadges.push(obj); + removeBadgeCell(); + }}> + + + )} + + + {name} + + + ); + }; + + const addButton = () => { + return ( + { + setBadgeViewVisible(false); + navigation.navigate('BadgeSelection', {editing: true}); + }} + style={styles.addButtonStyles}> + Add Badges + + ); + }; + + const modalHeader = () => { + const heading = isEditable ? 'Edit your badges!' : name; + const subheading = isEditable + ? 'Add or delete your badges' + : 'View badges to discover groups!'; + return ( + + {heading} + {subheading} + + ); + }; + + return ( + + + + + setBadgeViewVisible(false)}> + + + + + + + {modalHeader()} + + item.id.toString()} + /> + + {isEditable && addButton()} + + + + ); +}; + +const styles = StyleSheet.create({ + badgeCellContainerStyles: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 10, + }, + badgeCellImageContainerStyles: { + flexDirection: 'row', + marginLeft: 25, + }, + badgeCellImageNoEditContainerStyles: { + flexDirection: 'row', + marginHorizontal: 25, + }, + badgeCellImageStyles: { + borderRadius: 50, + }, + badgeCellTextStyles: { + fontWeight: '600', + fontSize: normalize(12), + lineHeight: normalize(16), + textAlign: 'center', + }, + addButtonStyles: { + height: 40, + borderRadius: 5, + marginBottom: 45, + width: '40%', + marginTop: 14, + alignItems: 'center', + justifyContent: 'center', + alignSelf: 'center', + backgroundColor: '#698DD3', + }, + addButtonTextStyles: {color: 'white'}, + modalHeadingStyles: { + fontWeight: '600', + fontSize: normalize(17), + lineHeight: normalize(20.29), + textAlign: 'center', + }, + modalSubheadingStyles: { + fontWeight: '600', + fontSize: normalize(11), + lineHeight: normalize(15), + textAlign: 'center', + color: '#828282', + }, + modalUpperContainerStyles: { + flexDirection: 'row', + alignItems: 'flex-start', + }, + modalImageContainerStyles: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + modalImageStyles: {left: -15, top: -30}, + modalListStyles: { + alignSelf: 'center', + marginVertical: 35, + }, + viewWrapper: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'rgba(0, 0, 0, 0.2)', + }, + modalView: { + width: '95%', + backgroundColor: '#fff', + borderRadius: 7, + }, +}); + +export default BadgeDetailView; diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 2c623c2b..a17d9f6f 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -1,10 +1,14 @@ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {StyleSheet, Text, View} from 'react-native'; +import {TouchableOpacity} from 'react-native-gesture-handler'; import {useSelector} from 'react-redux'; import {PROFILE_CUTOUT_TOP_Y} from '../../constants'; +import {BADGE_DATA} from '../../constants/badges'; +import {getSuggestedPeopleProfile} from '../../services'; import {RootState} from '../../store/rootreducer'; -import {ScreenType} from '../../types'; +import {ScreenType, UniversityBadge} from '../../types'; import {normalize} from '../../utils'; +import BadgeDetailView from '../common/BadgeDetailView'; import Avatar from './Avatar'; import FriendsCount from './FriendsCount'; import ProfileMoreInfoDrawer from './ProfileMoreInfoDrawer'; @@ -25,12 +29,59 @@ const ProfileHeader: React.FC = ({ }) => { const { profile: {name = '', university_class = 2021, university}, - user: {username: userXName = ''}, + user: {userId, username: userXName = ''}, } = useSelector((state: RootState) => userXId ? state.userX[screenType][userXId] : state.user, ); + + const { + user: {username = ''}, + } = useSelector((state: RootState) => state.user); const [drawerVisible, setDrawerVisible] = useState(false); + const [showBadgeView, setBadgeViewVisible] = useState(false); const [firstName, lastName] = [...name.split(' ')]; + const [selectedBadges, setSelectedBadges] = useState([]); + const [selectedBadgesWithImage, setSelectedBadgesWithImage] = useState( + [], + ); + + const fetchBadges = async () => { + const uid = userXId ? userXId : userId; + if (uid) { + const response = await getSuggestedPeopleProfile(uid); + if (response) { + const data = response.badges; + let extractedBadgeNames: UniversityBadge[] = []; + data.forEach((badge) => { + extractedBadgeNames.push(badge); + }); + setSelectedBadges(extractedBadgeNames); + } + } + }; + + useEffect(() => { + fetchBadges(); + }, []); + + useEffect(() => { + let badgesWithImage = []; + selectedBadges.forEach((e) => { + const uniData = BADGE_DATA[e.university]; + const categoryData = uniData.filter((u) => { + return u.title === e.category; + }); + const badgeData = categoryData[0].data.filter((c) => { + return c.badgeName === e.name; + }); + badgeData.forEach((c) => { + const obj = {...e, badgeImage: c.badgeImage}; + badgesWithImage.push(obj); + }); + }); + setSelectedBadgesWithImage(badgesWithImage); + }, [selectedBadges]); + return ( = ({ )} - + setBadgeViewVisible(true)}> + + + {showBadgeView && ( + + )} diff --git a/src/constants/api.ts b/src/constants/api.ts index dd934f0e..45b6e8ae 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -58,6 +58,8 @@ export const SP_MUTUAL_BADGE_HOLDERS_ENDPOINT: string = export const ADD_BADGES_ENDPOINT: string = SP_USERS_ENDPOINT + 'add_badges/'; export const UPDATE_BADGES_ENDPOINT: string = SP_USERS_ENDPOINT + 'update_badges/'; + export const REMOVE_BADGES_ENDPOINT: string = + SP_USERS_ENDPOINT + 'remove_badges/'; // Register as FCM device export const FCM_ENDPOINT: string = API_URL + 'fcm/'; diff --git a/src/screens/badge/BadgeSelection.tsx b/src/screens/badge/BadgeSelection.tsx index 91617377..0284498d 100644 --- a/src/screens/badge/BadgeSelection.tsx +++ b/src/screens/badge/BadgeSelection.tsx @@ -66,7 +66,7 @@ const BadgeSelection: React.FC = ({route}) => { style={styles.rightButtonContainer} onPress={async () => { if (editing) { - updateBadgesService(selectedBadges, university); + await updateBadgesService(selectedBadges, university); navigation.navigate('UpdateSPPicture', { editing: true, }); diff --git a/src/services/SuggestedPeopleService.ts b/src/services/SuggestedPeopleService.ts index 617f3970..7f5b4b8c 100644 --- a/src/services/SuggestedPeopleService.ts +++ b/src/services/SuggestedPeopleService.ts @@ -7,6 +7,7 @@ import { import { ADD_BADGES_ENDPOINT, EDIT_PROFILE_ENDPOINT, + REMOVE_BADGES_ENDPOINT, SP_MUTUAL_BADGE_HOLDERS_ENDPOINT, SP_UPDATE_PICTURE_ENDPOINT, SP_USERS_ENDPOINT, @@ -195,3 +196,34 @@ export const updateBadgesService = async ( Alert.alert(ERROR_UPLOAD_BADGES); } }; + +export const removeBadgesService = async ( + removableBadges: string[], + userId: string, +) => { + try { + const token = await AsyncStorage.getItem('token'); + const form = new FormData(); + form.append('badges', JSON.stringify(removableBadges)); + form.append('user', JSON.stringify(userId)); + const response = await fetch(REMOVE_BADGES_ENDPOINT, { + method: 'DELETE', + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: 'Token ' + token, + }, + body: form, + }); + if (response.status === 400) { + Alert.alert(ERROR_BADGES_EXCEED_LIMIT); + return; + } + if (response.status === 200) { + Alert.alert(SUCCESS_BADGES_UPDATE); + return; + } + } catch (error) { + console.log(error); + Alert.alert(ERROR_UPLOAD_BADGES); + } +}; -- cgit v1.2.3-70-g09d2 From af7c3d686149f32128d251c6d2fab3d82fb89ac4 Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Wed, 21 Apr 2021 16:42:34 -0700 Subject: Badge view changes --- src/components/common/BadgeDetailView.tsx | 154 ++++++++++++++++++++++-------- src/components/profile/FriendsCount.tsx | 1 + src/components/profile/ProfileHeader.tsx | 46 +-------- src/components/profile/UniversityIcon.tsx | 23 ++++- 4 files changed, 136 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/components/common/BadgeDetailView.tsx b/src/components/common/BadgeDetailView.tsx index 1dc50c32..f4271099 100644 --- a/src/components/common/BadgeDetailView.tsx +++ b/src/components/common/BadgeDetailView.tsx @@ -1,39 +1,90 @@ import {useNavigation} from '@react-navigation/core'; -import React, {useState} from 'react'; -import {FlatList, Image, Modal, StyleSheet, Text, View} from 'react-native'; +import React, {useEffect, useState} from 'react'; +import { + ActivityIndicator, + FlatList, + Image, + Modal, + StyleSheet, + Text, + View, +} from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; import LinearGradient from 'react-native-linear-gradient'; -import {BADGE_GRADIENT_FIRST} from '../../constants'; +import {useSelector} from 'react-redux'; import CloseIcon from '../../assets/ionicons/close-outline.svg'; +import {BADGE_GRADIENT_FIRST} from '../../constants'; +import {BADGE_DATA} from '../../constants/badges'; +import {getSuggestedPeopleProfile, removeBadgesService} from '../../services'; +import {RootState} from '../../store/rootreducer'; import {UniversityBadge, UniversityType} from '../../types'; import {getUniversityBadge, normalize} from '../../utils'; -import {removeBadgesService} from '../../services'; -import {useSelector} from 'react-redux'; -import {RootState} from '../../store/rootreducer'; interface BadgeDetailModalProps { isEditable: boolean; setBadgeViewVisible: Function; - selectedBadges: UniversityBadge[]; name?: string; - userId: string; } const BadgeDetailView: React.FC = ({ name, isEditable = true, setBadgeViewVisible, - selectedBadges, }) => { - const navigation = useNavigation(); - const [removableBadges, setRemovableBadges] = useState([]); - const { user: {userId = ''}, } = useSelector((state: RootState) => state.user); + const navigation = useNavigation(); + const [selectedBadges, setSelectedBadges] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [selectedBadgesWithImage, setSelectedBadgesWithImage] = useState( + [], + ); + + const fetchBadges = async () => { + if (userId) { + const response = await getSuggestedPeopleProfile(userId); + if (response) { + const data = response.badges; + let extractedBadgeNames: UniversityBadge[] = []; + data.forEach((badge) => { + extractedBadgeNames.push(badge); + }); + setSelectedBadges(extractedBadgeNames); + } + } + }; + + useEffect(() => { + setIsLoading(true); + fetchBadges(); + }, []); + + useEffect(() => { + let badgesWithImage = []; + selectedBadges.forEach((e) => { + const uniData = BADGE_DATA[e.university]; + const categoryData = uniData.filter((u) => { + return u.title === e.category; + }); + const badgeData = categoryData[0].data.filter((c) => { + return c.badgeName === e.name; + }); + badgeData.forEach((c) => { + const obj = {...e, badgeImage: c.badgeImage}; + badgesWithImage.push(obj); + }); + }); + setTimeout(() => { + setSelectedBadgesWithImage(badgesWithImage); + setIsLoading(false); + }, 500); + }, [selectedBadges]); - const removeBadgeCell = async () => { - await removeBadgesService(removableBadges, userId); + const removeBadgeCell = async (badge: string) => { + setIsLoading(true); + await removeBadgesService([badge], userId); + fetchBadges(); }; const badgeEditCell = ({item: {id, name, badgeImage}}) => { @@ -61,27 +112,20 @@ const BadgeDetailView: React.FC = ({ angle={136.69}> {isEditable && ( { - selectedBadges = selectedBadges.filter((b) => b.id === id); - const obj = {id, name}; - removableBadges.push(obj); - removeBadgeCell(); + removeBadgeCell(name); }}> )} - + {name} @@ -114,17 +158,13 @@ const BadgeDetailView: React.FC = ({ ); }; - return ( - + const _modalContent = () => { + return ( setBadgeViewVisible(false)}> @@ -136,19 +176,40 @@ const BadgeDetailView: React.FC = ({ {modalHeader()} - - item.id.toString()} - /> - + {!isLoading && ( + + item.id.toString()} + /> + + )} + {isLoading && _loaderView()} {isEditable && addButton()} + ); + }; + + const _loaderView = () => { + return ( + + + + ); + }; + + return ( + + {_modalContent()} ); }; @@ -163,12 +224,20 @@ const styles = StyleSheet.create({ flexDirection: 'row', marginLeft: 25, }, + badgeImageStyles: { + width: '50%', + height: '50%', + alignSelf: 'center', + }, badgeCellImageNoEditContainerStyles: { flexDirection: 'row', marginHorizontal: 25, }, badgeCellImageStyles: { borderRadius: 50, + width: 70, + height: 70, + justifyContent: 'center', }, badgeCellTextStyles: { fontWeight: '600', @@ -176,6 +245,8 @@ const styles = StyleSheet.create({ lineHeight: normalize(16), textAlign: 'center', }, + userNameContainerStyles: {marginTop: 10}, + crossButtonStyles: {marginTop: 10, marginLeft: 10}, addButtonStyles: { height: 40, borderRadius: 5, @@ -194,6 +265,7 @@ const styles = StyleSheet.create({ lineHeight: normalize(20.29), textAlign: 'center', }, + loaderStyles: {justifyContent: 'center', marginVertical: 20}, modalSubheadingStyles: { fontWeight: '600', fontSize: normalize(11), diff --git a/src/components/profile/FriendsCount.tsx b/src/components/profile/FriendsCount.tsx index 4790743b..18cd1400 100644 --- a/src/components/profile/FriendsCount.tsx +++ b/src/components/profile/FriendsCount.tsx @@ -62,6 +62,7 @@ const styles = StyleSheet.create({ fontSize: normalize(14), }, label: { + marginTop: 10, fontWeight: '500', fontSize: normalize(14), }, diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index a17d9f6f..061bbd0d 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -40,47 +40,6 @@ const ProfileHeader: React.FC = ({ const [drawerVisible, setDrawerVisible] = useState(false); const [showBadgeView, setBadgeViewVisible] = useState(false); const [firstName, lastName] = [...name.split(' ')]; - const [selectedBadges, setSelectedBadges] = useState([]); - const [selectedBadgesWithImage, setSelectedBadgesWithImage] = useState( - [], - ); - - const fetchBadges = async () => { - const uid = userXId ? userXId : userId; - if (uid) { - const response = await getSuggestedPeopleProfile(uid); - if (response) { - const data = response.badges; - let extractedBadgeNames: UniversityBadge[] = []; - data.forEach((badge) => { - extractedBadgeNames.push(badge); - }); - setSelectedBadges(extractedBadgeNames); - } - } - }; - - useEffect(() => { - fetchBadges(); - }, []); - - useEffect(() => { - let badgesWithImage = []; - selectedBadges.forEach((e) => { - const uniData = BADGE_DATA[e.university]; - const categoryData = uniData.filter((u) => { - return u.title === e.category; - }); - const badgeData = categoryData[0].data.filter((c) => { - return c.badgeName === e.name; - }); - badgeData.forEach((c) => { - const obj = {...e, badgeImage: c.badgeImage}; - badgesWithImage.push(obj); - }); - }); - setSelectedBadgesWithImage(badgesWithImage); - }, [selectedBadges]); return ( @@ -111,13 +70,14 @@ const ProfileHeader: React.FC = ({ setBadgeViewVisible(true)}> - + {showBadgeView && ( )} diff --git a/src/components/profile/UniversityIcon.tsx b/src/components/profile/UniversityIcon.tsx index 4cb1abe6..f1451f83 100644 --- a/src/components/profile/UniversityIcon.tsx +++ b/src/components/profile/UniversityIcon.tsx @@ -8,6 +8,7 @@ export interface UniversityIconProps extends ViewProps { university: UniversityType; university_class?: number; imageStyle?: StyleProp; + needsShadow?: boolean; } /** @@ -18,13 +19,16 @@ const UniversityIcon: React.FC = ({ university, university_class, imageStyle, + needsShadow = false, }) => { return ( - + + + {university_class && ( {getUniversityClass(university_class)} @@ -50,6 +54,17 @@ const styles = StyleSheet.create({ width: normalize(17), height: normalize(19), }, + shadowStyle: { + padding: 5, + borderRadius: 30, + shadowOffset: { + width: 1, + height: 1, + }, + shadowOpacity: 1, + shadowRadius: 3, + backgroundColor: 'white', + }, }); export default UniversityIcon; -- cgit v1.2.3-70-g09d2 From c740dd5b018e427c23fd41b8dc255df671eabc61 Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Thu, 22 Apr 2021 15:55:49 -0700 Subject: PR Changes --- src/components/common/BadgeDetailView.tsx | 2 +- src/components/profile/FriendsCount.tsx | 2 +- src/components/profile/UniversityIcon.tsx | 5 +++-- src/screens/badge/BadgeSelection.tsx | 10 +++++++--- 4 files changed, 12 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/components/common/BadgeDetailView.tsx b/src/components/common/BadgeDetailView.tsx index f4271099..32cc7aa4 100644 --- a/src/components/common/BadgeDetailView.tsx +++ b/src/components/common/BadgeDetailView.tsx @@ -205,7 +205,7 @@ const BadgeDetailView: React.FC = ({ return ( diff --git a/src/components/profile/FriendsCount.tsx b/src/components/profile/FriendsCount.tsx index 18cd1400..8252266e 100644 --- a/src/components/profile/FriendsCount.tsx +++ b/src/components/profile/FriendsCount.tsx @@ -62,7 +62,7 @@ const styles = StyleSheet.create({ fontSize: normalize(14), }, label: { - marginTop: 10, + marginTop: 4, fontWeight: '500', fontSize: normalize(14), }, diff --git a/src/components/profile/UniversityIcon.tsx b/src/components/profile/UniversityIcon.tsx index f1451f83..cfe1366d 100644 --- a/src/components/profile/UniversityIcon.tsx +++ b/src/components/profile/UniversityIcon.tsx @@ -51,8 +51,8 @@ const styles = StyleSheet.create({ fontWeight: '500', }, icon: { - width: normalize(17), - height: normalize(19), + width: normalize(12), + height: normalize(13), }, shadowStyle: { padding: 5, @@ -63,6 +63,7 @@ const styles = StyleSheet.create({ }, shadowOpacity: 1, shadowRadius: 3, + shadowColor: 'rgba(0, 0, 0, 0.3)', backgroundColor: 'white', }, }); diff --git a/src/screens/badge/BadgeSelection.tsx b/src/screens/badge/BadgeSelection.tsx index 0284498d..38a2b01c 100644 --- a/src/screens/badge/BadgeSelection.tsx +++ b/src/screens/badge/BadgeSelection.tsx @@ -67,9 +67,13 @@ const BadgeSelection: React.FC = ({route}) => { onPress={async () => { if (editing) { await updateBadgesService(selectedBadges, university); - navigation.navigate('UpdateSPPicture', { - editing: true, - }); + if (navigation.canGoBack()) { + navigation.goBack(); + } else { + navigation.navigate('UpdateSPPicture', { + editing: true, + }); + } } else { if (selectedBadges.length !== 0) { const success = await addBadgesService( -- cgit v1.2.3-70-g09d2 From ac082ed35d765f7182e95541d8eb9e2d9f033641 Mon Sep 17 00:00:00 2001 From: ankit-thanekar007 Date: Thu, 22 Apr 2021 16:00:44 -0700 Subject: Linter Issues --- src/components/profile/ProfileHeader.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/components/profile/ProfileHeader.tsx b/src/components/profile/ProfileHeader.tsx index 061bbd0d..9dc58501 100644 --- a/src/components/profile/ProfileHeader.tsx +++ b/src/components/profile/ProfileHeader.tsx @@ -1,12 +1,10 @@ -import React, {useEffect, useState} from 'react'; +import React, {useState} from 'react'; import {StyleSheet, Text, View} from 'react-native'; import {TouchableOpacity} from 'react-native-gesture-handler'; import {useSelector} from 'react-redux'; import {PROFILE_CUTOUT_TOP_Y} from '../../constants'; -import {BADGE_DATA} from '../../constants/badges'; -import {getSuggestedPeopleProfile} from '../../services'; import {RootState} from '../../store/rootreducer'; -import {ScreenType, UniversityBadge} from '../../types'; +import {ScreenType} from '../../types'; import {normalize} from '../../utils'; import BadgeDetailView from '../common/BadgeDetailView'; import Avatar from './Avatar'; @@ -29,7 +27,7 @@ const ProfileHeader: React.FC = ({ }) => { const { profile: {name = '', university_class = 2021, university}, - user: {userId, username: userXName = ''}, + user: {username: userXName = ''}, } = useSelector((state: RootState) => userXId ? state.userX[screenType][userXId] : state.user, ); -- cgit v1.2.3-70-g09d2