From 67a8fdb2e2f12cf9320c2915537c905f96afb547 Mon Sep 17 00:00:00 2001 From: Angie Ta Date: Wed, 19 Apr 2023 17:36:33 -0700 Subject: [PATCH 01/27] PhotoScrool styling and header styling --- src/components/ObsDetails/ActivityHeader.js | 8 ++++++-- src/components/ObsDetails/ActivityItem.js | 2 +- src/components/ObsDetails/ObsDetails.js | 4 ++-- src/components/ObsDetails/TaxonImage.js | 6 +++++- src/components/SharedComponents/PhotoScroll.js | 10 ++++++---- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/components/ObsDetails/ActivityHeader.js b/src/components/ObsDetails/ActivityHeader.js index b14b9ebab..9050b4160 100644 --- a/src/components/ObsDetails/ActivityHeader.js +++ b/src/components/ObsDetails/ActivityHeader.js @@ -2,6 +2,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { deleteComments } from "api/comments"; +import classnames from "classnames"; import { isCurrentUser } from "components/LoginSignUp/AuthenticationService"; import FlagItemModal from "components/ObsDetails/FlagItemModal"; import { Body4, INatIcon, InlineUser } from "components/SharedComponents"; @@ -26,9 +27,12 @@ type Props = { item: Object, refetchRemoteObservation?: Function, toggleRefetch?: Function, + classNameMargin?: string } -const ActivityHeader = ( { item, refetchRemoteObservation, toggleRefetch }:Props ): Node => { +const ActivityHeader = ( { + item, refetchRemoteObservation, toggleRefetch, classNameMargin +}:Props ): Node => { const [currentUser, setCurrentUser] = useState( null ); const [kebabMenuVisible, setKebabMenuVisible] = useState( false ); const [flagModalVisible, setFlagModalVisible] = useState( false ); @@ -151,7 +155,7 @@ const ActivityHeader = ( { item, refetchRemoteObservation, toggleRefetch }:Props ); return ( - + {( item._created_at ) ? diff --git a/src/components/ObsDetails/ActivityItem.js b/src/components/ObsDetails/ActivityItem.js index 61be586bb..423ef94c8 100644 --- a/src/components/ObsDetails/ActivityItem.js +++ b/src/components/ObsDetails/ActivityItem.js @@ -56,7 +56,7 @@ const ActivityItem = ( { {taxon && ( { return ( <> - {displayPhoto()} - + + {showTaxon()} diff --git a/src/components/ObsDetails/TaxonImage.js b/src/components/ObsDetails/TaxonImage.js index d8083014c..94af336d1 100644 --- a/src/components/ObsDetails/TaxonImage.js +++ b/src/components/ObsDetails/TaxonImage.js @@ -8,7 +8,11 @@ type Props = { } const TaxonImage = ( { uri }: Props ): React.Node => ( - + ); export default TaxonImage; diff --git a/src/components/SharedComponents/PhotoScroll.js b/src/components/SharedComponents/PhotoScroll.js index 0d0c32948..822a86aac 100644 --- a/src/components/SharedComponents/PhotoScroll.js +++ b/src/components/SharedComponents/PhotoScroll.js @@ -1,6 +1,6 @@ // @flow -import { Image, Text } from "components/styledComponents"; +import { Image, Text, View } from "components/styledComponents"; import * as React from "react"; import { FlatList } from "react-native-gesture-handler"; @@ -20,16 +20,18 @@ const PhotoScroll = ( { photos }: Props ): React.Node => { : photo.localFilePath; return ( - <> + {photo.licenseCode || photo.license_code} - + ); }; From 454be0edb1fe58a88d5f7dabb512766bb3882c04 Mon Sep 17 00:00:00 2001 From: Angie Ta Date: Mon, 1 May 2023 19:04:59 -0700 Subject: [PATCH 02/27] Data Tab renamed to Details Tab, styling to match designs, changed text strings, --- src/components/ObsDetails/Attribution.js | 13 +- src/components/ObsDetails/DataTab.js | 80 --------- src/components/ObsDetails/DetailsTab.js | 156 ++++++++++++++++++ src/components/ObsDetails/ObsDetails.js | 31 +++- src/components/SharedComponents/Geoprivacy.js | 35 ++++ .../SharedComponents/ObservationLocation.js | 56 +++++-- src/i18n/l10n/en.ftl | 20 ++- src/i18n/l10n/en.ftl.json | 16 +- src/i18n/strings.ftl | 20 ++- src/navigation/BottomTabNavigator/index.js | 1 + .../{DataTab.test.js => DetailsTab.test.js} | 8 +- .../components/ObsDetails/ObsDetails.test.js | 4 +- 12 files changed, 298 insertions(+), 142 deletions(-) delete mode 100644 src/components/ObsDetails/DataTab.js create mode 100644 src/components/ObsDetails/DetailsTab.js create mode 100644 src/components/SharedComponents/Geoprivacy.js rename tests/unit/components/ObsDetails/{DataTab.test.js => DetailsTab.test.js} (86%) diff --git a/src/components/ObsDetails/Attribution.js b/src/components/ObsDetails/Attribution.js index d563edfe4..45cc0d461 100644 --- a/src/components/ObsDetails/Attribution.js +++ b/src/components/ObsDetails/Attribution.js @@ -1,7 +1,8 @@ // @flow -import { Text } from "components/styledComponents"; +import { Body4 } from "components/SharedComponents"; import { t } from "i18next"; +import _ from "lodash"; import type { Node } from "react"; import React from "react"; @@ -13,25 +14,25 @@ type Props = { // https://github.com/inaturalist/inaturalist/blob/768b9263931ebeea229bbc47d8442ca6b0377d45/app/webpack/shared/components/observation_attribution.jsx const Attribution = ( { observation }: Props ): Node => { const { user } = observation; - const licenseCode = observation.license_code; + const licenseCode = _.upperCase( observation.license_code ); const copyrightAttribution = user ? ( user.name || user.login ) : t( "unknown" ); const renderLicenseCode = ( ) => { if ( !licenseCode ) { return t( "all-rights-reserved" ); } if ( licenseCode === "cc0" ) { - return t( "no-rights-reserved" ) + licenseCode; + return `${t( "no-rights-reserved" )} ${licenseCode}`; } - return t( "some-rights-reserved" ) + licenseCode; + return `${t( "some-rights-reserved" )} ${licenseCode}`; }; return ( - + {t( "Observation-Attribution", { attribution: copyrightAttribution, licenseCode: renderLicenseCode( ) } )} - + ); }; diff --git a/src/components/ObsDetails/DataTab.js b/src/components/ObsDetails/DataTab.js deleted file mode 100644 index cd687f3bd..000000000 --- a/src/components/ObsDetails/DataTab.js +++ /dev/null @@ -1,80 +0,0 @@ -// @flow - -import { - DateDisplay, - ObservationLocation -} from "components/SharedComponents"; -import Map from "components/SharedComponents/Map"; -import { Text, View } from "components/styledComponents"; -import { t } from "i18next"; -import type { Node } from "react"; -import React from "react"; -import IconMaterial from "react-native-vector-icons/MaterialIcons"; -import useIsConnected from "sharedHooks/useIsConnected"; - -import Attribution from "./Attribution"; -import checkCamelAndSnakeCase from "./helpers/checkCamelAndSnakeCase"; - -type Props = { - observation: Object -} - -const DataTab = ( { observation }: Props ): Node => { - const isOnline = useIsConnected( ); - const application = observation?.application?.name; - - return ( - <> - - {observation.description && ( - <> - {t( "Notes" )} - {observation.description} - - )} - {t( "Location" )} - - {isOnline - ? ( - - ) : ( - - - - )} - - - - - {t( "Date" )} - - - {t( "Other-Data" )} - - {application && ( - <> - {t( "This-observation-was-created-using" )} - {application} - - )} - - - ); -}; - -export default DataTab; diff --git a/src/components/ObsDetails/DetailsTab.js b/src/components/ObsDetails/DetailsTab.js new file mode 100644 index 000000000..1730b143a --- /dev/null +++ b/src/components/ObsDetails/DetailsTab.js @@ -0,0 +1,156 @@ +// @flow + +import { + Body2, + Body4, + Button, + DateDisplay, + Heading4, + ObservationLocation, + QualityGradeStatus +} from "components/SharedComponents"; +import KebabMenu from "components/SharedComponents/KebabMenu"; +import Map from "components/SharedComponents/Map"; +import { View } from "components/styledComponents"; +import { t } from "i18next"; +import _ from "lodash"; +import type { Node } from "react"; +import React, { useState } from "react"; +import { Divider, Menu } from "react-native-paper"; +import IconMaterial from "react-native-vector-icons/MaterialIcons"; +import useIsConnected from "sharedHooks/useIsConnected"; + +import Attribution from "./Attribution"; +import checkCamelAndSnakeCase from "./helpers/checkCamelAndSnakeCase"; + +type Props = { + observation: Object +} + +const headingClass = "mt-[20px] mb-[11px] text-black"; +const sectionClass = "mx-[15px] mb-[20px]"; + +const DetailsTab = ( { observation }: Props ): Node => { + const isOnline = useIsConnected( ); + const application = observation?.application?.name; + const [locationKebabMenuVisible, setLocationKebabMenuVisible] = useState( false ); + + const displayQualityGradeOption = option => { + const qualityGrade = observation?.quality_grade; + const labelClassName = ( qualityGrade === option ) ? "font-bold" : ""; + + return ( + + + {_.startCase( _.camelCase( t( option ) ) )} + + ); + }; + + return ( + <> + {observation.description && ( + <> + + {t( "NOTES" )} + {observation.description} + + + + )} + + + {t( "LOCATION" )} + + { + + }} + title={t( "Share-location" )} + /> + { + + }} + title={t( "Copy-coordinates" )} + /> + + + {isOnline + ? ( + + ) : ( + + + + )} + + + + + + + {t( "DATE" )} + + + + + + + {t( "DATA-QUALITY" )} + + + {displayQualityGradeOption( "casual" )} + {displayQualityGradeOption( "needs_id" )} + {displayQualityGradeOption( "research" )} + + + {t( "This observation needs more identifications to reach research grade" )} + +