Skip to content

Commit

Permalink
[FEAT] #38 신규 쓰레기통 상세 뷰 (이미지 업로드 제외) 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee-Sunho committed Oct 18, 2024
1 parent f67fa1d commit a8e32fe
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 19 deletions.
2 changes: 1 addition & 1 deletion app/src/assets/images/CancelSvg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Svg, {Path} from 'react-native-svg';
export default function CancelSvg({width, height, fill}: SvgProps) {
return (
<Svg width={width} height={height} viewBox="0 0 24 24" fill="none">
<Path d="M6 6L18 18M18 6L6 18" stroke="#5A5E6A" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<Path d="M6 6L18 18M18 6L6 18" stroke={fill ?? "#5A5E6A"} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</Svg>
);
}
12 changes: 0 additions & 12 deletions app/src/assets/images/reportNewBin/AddressSvg.tsx

This file was deleted.

2 changes: 2 additions & 0 deletions app/src/components/ReportNewBinNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import ReportNewBin from "screens/reportNewBin/ReportNewBin";
import ReportNewBinDetail from "screens/reportNewBinDetail/ReportNewBinDetail";

export default function ReportNewBinNavigator() {
const Stack = createNativeStackNavigator<RootReportNewBinParamList>();

return (
<Stack.Navigator initialRouteName="ReportNewBin" screenOptions={{headerShown: false}}>
<Stack.Screen name="ReportNewBin" component={ReportNewBin} />
<Stack.Screen name="ReportNewBinDetail" component={ReportNewBinDetail} />
</Stack.Navigator>
);
}
23 changes: 17 additions & 6 deletions app/src/screens/reportNewBin/ReportNewBin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
import WebView, { WebViewMessageEvent } from 'react-native-webview';
import {mapStore} from 'store/Store';
import * as S from 'screens/reportNewBin/ReportNewBin.style';
import AddressSvg from 'assets/images/reportNewBin/AddressSvg';
import { NavigationProp, useNavigation } from '@react-navigation/native';
import LocationSvg from 'assets/images/LocationSvg';
import { Palette } from 'constants/palette';

export default function ReportNewBin() {
const webViewRef = useRef<WebView>(null);
Expand All @@ -16,8 +18,10 @@ export default function ReportNewBin() {
// const URL = 'https://binvoyage.netlify.app/reportNewBin';
const URL = 'https://feature-38--binvoyage.netlify.app/reportNewBin';
const alertShown = useRef(false);
const navigation = useNavigation<NavigationProp<RootReportNewBinParamList>>();
const [markerPosition, setMarkerPosition] = useState<[number, number] | null>(null);
const [address, setAddress] = useState<string>('서울 성북구 삼선교로 16길 16-3');

const {width, height} = Dimensions.get('window');
const refreshWrapperBottom = bottomSheetOffset > 0 ? bottomSheetOffset + 10 : 40;

const animatedStyle = useAnimatedStyle(() => {
Expand Down Expand Up @@ -117,6 +121,7 @@ export default function ReportNewBin() {
const data = JSON.parse(e.nativeEvent.data);
if (data.type === 'newBinPoint') {
console.log("newBinPoint:", data.payload.latitude, data.payload.longitude);
setMarkerPosition([data.payload.latitude, data.payload.longitude]);
}
} catch (err) {
console.log(err);
Expand All @@ -141,13 +146,19 @@ export default function ReportNewBin() {
<Image source={require('assets/images/icon-refresh.png')} style={{width: 60, height: 60}} />
</TouchableOpacity>
</Animated.View>
<MyBottomSheet onSheetChange={setBottomSheetOffset} wrapperStyle={{paddingHorizontal: 16 }}>
<MyBottomSheet onSheetChange={setBottomSheetOffset} wrapperStyle={{paddingHorizontal: 16}}>
<View style={styles.view}>
<S.AddressWrapper>
<AddressSvg width='24' height='24'/>
<S.TextAddress>서울 성북구 삼선교로 16길 16-3</S.TextAddress>
<LocationSvg width="24" height="24" fill={Palette.Primary} />
<S.TextAddress>{address}</S.TextAddress>
</S.AddressWrapper>
<S.Button>
<S.Button
onPress={() =>
navigation.navigate('ReportNewBinDetail', {
address,
coordinate: markerPosition,
})
}>
<S.ButtonText>There’s a bin here!</S.ButtonText>
</S.Button>
</View>
Expand Down
133 changes: 133 additions & 0 deletions app/src/screens/reportNewBinDetail/ReportNewBinDetail.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import DefaultText from 'components/DefaultText';
import {Palette} from 'constants/palette';
import {Typo} from 'constants/typo';
import {Dimensions} from 'react-native';
import styled from 'styled-components/native';

const screenWidth = Dimensions.get('window').width;
const width = screenWidth - 32;

export const Container = styled.View`
flex: 1;
background: ${Palette.White};
padding: 10px 16px 0px;
`;

export const ArrowPrevWrapper = styled.TouchableOpacity`
width: 24px;
height: 24px;
justify-content: center;
align-items: center;
margin-bottom: 16px;
`;

export const LocationWrapper = styled.View`
flex-direction: row;
align-items: center;
gap: 3px;
padding: 10px 12px;
background: ${Palette.Gray1};
border-radius: 10px;
shadow-color: rgba(0, 0, 0, 0.2);
shadow-offset: 0px 2px;
shadow-opacity: 1;
shadow-radius: 6px;
elevation: 3;
`;

export const LocationText = styled(DefaultText)`
font-size: ${Typo.B3.fontSize};
font-weight: ${Typo.B3.fontWeight};
color: #3b3f4a;
flex-shrink: 1;
flex-wrap: wrap;
`;

export const Title = styled(DefaultText)`
font-size: ${Typo.Title1.fontSize};
font-weight: ${Typo.Title1.fontWeight};
color: ${Palette.Black};
margin-top: 22px;
margin-bottom: 26px;
`;

export const SubTitle = styled(DefaultText)`
font-size: ${Typo.SubTitle.fontSize};
font-weight: ${Typo.SubTitle.fontWeight};
color: ${Palette.Black};
margin-bottom: 12px;
`;

export const SelectWrapper = styled.View`
flex-direction: row;
row-gap: 12px;
column-gap: 16px;
flex-wrap: wrap;
`;

export const SelectItem = styled.TouchableOpacity<{isSelected: boolean}>`
padding: 10px 22px;
border-radius: 22px;
flex-shrink: 0;
background: ${props => props.isSelected ? Palette.Primary : Palette.Gray2};
`;

export const TextSelectItem = styled(DefaultText)<{isSelected: boolean}>`
font-size: ${Typo.B3.fontSize};
font-weight: ${Typo.B3.fontWeight};
color: ${props => props.isSelected ? Palette.White : Palette.Gray4};
text-align: center;
`

export const ReviewInput = styled.TextInput`
width: 100%;
height: 150px;
border-radius: 8px;
padding: 12px 14px;
background: ${Palette.Gray1};
font-size: ${Typo.B3.fontSize};
font-weight: ${Typo.B3.fontWeight};
line-height: 19px;
color: ${Palette.Black};
margin: 18px 0px 15px;
`;

export const AddPicture = styled.TouchableOpacity`
width: 100%;
height: 134px;
justify-content: center;
background: ${Palette.P100};
border-radius: 8px;
padding: 22px 15px;
`;

export const IconAddPicture = styled.ImageBackground`
width: 35px;
height: 35px;
align-self: center;
margin-bottom: 10px;
`

export const TextB3 = styled(DefaultText)`
font-size: ${Typo.B3.fontSize};
font-weight: ${Typo.B3.fontWeight};
color: ${Palette.Gray6};
text-align: center;
`;

export const Button = styled.TouchableOpacity<{isValid: boolean}>`
width: 100%;
padding: 16px 30px;
justify-content: center;
align-items: center;
background: ${props => props.isValid ? Palette.Primary : Palette.Gray3};
border-radius: 10px;
margin-bottom: 20px;
`;

export const ButtonText = styled(DefaultText)<{isValid: boolean}>`
font-size: ${Typo.Button1.fontSize};
font-weight: ${Typo.Button1.fontWeight};
color: ${props => props.isValid ? Palette.White : Palette.Gray5};
`;
122 changes: 122 additions & 0 deletions app/src/screens/reportNewBinDetail/ReportNewBinDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {NavigationProp, RouteProp, useNavigation} from '@react-navigation/native';
import api from 'api/api';
import CancelSvg from 'assets/images/CancelSvg';
import LocationSvg from 'assets/images/LocationSvg';
import {Palette} from 'constants/palette';
import {useEffect, useState} from 'react';
import {ScrollView} from 'react-native';
import Toast from 'react-native-toast-message';
import * as S from 'screens/reportNewBinDetail/ReportNewBinDetail.style';

type ReportNewBinProps = {
route: RouteProp<RootReportNewBinParamList, 'ReportNewBinDetail'>;
}

export default function ReportNewBinDetail({route}: ReportNewBinProps) {
const {address, coordinate} = route.params;
const navigation = useNavigation<NavigationProp<RootReportNewBinParamList>>();
const navigation2 = useNavigation<NavigationProp<RootHomeParamList>>();
const binTypeLabel = ['Trash', 'Recycling'];
const locationDetailLabel = ['Subway Entrance', 'Bus/Taxi Stop', 'Roadside', 'Square/Park', 'Other'];
const [selectedBinTypeLabel, setSelectedBinTypeLabel] = useState<number>(-1);
const [selectedLocationDetailLabel, setSelectedLocationDetailLabel] = useState<number>(-1);
const placeholder = `In front of a store (e.g. Olive Young) or by the bus stop? More details will help fellow wanderers!`;
const [content, setContent] = useState<string>('');
const [isValid, setIsValid] = useState<boolean>(false);

const handleSubmit = async () => {
try {
const response = await api.post(`/bin/new`, {
address: address,
lat: coordinate![0],
lng: coordinate![1],
detail: content,
type_no: selectedBinTypeLabel === 0 ? 1 : 2,
location_type_no: selectedLocationDetailLabel + 1,
image: 'https://test.test/123',
});
if (response.data.success) {
Toast.show({
type: 'success',
text1: 'Thank you for letting us know!',
position: 'bottom',
bottomOffset: 100,
visibilityTime: 2000,
});
navigation2.navigate('Home');
} else {
Toast.show({
type: 'error',
text1: 'Failed to submit. Please try again later.',
position: 'bottom',
bottomOffset: 100,
visibilityTime: 2000,
});
}
} catch (error: any) {
console.log(error);
Toast.show({
type: 'error',
text1: 'Failed to submit. Please try again later.',
position: 'bottom',
bottomOffset: 100,
visibilityTime: 2000,
});
}
}

useEffect(() => {
if (selectedBinTypeLabel !== -1 && selectedLocationDetailLabel !== -1) {
setIsValid(true);
return;
}
setIsValid(false);
}, [selectedBinTypeLabel, selectedLocationDetailLabel])

return (
<S.Container>
<S.ArrowPrevWrapper onPress={() => navigation.goBack()}>
<CancelSvg width="24" height="24" fill={Palette.Gray4} />
</S.ArrowPrevWrapper>
<ScrollView bounces={false} showsVerticalScrollIndicator={false} style={{marginBottom: 30}}>
<S.LocationWrapper>
<LocationSvg width="24" height="24" fill={Palette.Primary} />
<S.LocationText>{address}</S.LocationText>
</S.LocationWrapper>
<S.Title>{`Tell us more about the bin\nyou found`}</S.Title>
<S.SubTitle>Bin type</S.SubTitle>
<S.SelectWrapper style={{marginBottom: 24}}>
{binTypeLabel.map((item, index) => (
<S.SelectItem key={index} isSelected={selectedBinTypeLabel === index} onPress={() => setSelectedBinTypeLabel(index)}>
<S.TextSelectItem isSelected={selectedBinTypeLabel === index}>{item}</S.TextSelectItem>
</S.SelectItem>
))}
</S.SelectWrapper>
<S.SubTitle>Location details</S.SubTitle>
<S.SelectWrapper>
{locationDetailLabel.map((item, index) => (
<S.SelectItem key={index} isSelected={selectedLocationDetailLabel === index} onPress={() => setSelectedLocationDetailLabel(index)}>
<S.TextSelectItem isSelected={selectedLocationDetailLabel === index}>{item}</S.TextSelectItem>
</S.SelectItem>
))}
</S.SelectWrapper>
<S.ReviewInput
value={content}
onChangeText={setContent}
placeholder={placeholder}
placeholderTextColor={Palette.Gray4}
multiline
textAlignVertical="top"
/>
<S.AddPicture>
<S.IconAddPicture source={require('assets/images/AddImage.png')} />
<S.SubTitle style={{textAlign: 'center', marginBottom: 0}}>Upload a photo of the bin (Optional)</S.SubTitle>
<S.TextB3>You can upload only one photo!</S.TextB3>
</S.AddPicture>
</ScrollView>
<S.Button disabled={!isValid} isValid={isValid} onPress={handleSubmit}>
<S.ButtonText isValid={isValid}>Submit</S.ButtonText>
</S.Button>
</S.Container>
);
}
4 changes: 4 additions & 0 deletions app/src/types/navigator.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ type RootBinDetailParamList = {

type RootReportNewBinParamList = {
ReportNewBin: undefined;
ReportNewBinDetail: {
address: string;
coordinate: [number, number] | null; // [lat, lng]
};
}

type RootMyParamList = {
Expand Down

0 comments on commit a8e32fe

Please sign in to comment.