mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2025-12-23 22:18:36 -05:00
Merge branch 'main' into mob-722-ts
This commit is contained in:
@@ -148,7 +148,8 @@ module.exports = {
|
||||
"@typescript-eslint/consistent-type-imports": ["error", {
|
||||
fixStyle: "separate-type-imports"
|
||||
}],
|
||||
"import/consistent-type-specifier-style": ["error", "prefer-top-level"]
|
||||
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
||||
"@stylistic/member-delimiter-style": "error"
|
||||
},
|
||||
ignorePatterns: ["!.detoxrc.js", "/coverage/*", "/vendor/*", "**/flow-typed"],
|
||||
settings: {
|
||||
@@ -168,7 +169,8 @@ module.exports = {
|
||||
"@typescript-eslint/no-wrapper-object-types": "off",
|
||||
"@typescript-eslint/no-require-imports": "off",
|
||||
"@typescript-eslint/consistent-type-imports": "off",
|
||||
"import/consistent-type-specifier-style": "off"
|
||||
"import/consistent-type-specifier-style": "off",
|
||||
"@stylistic/member-delimiter-style": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,18 +13,18 @@ export default async function signIn() {
|
||||
*/
|
||||
await switchPowerMode();
|
||||
// Find the Menu item from tabs
|
||||
const openDrawerMenuItem = element( by.id( "OPEN_DRAWER" ) );
|
||||
await waitFor( openDrawerMenuItem ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await expect( openDrawerMenuItem ).toBeVisible();
|
||||
await element( by.id( "OPEN_DRAWER" ) ).tap( { x: 0, y: 0 } );
|
||||
const menuButton = element( by.id( "Menu" ) );
|
||||
await waitFor( menuButton ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await expect( menuButton ).toBeVisible();
|
||||
await element( by.id( "Menu" ) ).tap( { x: 0, y: 0 } );
|
||||
// Tap the Log-In menu item
|
||||
// TODO: consider this a temporary solution as it only checks for the drawer-top-banner
|
||||
// TODO: consider this a temporary solution as it only checks for the menu-header
|
||||
// which can be a login prompt or the logged in user's details. If the user is already
|
||||
// logged in, this should fail instead.
|
||||
const loginMenuItem = element( by.id( "drawer-top-banner" ) );
|
||||
const loginMenuItem = element( by.id( "menu-header" ) );
|
||||
await waitFor( loginMenuItem ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await expect( loginMenuItem ).toBeVisible();
|
||||
await element( by.id( "drawer-top-banner" ) ).tap();
|
||||
await element( by.id( "menu-header" ) ).tap();
|
||||
const usernameInput = element( by.id( "Login.email" ) );
|
||||
await waitFor( usernameInput ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await expect( usernameInput ).toBeVisible();
|
||||
@@ -37,7 +37,7 @@ export default async function signIn() {
|
||||
const loginButton = element( by.id( "Login.loginButton" ) );
|
||||
await expect( loginButton ).toBeVisible();
|
||||
await element( by.id( "Login.loginButton" ) ).tap();
|
||||
const username = element( by.text( `${Config.E2E_TEST_USERNAME}` ) ).atIndex( 1 );
|
||||
const username = element( by.text( `${Config.E2E_TEST_USERNAME}` ) );
|
||||
await waitFor( username ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await expect( username ).toBeVisible();
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
const TIMEOUT = 10_000;
|
||||
|
||||
export default async function switchPowerMode() {
|
||||
const drawerButton = element( by.id( "OPEN_DRAWER" ) );
|
||||
await waitFor( drawerButton ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await drawerButton.tap( { x: 0, y: 0 } );
|
||||
// Tap the settings drawer menu item
|
||||
const settingsDrawerMenuItem = element( by.id( "settings" ) );
|
||||
await waitFor( settingsDrawerMenuItem ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await settingsDrawerMenuItem.tap();
|
||||
const menuButton = element( by.id( "Menu" ) );
|
||||
await waitFor( menuButton ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await menuButton.tap( { x: 0, y: 0 } );
|
||||
// Tap the settings menu item
|
||||
const settingsMenuItem = element( by.id( "settings" ) );
|
||||
await waitFor( settingsMenuItem ).toBeVisible().withTimeout( TIMEOUT );
|
||||
await settingsMenuItem.tap();
|
||||
// Switch settings to advanced interface mode
|
||||
const advancedInterfaceSwitch = element( by.id( "advanced-interface-switch.switch" ) );
|
||||
await waitFor( advancedInterfaceSwitch ).toBeVisible().withTimeout( TIMEOUT );
|
||||
|
||||
53
package-lock.json
generated
53
package-lock.json
generated
@@ -29,7 +29,6 @@
|
||||
"@react-native-picker/picker": "^2.11.1",
|
||||
"@react-native-vector-icons/common": "^12.3.0",
|
||||
"@react-navigation/bottom-tabs": "^7.4.6",
|
||||
"@react-navigation/drawer": "^7.5.7",
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"@react-navigation/native-stack": "^7.3.25",
|
||||
@@ -6413,36 +6412,6 @@
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/drawer": {
|
||||
"version": "7.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-7.5.7.tgz",
|
||||
"integrity": "sha512-iRmeFUMZ4DPYgiuPVKsohL40flGAO0rxWwOg4iWkh0DsglI9yKpDXTUsUjmY4bMTv61jYYWV92OcSCBJjXwJoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"color": "^4.2.3",
|
||||
"react-native-drawer-layout": "^4.1.12",
|
||||
"use-latest-callback": "^0.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">= 2.0.0",
|
||||
"react-native-reanimated": ">= 2.0.0",
|
||||
"react-native-safe-area-context": ">= 4.0.0",
|
||||
"react-native-screens": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/drawer/node_modules/use-latest-callback": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.4.tgz",
|
||||
"integrity": "sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/elements": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.6.3.tgz",
|
||||
@@ -20146,28 +20115,6 @@
|
||||
"react-native-reanimated": ">=2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-drawer-layout": {
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/react-native-drawer-layout/-/react-native-drawer-layout-4.1.12.tgz",
|
||||
"integrity": "sha512-oKolvp5seiUieG+RHGjpFe8rH8Ds24iW0QBl31TlCVOX7tdn42IQIBl5tuD1i7h3q+VqqnbcT+NB2dcJ5suZkw==",
|
||||
"dependencies": {
|
||||
"use-latest-callback": "^0.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 18.2.0",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">= 2.0.0",
|
||||
"react-native-reanimated": ">= 2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-drawer-layout/node_modules/use-latest-callback": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.4.tgz",
|
||||
"integrity": "sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-event-listeners": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-event-listeners/-/react-native-event-listeners-1.0.7.tgz",
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
"@react-native-picker/picker": "^2.11.1",
|
||||
"@react-native-vector-icons/common": "^12.3.0",
|
||||
"@react-navigation/bottom-tabs": "^7.4.6",
|
||||
"@react-navigation/drawer": "^7.5.7",
|
||||
"@react-navigation/elements": "^2.6.3",
|
||||
"@react-navigation/native": "^7.1.17",
|
||||
"@react-navigation/native-stack": "^7.3.25",
|
||||
|
||||
@@ -66,10 +66,10 @@ export interface ErrorWithResponse {
|
||||
json: () => Promise<{
|
||||
status: string;
|
||||
errors: Array<{
|
||||
errorCode: string,
|
||||
message: string,
|
||||
from: string | null,
|
||||
stack: string | null,
|
||||
errorCode: string;
|
||||
message: string;
|
||||
from: string | null;
|
||||
stack: string | null;
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ const api = create( {
|
||||
}
|
||||
} );
|
||||
|
||||
function isError( error: { message?: string, stack?: string } ) {
|
||||
function isError( error: { message?: string; stack?: string } ) {
|
||||
if ( error instanceof Error ) return true;
|
||||
if ( error?.stack && error?.message ) return true;
|
||||
return false;
|
||||
|
||||
@@ -20,12 +20,12 @@ interface SearchResponse extends ApiResponse {
|
||||
project?: ApiProject;
|
||||
taxon?: ApiTaxon;
|
||||
user?: ApiUser;
|
||||
}[]
|
||||
}[];
|
||||
}
|
||||
|
||||
interface SearchParams extends ApiParams {
|
||||
q?: string;
|
||||
sources?: string | string[]
|
||||
sources?: string | string[];
|
||||
}
|
||||
|
||||
const PARAMS: ApiParams = {
|
||||
|
||||
2
src/api/types.d.ts
vendored
2
src/api/types.d.ts
vendored
@@ -153,7 +153,7 @@ export interface ApiSuggestion {
|
||||
}
|
||||
|
||||
export interface ApiObservationsSearchResponse extends ApiResponse {
|
||||
results: ApiObservation[]
|
||||
results: ApiObservation[];
|
||||
}
|
||||
|
||||
export const ORDER_BY_CREATED_AT = "created_at";
|
||||
|
||||
@@ -12,18 +12,18 @@ import colors from "styles/tailwindColors";
|
||||
interface Props {
|
||||
closeBottomSheet: ( ) => void;
|
||||
navAndCloseBottomSheet: ( screen: string, params?: {
|
||||
camera?: string
|
||||
camera?: string;
|
||||
} ) => void;
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
type ObsCreateItem = {
|
||||
text: string,
|
||||
icon: string,
|
||||
onPress: ( ) => void,
|
||||
testID: string,
|
||||
accessibilityLabel: string,
|
||||
accessibilityHint: string
|
||||
text: string;
|
||||
icon: string;
|
||||
onPress: ( ) => void;
|
||||
testID: string;
|
||||
accessibilityLabel: string;
|
||||
accessibilityHint: string;
|
||||
}
|
||||
|
||||
const majorVersionIOS = parseInt( String( Platform.Version ), 10 );
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import RootDrawerNavigator from "navigation/rootDrawerNavigator";
|
||||
import RootStackNavigator from "navigation/RootStackNavigator";
|
||||
import type { Node } from "react";
|
||||
import React, { useCallback } from "react";
|
||||
import { log } from "sharedHelpers/logger";
|
||||
@@ -80,7 +80,7 @@ const App = ( { children }: Props ): Node => {
|
||||
<StartupService />
|
||||
<NetworkService />
|
||||
<AppStateListener />
|
||||
{children || <RootDrawerNavigator />}
|
||||
{children || <RootStackNavigator />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -231,7 +231,12 @@ const AICamera = ( {
|
||||
|
||||
const handleClose = async ( ) => {
|
||||
await deleteSentinelFile( sentinelFileName );
|
||||
navigation.goBack( );
|
||||
navigation.navigate( "TabNavigator", {
|
||||
screen: "ObservationsTab",
|
||||
params: {
|
||||
screen: "ObsList"
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -32,20 +32,20 @@ Reanimated.addWhitelistedNativeProps( {
|
||||
} );
|
||||
|
||||
interface Props {
|
||||
animatedProps: CameraProps,
|
||||
cameraRef: React.RefObject<Camera | null>,
|
||||
cameraScreen: "standard" | "ai",
|
||||
debugFormat: CameraDeviceFormat | undefined,
|
||||
device: CameraDevice,
|
||||
frameProcessor?: () => void,
|
||||
onCameraError: ( error: CameraRuntimeError ) => void,
|
||||
onCaptureError: ( error: CameraRuntimeError ) => void,
|
||||
onClassifierError: ( error: CameraRuntimeError ) => void,
|
||||
onDeviceNotSupported: ( error: CameraRuntimeError ) => void,
|
||||
panToZoom: PanGesture,
|
||||
pinchToZoom: PinchGesture,
|
||||
resizeMode?: "cover" | "contain",
|
||||
inactive?: boolean
|
||||
animatedProps: CameraProps;
|
||||
cameraRef: React.RefObject<Camera | null>;
|
||||
cameraScreen: "standard" | "ai";
|
||||
debugFormat: CameraDeviceFormat | undefined;
|
||||
device: CameraDevice;
|
||||
frameProcessor?: () => void;
|
||||
onCameraError: ( error: CameraRuntimeError ) => void;
|
||||
onCaptureError: ( error: CameraRuntimeError ) => void;
|
||||
onClassifierError: ( error: CameraRuntimeError ) => void;
|
||||
onDeviceNotSupported: ( error: CameraRuntimeError ) => void;
|
||||
panToZoom: PanGesture;
|
||||
pinchToZoom: PinchGesture;
|
||||
resizeMode?: "cover" | "contain";
|
||||
inactive?: boolean;
|
||||
}
|
||||
|
||||
// A container for the Camera component
|
||||
|
||||
@@ -11,23 +11,23 @@ import StandardCamera from "./StandardCamera/StandardCamera";
|
||||
const isTablet = DeviceInfo.isTablet( );
|
||||
|
||||
interface Props {
|
||||
cameraType: string,
|
||||
device: CameraDevice,
|
||||
camera: object,
|
||||
flipCamera: ( ) => void,
|
||||
handleCheckmarkPress: ( ) => void,
|
||||
cameraType: string;
|
||||
device: CameraDevice;
|
||||
camera: object;
|
||||
flipCamera: ( ) => void;
|
||||
handleCheckmarkPress: ( ) => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
toggleFlash: Function,
|
||||
takingPhoto: boolean,
|
||||
toggleFlash: Function;
|
||||
takingPhoto: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
takePhotoAndStoreUri: Function,
|
||||
newPhotoUris: Array<object>,
|
||||
takePhotoAndStoreUri: Function;
|
||||
newPhotoUris: Array<object>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
setNewPhotoUris: Function,
|
||||
takePhotoOptions: object,
|
||||
userLocation: UserLocation | null,
|
||||
hasLocationPermissions: boolean,
|
||||
requestLocationPermissions: () => void,
|
||||
setNewPhotoUris: Function;
|
||||
takePhotoOptions: object;
|
||||
userLocation: UserLocation | null;
|
||||
hasLocationPermissions: boolean;
|
||||
requestLocationPermissions: () => void;
|
||||
}
|
||||
|
||||
const CameraWithDevice = ( {
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Animated } from "react-native";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props {
|
||||
takingPhoto: boolean,
|
||||
cameraType: string
|
||||
takingPhoto: boolean;
|
||||
cameraType: string;
|
||||
}
|
||||
|
||||
const fade = value => ( {
|
||||
|
||||
@@ -4,7 +4,7 @@ import React from "react";
|
||||
import { Animated } from "react-native";
|
||||
|
||||
interface Props {
|
||||
animatedStyle: object
|
||||
animatedStyle: object;
|
||||
}
|
||||
|
||||
const FocusSquare = ( { animatedStyle }: Props ) => {
|
||||
|
||||
@@ -75,7 +75,7 @@ const boldClassname = ( line: string, isDirectory = false ) => classnames(
|
||||
|
||||
interface DirectorySizesProps {
|
||||
directoryName: string;
|
||||
directoryEntrySizes: DirectoryEntrySize[]
|
||||
directoryEntrySizes: DirectoryEntrySize[];
|
||||
}
|
||||
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
|
||||
@@ -121,7 +121,7 @@ export function getTotalDirectorySize( directoryItems: DirectoryEntrySize[] ): n
|
||||
}
|
||||
|
||||
type AppSize = {
|
||||
[directoryName: string]: DirectoryEntrySize[]
|
||||
[directoryName: string]: DirectoryEntrySize[];
|
||||
}
|
||||
|
||||
async function fetchAppSize(): Promise<AppSize> {
|
||||
|
||||
@@ -50,19 +50,19 @@ const centeredLoadingWheel = {
|
||||
|
||||
interface Props {
|
||||
// Bounding box of the observations retrieved for the query params
|
||||
observationBounds?: MapBoundaries,
|
||||
observationBounds?: MapBoundaries;
|
||||
queryParams: {
|
||||
taxon_id?: number;
|
||||
return_bounds?: boolean;
|
||||
order?: string;
|
||||
orderBy?: string;
|
||||
};
|
||||
isLoading: boolean,
|
||||
hasLocationPermissions?: boolean,
|
||||
isLoading: boolean;
|
||||
hasLocationPermissions?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
renderLocationPermissionsGate: Function,
|
||||
renderLocationPermissionsGate: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
requestLocationPermissions: Function
|
||||
requestLocationPermissions: Function;
|
||||
}
|
||||
|
||||
const MapView = ( {
|
||||
|
||||
@@ -21,8 +21,8 @@ const DROP_SHADOW = getShadow( {
|
||||
} );
|
||||
|
||||
type Props = {
|
||||
closeModal: ( ) => void,
|
||||
updateProject: ( project: ApiProject ) => void
|
||||
closeModal: ( ) => void;
|
||||
updateProject: ( project: ApiProject ) => void;
|
||||
};
|
||||
|
||||
const ExploreProjectSearch = ( { closeModal, updateProject }: Props ) => {
|
||||
|
||||
@@ -62,14 +62,14 @@ type FullPageWebViewParams = {
|
||||
}
|
||||
|
||||
type ParamList = {
|
||||
FullPageWebView: FullPageWebViewParams
|
||||
FullPageWebView: FullPageWebViewParams;
|
||||
}
|
||||
|
||||
type WebViewSource = {
|
||||
uri: string;
|
||||
headers?: {
|
||||
Authorization?: string | null
|
||||
}
|
||||
Authorization?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export function onShouldStartLoadWithRequest(
|
||||
|
||||
@@ -187,9 +187,9 @@ const getUsername = async (): Promise<string> => getSensitiveItem( "username" );
|
||||
*/
|
||||
const signOut = async (
|
||||
options: {
|
||||
realm?: Realm,
|
||||
clearRealm?: boolean,
|
||||
queryClient?: QueryClient
|
||||
realm?: Realm;
|
||||
clearRealm?: boolean;
|
||||
queryClient?: QueryClient;
|
||||
} = {
|
||||
clearRealm: false,
|
||||
queryClient: undefined
|
||||
@@ -579,7 +579,7 @@ async function authenticateUserByAssertion(
|
||||
}
|
||||
|
||||
interface CreateUserResponse {
|
||||
errors?: string[]
|
||||
errors?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@ import LoginSignUpWrapper from "./LoginSignUpWrapper";
|
||||
|
||||
type RenderProps = {
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
scrollViewRef: { current: null | React.Ref<typeof ScrollView> }
|
||||
scrollViewRef: { current: null | React.Ref<typeof ScrollView> };
|
||||
};
|
||||
|
||||
const ForgotPassword = ( ) => {
|
||||
|
||||
@@ -16,8 +16,8 @@ import useKeyboardInfo from "sharedHooks/useKeyboardInfo";
|
||||
import LoginSignUpInputField from "./LoginSignUpInputField";
|
||||
|
||||
type Props = {
|
||||
reset: ( email: string ) => Promise<void>,
|
||||
scrollViewRef?: { current: null | ElementRef<typeof ScrollView> },
|
||||
reset: ( email: string ) => Promise<void>;
|
||||
scrollViewRef?: { current: null | ElementRef<typeof ScrollView> };
|
||||
}
|
||||
|
||||
const ForgotPasswordForm = ( { reset, scrollViewRef }: Props ): Node => {
|
||||
|
||||
@@ -28,7 +28,7 @@ import LoginSignUpInputField from "./LoginSignUpInputField";
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
interface Props {
|
||||
scrollViewRef?: React.Ref
|
||||
scrollViewRef?: React.Ref;
|
||||
}
|
||||
|
||||
interface LoginFormParams {
|
||||
@@ -38,7 +38,7 @@ interface LoginFormParams {
|
||||
}
|
||||
|
||||
type ParamList = {
|
||||
LoginFormParams: LoginFormParams
|
||||
LoginFormParams: LoginFormParams;
|
||||
}
|
||||
|
||||
const LoginForm = ( {
|
||||
|
||||
@@ -18,8 +18,8 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props extends PropsWithChildren {
|
||||
backgroundSource: ImageSourcePropType,
|
||||
imageStyle?: StyleProp<ImageStyle>
|
||||
backgroundSource: ImageSourcePropType;
|
||||
imageStyle?: StyleProp<ImageStyle>;
|
||||
}
|
||||
|
||||
const windowHeight = Dimensions.get( "window" ).height;
|
||||
|
||||
@@ -15,12 +15,12 @@ import {
|
||||
} from "sharedHooks";
|
||||
|
||||
type Props = {
|
||||
confidence: number | null,
|
||||
handlePress?: ( ) => void,
|
||||
taxon: RealmTaxon | ApiTaxon,
|
||||
testID?: string,
|
||||
updateMaxHeight?: ( height: number ) => void,
|
||||
forcedHeight: number
|
||||
confidence: number | null;
|
||||
handlePress?: ( ) => void;
|
||||
taxon: RealmTaxon | ApiTaxon;
|
||||
testID?: string;
|
||||
updateMaxHeight?: ( height: number ) => void;
|
||||
forcedHeight: number;
|
||||
}
|
||||
|
||||
const SuggestionsResult = ( {
|
||||
|
||||
@@ -18,7 +18,7 @@ const LIKELY_CONFIDENCE_THRESHOLD = 50;
|
||||
|
||||
interface Props {
|
||||
topSuggestion?: ApiSuggestion;
|
||||
hideObservationStatus?: boolean
|
||||
hideObservationStatus?: boolean;
|
||||
}
|
||||
|
||||
const MatchHeader = ( { topSuggestion, hideObservationStatus }: Props ) => {
|
||||
|
||||
@@ -62,7 +62,7 @@ const MatchTaxonSearchScreen = ( ) => {
|
||||
// no-unused-prop-types failing for components defined at runtime seems to
|
||||
// be a bug. These props are clearly used
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
( { item: taxon, index }: { item: ApiTaxon, index: number } ) => (
|
||||
( { item: taxon, index }: { item: ApiTaxon; index: number } ) => (
|
||||
<TaxonResult
|
||||
accessibilityLabel={t( "Choose-taxon" )}
|
||||
fetchRemote={false}
|
||||
|
||||
@@ -12,9 +12,9 @@ const MIN_SCALE = 0.5;
|
||||
const MAX_SCALE = 5;
|
||||
|
||||
interface Props {
|
||||
uri: string
|
||||
setZooming: ( ) => void,
|
||||
selectedMediaIndex: number
|
||||
uri: string;
|
||||
setZooming: ( ) => void;
|
||||
selectedMediaIndex: number;
|
||||
}
|
||||
|
||||
const CustomImageZoom = ( {
|
||||
|
||||
282
src/components/Menu/Menu.tsx
Normal file
282
src/components/Menu/Menu.tsx
Normal file
@@ -0,0 +1,282 @@
|
||||
import { useNetInfo } from "@react-native-community/netinfo";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
signOut
|
||||
} from "components/LoginSignUp/AuthenticationService";
|
||||
import {
|
||||
Body1,
|
||||
INatIcon,
|
||||
List2, TextInputSheet,
|
||||
UserIcon,
|
||||
WarningSheet
|
||||
} from "components/SharedComponents";
|
||||
import { Pressable, ScrollView, View } from "components/styledComponents";
|
||||
import { RealmContext } from "providers/contexts";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { Alert } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import User from "realmModels/User";
|
||||
import { log } from "sharedHelpers/logger";
|
||||
import { useCurrentUser, useTranslation } from "sharedHooks";
|
||||
import useStore, { zustandStorage } from "stores/useStore";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
import MenuItem from "./MenuItem";
|
||||
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
function isDefaultMode( ) {
|
||||
return useStore.getState( ).layout.isDefaultMode === true;
|
||||
}
|
||||
|
||||
interface BaseMenuOption {
|
||||
label: string;
|
||||
icon: string;
|
||||
color?: string;
|
||||
testID?: string;
|
||||
isLogout?: boolean;
|
||||
}
|
||||
|
||||
interface MenuOptionWithNavigation extends BaseMenuOption {
|
||||
navigation: string;
|
||||
onPress?: never;
|
||||
}
|
||||
|
||||
interface MenuOptionWithOnPress extends BaseMenuOption {
|
||||
onPress: ( ) => void;
|
||||
navigation?: never;
|
||||
}
|
||||
|
||||
export type MenuOption = MenuOptionWithNavigation | MenuOptionWithOnPress;
|
||||
|
||||
export enum MenuModalState {
|
||||
ConfirmLogout = "confirmLogout",
|
||||
ProvideFeedback = "provideFeedback"
|
||||
}
|
||||
|
||||
const feedbackLogger = log.extend( "feedback" );
|
||||
|
||||
function showOfflineAlert( t: ( _: string ) => string ) {
|
||||
Alert.alert( t( "You-are-offline" ), t( "Please-try-again-when-you-are-online" ) );
|
||||
}
|
||||
|
||||
const Menu = ( ) => {
|
||||
const isDebug = zustandStorage.getItem( "debugMode" ) === "true";
|
||||
const realm = useRealm( );
|
||||
const navigation = useNavigation( );
|
||||
const queryClient = useQueryClient( );
|
||||
const currentUser = useCurrentUser( );
|
||||
const { t } = useTranslation( );
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const { isConnected } = useNetInfo( );
|
||||
|
||||
const [modalState, setModalState] = useState<MenuModalState | null>( null );
|
||||
|
||||
const menuItems: Record<string, MenuOption> = {
|
||||
projects: {
|
||||
label: t( "PROJECTS" ),
|
||||
navigation: "Projects",
|
||||
icon: "briefcase"
|
||||
},
|
||||
about: {
|
||||
label: t( "ABOUT" ),
|
||||
navigation: "About",
|
||||
icon: "inaturalist"
|
||||
},
|
||||
donate: {
|
||||
label: t( "DONATE" ),
|
||||
navigation: "Donate",
|
||||
icon: "heart",
|
||||
color: colors.inatGreen
|
||||
},
|
||||
help: {
|
||||
label: t( "HELP" ),
|
||||
navigation: "Help",
|
||||
icon: "help-circle"
|
||||
},
|
||||
settings: {
|
||||
testID: "settings",
|
||||
label: t( "SETTINGS" ),
|
||||
navigation: "Settings",
|
||||
icon: "gear"
|
||||
},
|
||||
|
||||
feedback: {
|
||||
label: t( "FEEDBACK" ),
|
||||
icon: "feedback",
|
||||
onPress: () => {
|
||||
if ( isConnected ) {
|
||||
setModalState( MenuModalState.ProvideFeedback );
|
||||
} else {
|
||||
showOfflineAlert( t );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
...( currentUser
|
||||
? {
|
||||
logout: {
|
||||
label: t( "LOG-OUT" ),
|
||||
icon: "door-exit",
|
||||
onPress: () => setModalState( MenuModalState.ConfirmLogout ),
|
||||
isLogout: true
|
||||
}
|
||||
}
|
||||
: {
|
||||
login: {
|
||||
label: t( "LOG-IN" ),
|
||||
icon: "door-enter",
|
||||
color: colors.inatGreen,
|
||||
onPress: () => navigation.navigate( "LoginStackNavigator" )
|
||||
}
|
||||
} ),
|
||||
|
||||
...( isDebug
|
||||
? {
|
||||
debug: {
|
||||
label: "DEBUG",
|
||||
navigation: "Debug",
|
||||
icon: "triangle-exclamation",
|
||||
color: "deeppink"
|
||||
}
|
||||
}
|
||||
: {} )
|
||||
};
|
||||
|
||||
const onSignOut = async ( ) => {
|
||||
await signOut( { realm, clearRealm: true, queryClient } );
|
||||
setModalState( null );
|
||||
|
||||
// TODO might be necessary to restart the app at this point. We just
|
||||
// deleted the realm file on disk, but the RealmProvider may still have a
|
||||
// copy of realm in local state
|
||||
navigation.goBack( );
|
||||
};
|
||||
|
||||
const onSubmitFeedback = useCallback( ( text: string ) => {
|
||||
if ( !isConnected ) {
|
||||
showOfflineAlert( t );
|
||||
return false;
|
||||
}
|
||||
const mode = isDefaultMode( )
|
||||
? "DEFAULT:"
|
||||
: "ADVANCED:";
|
||||
feedbackLogger.info( mode, text );
|
||||
Alert.alert( t( "Feedback-Submitted" ), t( "Thank-you-for-sharing-your-feedback" ) );
|
||||
setModalState( null );
|
||||
return true;
|
||||
}, [isConnected, t] );
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
bounces={false}
|
||||
className="bg-white h-full"
|
||||
style={{ paddingTop: insets.top }}
|
||||
>
|
||||
<View>
|
||||
{/* Header */}
|
||||
<Pressable
|
||||
testID="menu-header"
|
||||
accessible
|
||||
accessibilityRole="button"
|
||||
accessibilityHint={
|
||||
currentUser
|
||||
? t( "Navigates-to-user-profile" )
|
||||
: t( "Navigates-to-log-in-screen" )
|
||||
}
|
||||
className="px-[26px] pt-[68px] pb-[31px] border-b border-lightGray"
|
||||
onPress={( ) => {
|
||||
if ( !currentUser ) {
|
||||
navigation.navigate( "LoginStackNavigator" );
|
||||
} else {
|
||||
navigation.navigate( "TabNavigator", {
|
||||
screen: "ObservationsTab",
|
||||
params: {
|
||||
screen: "UserProfile",
|
||||
params: { userId: currentUser.id }
|
||||
}
|
||||
} );
|
||||
}
|
||||
}}
|
||||
>
|
||||
<View className="flex-row">
|
||||
{currentUser
|
||||
? (
|
||||
<UserIcon
|
||||
uri={User.uri( currentUser )}
|
||||
medium
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<INatIcon
|
||||
name="inaturalist"
|
||||
color={colors.inatGreen}
|
||||
size={62}
|
||||
/>
|
||||
) }
|
||||
<View className="ml-5 justify-center">
|
||||
<Body1>
|
||||
{currentUser
|
||||
? currentUser?.login
|
||||
: t( "Log-in-to-iNaturalist" )}
|
||||
</Body1>
|
||||
{currentUser && (
|
||||
<List2>
|
||||
{t( "X-Observations", { count: currentUser.observations_count } )}
|
||||
</List2>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
{/* Menu Items */}
|
||||
<View>
|
||||
{Object.entries( menuItems ).map( ( [key, item] ) => (
|
||||
<MenuItem
|
||||
key={key}
|
||||
item={item}
|
||||
onPress={() => {
|
||||
if ( item.navigation ) {
|
||||
navigation.navigate( "TabNavigator", {
|
||||
screen: "MenuTab",
|
||||
params: {
|
||||
screen: menuItems[key].navigation
|
||||
}
|
||||
} );
|
||||
}
|
||||
item.onPress?.();
|
||||
}}
|
||||
/>
|
||||
) )}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{modalState === MenuModalState.ConfirmLogout && (
|
||||
<WarningSheet
|
||||
onPressClose={() => setModalState( null )}
|
||||
headerText={t( "LOG-OUT--question" )}
|
||||
text={t( "Are-you-sure-you-want-to-log-out" )}
|
||||
handleSecondButtonPress={() => setModalState( null )}
|
||||
secondButtonText={t( "CANCEL" )}
|
||||
confirm={onSignOut}
|
||||
buttonText={t( "LOG-OUT" )}
|
||||
loading={false}
|
||||
/>
|
||||
)}
|
||||
{modalState === MenuModalState.ProvideFeedback && (
|
||||
<TextInputSheet
|
||||
buttonText={t( "SUBMIT" )}
|
||||
onPressClose={() => setModalState( null )}
|
||||
headerText={t( "FEEDBACK" )}
|
||||
confirm={onSubmitFeedback}
|
||||
description={t( "Thanks-for-using-any-suggestions" )}
|
||||
maxLength={1000}
|
||||
/>
|
||||
)}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
34
src/components/Menu/MenuItem.tsx
Normal file
34
src/components/Menu/MenuItem.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import classNames from "classnames";
|
||||
import { Heading4, INatIcon } from "components/SharedComponents";
|
||||
import { Pressable, View } from "components/styledComponents";
|
||||
import React from "react";
|
||||
|
||||
import type { MenuOption } from "./Menu";
|
||||
|
||||
const MenuItem = ( {
|
||||
item,
|
||||
onPress
|
||||
}: {
|
||||
item: MenuOption;
|
||||
onPress: ( ) => void;
|
||||
} ) => (
|
||||
<Pressable
|
||||
testID={item.testID}
|
||||
className={classNames(
|
||||
item.isLogout
|
||||
? "opacity-50"
|
||||
: "",
|
||||
"flex-row items-center pl-10 py-[22px] border-b border-lightGray"
|
||||
)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={item.label}
|
||||
onPress={onPress}
|
||||
>
|
||||
<View className="mr-5">
|
||||
<INatIcon name={item.icon} size={22} color={item.color} />
|
||||
</View>
|
||||
<Heading4>{item.label}</Heading4>
|
||||
</Pressable>
|
||||
);
|
||||
|
||||
export default MenuItem;
|
||||
@@ -42,8 +42,8 @@ import MyObservationsSimple, {
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
interface SpeciesCount {
|
||||
count: number,
|
||||
taxon: RealmTaxon
|
||||
count: number;
|
||||
taxon: RealmTaxon;
|
||||
}
|
||||
|
||||
interface SyncOptions {
|
||||
@@ -243,9 +243,9 @@ const MyObservationsContainer = ( ): React.FC => {
|
||||
const onScroll = ( scrollEvent: {
|
||||
nativeEvent: {
|
||||
contentOffset: {
|
||||
y: number
|
||||
}
|
||||
}
|
||||
y: number;
|
||||
};
|
||||
};
|
||||
} ) => setMyObsOffset( scrollEvent.nativeEvent.contentOffset.y );
|
||||
|
||||
const numOfUserObservations = zustandStorage.getItem( "numOfUserObservations" );
|
||||
|
||||
@@ -38,8 +38,8 @@ import SimpleTaxonGridItem from "./SimpleTaxonGridItem";
|
||||
import StatTab from "./StatTab";
|
||||
|
||||
interface SpeciesCount {
|
||||
count: number,
|
||||
taxon: RealmTaxon
|
||||
count: number;
|
||||
taxon: RealmTaxon;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
|
||||
@@ -17,15 +17,15 @@ const imageClassNames = [
|
||||
];
|
||||
|
||||
interface SpeciesCount {
|
||||
count: number,
|
||||
taxon: RealmTaxon
|
||||
count: number;
|
||||
taxon: RealmTaxon;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
accessibleName: string;
|
||||
navToTaxonDetails: ( ) => void;
|
||||
source: {
|
||||
uri: string
|
||||
uri: string;
|
||||
};
|
||||
style?: object;
|
||||
speciesCount: SpeciesCount;
|
||||
|
||||
@@ -14,16 +14,16 @@ import { useTranslation } from "sharedHooks";
|
||||
import type { Notification } from "sharedHooks/useInfiniteNotificationsScroll";
|
||||
|
||||
type Props = {
|
||||
currentUser: RealmUser | null,
|
||||
data: Notification[],
|
||||
isError?: boolean,
|
||||
isFetching?: boolean,
|
||||
isInitialLoading?: boolean,
|
||||
isConnected: boolean | null,
|
||||
onEndReached: ( ) => void,
|
||||
onRefresh: ( ) => void,
|
||||
refreshing: boolean,
|
||||
reload: ( ) => void
|
||||
currentUser: RealmUser | null;
|
||||
data: Notification[];
|
||||
isError?: boolean;
|
||||
isFetching?: boolean;
|
||||
isInitialLoading?: boolean;
|
||||
isConnected: boolean | null;
|
||||
onEndReached: ( ) => void;
|
||||
onRefresh: ( ) => void;
|
||||
refreshing: boolean;
|
||||
reload: ( ) => void;
|
||||
};
|
||||
|
||||
interface RenderItemProps {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { Notification } from "sharedHooks/useInfiniteNotificationsScroll";
|
||||
import { OBS_DETAILS_TAB } from "stores/createLayoutSlice";
|
||||
|
||||
type Props = {
|
||||
notification: Notification
|
||||
notification: Notification;
|
||||
};
|
||||
|
||||
const NotificationsListItem = ( { notification }: Props ) => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { Notification } from "sharedHooks/useInfiniteNotificationsScroll";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props {
|
||||
notification: Notification
|
||||
notification: Notification;
|
||||
}
|
||||
|
||||
const ObsNotification = ( { notification }: Props ) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useTranslation } from "sharedHooks";
|
||||
import type { Notification } from "sharedHooks/useInfiniteNotificationsScroll";
|
||||
|
||||
interface Props {
|
||||
notification: Notification
|
||||
notification: Notification;
|
||||
}
|
||||
|
||||
const ObsNotificationText = ( { notification }: Props ) => {
|
||||
|
||||
@@ -10,15 +10,15 @@ interface Props {
|
||||
preferred_common_name?: string;
|
||||
rank: string;
|
||||
rank_level: number;
|
||||
},
|
||||
username: string,
|
||||
withdrawn?: boolean
|
||||
};
|
||||
username: string;
|
||||
withdrawn?: boolean;
|
||||
}
|
||||
|
||||
// TODO replace when we've properly typed Realm object
|
||||
interface User {
|
||||
prefers_common_names?: boolean;
|
||||
prefers_scientific_name_first?: boolean
|
||||
prefers_scientific_name_first?: boolean;
|
||||
}
|
||||
|
||||
const DisagreementText = ( { taxon, username, withdrawn }: Props ) => {
|
||||
|
||||
@@ -19,7 +19,7 @@ import DetailsMapHeader from "./DetailsMapHeader";
|
||||
import ObscurationExplanation from "./ObscurationExplanation";
|
||||
|
||||
interface Props {
|
||||
observation: Observation
|
||||
observation: Observation;
|
||||
}
|
||||
|
||||
const DETAILS_MAP_MODAL_STYLE = { margin: 0 };
|
||||
|
||||
@@ -20,7 +20,7 @@ interface Props {
|
||||
non_traditional_projects: Array<{
|
||||
project: object;
|
||||
}>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const ProjectSection = ( { observation }: Props ) => {
|
||||
|
||||
@@ -12,10 +12,10 @@ import type { RealmTaxon } from "realmModels/types";
|
||||
import { useCurrentUser, useTranslation } from "sharedHooks";
|
||||
|
||||
interface Props {
|
||||
onPressClose: () => void,
|
||||
onPressClose: () => void;
|
||||
onPotentialDisagreePressed: ( _checkedValue: string ) => void;
|
||||
newTaxon: RealmTaxon | ApiTaxon,
|
||||
oldTaxon: RealmTaxon | ApiTaxon
|
||||
newTaxon: RealmTaxon | ApiTaxon;
|
||||
oldTaxon: RealmTaxon | ApiTaxon;
|
||||
}
|
||||
|
||||
const PotentialDisagreementSheet = ( {
|
||||
|
||||
@@ -11,17 +11,17 @@ import type { Node } from "react";
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
hidden?: boolean,
|
||||
hidden?: boolean;
|
||||
identification: {
|
||||
body?: string,
|
||||
taxon: { id: number }
|
||||
}
|
||||
body?: string;
|
||||
taxon: { id: number };
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
onSuggestId:Function,
|
||||
onSuggestId:Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
editIdentBody: Function,
|
||||
editIdentBody: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
onPressClose: Function
|
||||
onPressClose: Function;
|
||||
}
|
||||
|
||||
const SuggestIDSheet = ( {
|
||||
|
||||
@@ -11,15 +11,15 @@ interface Props {
|
||||
preferred_common_name?: string;
|
||||
rank: string;
|
||||
rank_level: number;
|
||||
},
|
||||
username: string,
|
||||
withdrawn?: boolean
|
||||
};
|
||||
username: string;
|
||||
withdrawn?: boolean;
|
||||
}
|
||||
|
||||
// TODO replace when we've properly typed Realm object
|
||||
interface User {
|
||||
prefers_common_names?: boolean;
|
||||
prefers_scientific_name_first?: boolean
|
||||
prefers_scientific_name_first?: boolean;
|
||||
}
|
||||
|
||||
const DisagreementText = ( { taxon, username, withdrawn }: Props ) => {
|
||||
|
||||
@@ -13,8 +13,8 @@ import { useCurrentUser } from "sharedHooks";
|
||||
import ObscurationExplanation from "./ObscurationExplanation";
|
||||
|
||||
interface Props {
|
||||
belongsToCurrentUser: boolean,
|
||||
observation: RealmObservation
|
||||
belongsToCurrentUser: boolean;
|
||||
observation: RealmObservation;
|
||||
}
|
||||
|
||||
const LocationSection = ( {
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from "react";
|
||||
import { useCurrentUser } from "sharedHooks";
|
||||
|
||||
interface Props {
|
||||
observationUUID: string
|
||||
observationUUID: string;
|
||||
}
|
||||
|
||||
const DQAButton = ( { observationUUID }: Props ) => {
|
||||
|
||||
@@ -12,7 +12,7 @@ interface Props {
|
||||
non_traditional_projects: Array<{
|
||||
project: object;
|
||||
}>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const ProjectButton = ( { observation }: Props ) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import React from "react";
|
||||
import { Alert, Platform, Share } from "react-native";
|
||||
|
||||
type Props = {
|
||||
id: number
|
||||
id: number;
|
||||
}
|
||||
|
||||
const OBSERVATION_URL = "https://www.inaturalist.org/observations";
|
||||
|
||||
@@ -27,22 +27,22 @@ import StatusSection from "./StatusSection/StatusSection";
|
||||
const cardClassBottom = "rounded-b-2xl border-lightGray border-[2px] pb-3 border-t-0 -mt-0.5 mb-4";
|
||||
|
||||
type Props = {
|
||||
activityItems: Array<object>,
|
||||
addingActivityItem: boolean,
|
||||
belongsToCurrentUser: boolean,
|
||||
currentUser: RealmUser,
|
||||
isConnected: boolean,
|
||||
navToSuggestions: () => void,
|
||||
observation: RealmObservation & Observation & { id: number },
|
||||
openAddCommentSheet: () => void,
|
||||
openAgreeWithIdSheet: () => void,
|
||||
refetchRemoteObservation: () => void,
|
||||
refetchSubscriptions: () => void,
|
||||
showAddCommentSheet: () => void,
|
||||
subscriptions: object,
|
||||
targetActivityItemID: number,
|
||||
wasSynced: boolean,
|
||||
uuid: string
|
||||
activityItems: Array<object>;
|
||||
addingActivityItem: boolean;
|
||||
belongsToCurrentUser: boolean;
|
||||
currentUser: RealmUser;
|
||||
isConnected: boolean;
|
||||
navToSuggestions: () => void;
|
||||
observation: RealmObservation & Observation & { id: number };
|
||||
openAddCommentSheet: () => void;
|
||||
openAgreeWithIdSheet: () => void;
|
||||
refetchRemoteObservation: () => void;
|
||||
refetchSubscriptions: () => void;
|
||||
showAddCommentSheet: () => void;
|
||||
subscriptions: object;
|
||||
targetActivityItemID: number;
|
||||
wasSynced: boolean;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
const ObsDetailsDefaultMode = ( {
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
import SavedMatchContainer from "./SavedMatch/SavedMatchContainer";
|
||||
|
||||
type RouteParams = {
|
||||
targetActivityItemID?: number,
|
||||
uuid: string,
|
||||
targetActivityItemID?: number;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
const ObsDetailsDefaultModeScreensWrapper = () => {
|
||||
|
||||
@@ -14,8 +14,8 @@ import { useTranslation } from "sharedHooks";
|
||||
import SavedMatchHeaderRight from "./SavedMatchHeaderRight";
|
||||
|
||||
interface Props {
|
||||
observation: RealmObservation,
|
||||
navToTaxonDetails: ( ) => void,
|
||||
observation: RealmObservation;
|
||||
navToTaxonDetails: ( ) => void;
|
||||
}
|
||||
|
||||
const SavedMatch = ( {
|
||||
|
||||
@@ -6,7 +6,7 @@ import React from "react";
|
||||
import type { RealmObservation } from "realmModels/types";
|
||||
|
||||
interface Props {
|
||||
observation: RealmObservation,
|
||||
observation: RealmObservation;
|
||||
}
|
||||
|
||||
const SavedMatchContainer = ( { observation }: Props ) => {
|
||||
|
||||
@@ -11,17 +11,17 @@ import type { Node } from "react";
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
hidden?: boolean,
|
||||
hidden?: boolean;
|
||||
identification: {
|
||||
body?: string,
|
||||
taxon: { id: number }
|
||||
}
|
||||
body?: string;
|
||||
taxon: { id: number };
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
onSuggestId:Function,
|
||||
onSuggestId:Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
editIdentBody: Function,
|
||||
editIdentBody: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
onPressClose: Function
|
||||
onPressClose: Function;
|
||||
}
|
||||
|
||||
const SuggestIDSheet = ( {
|
||||
|
||||
@@ -19,15 +19,15 @@ export const SAVE = "save";
|
||||
export type ButtonType = typeof SAVE | typeof UPLOAD | null;
|
||||
|
||||
type Props = {
|
||||
buttonPressed: ButtonType,
|
||||
canSaveOnly: boolean,
|
||||
buttonPressed: ButtonType;
|
||||
canSaveOnly: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
handlePress: Function,
|
||||
loading: boolean,
|
||||
showFocusedChangesButton: boolean,
|
||||
showFocusedUploadButton: boolean
|
||||
showHalfOpacity: boolean,
|
||||
wasSynced: boolean,
|
||||
handlePress: Function;
|
||||
loading: boolean;
|
||||
showFocusedChangesButton: boolean;
|
||||
showFocusedUploadButton: boolean;
|
||||
showHalfOpacity: boolean;
|
||||
wasSynced: boolean;
|
||||
}
|
||||
|
||||
const BottomButtons = ( {
|
||||
|
||||
@@ -21,14 +21,14 @@ import MissingEvidenceSheet from "./Sheets/MissingEvidenceSheet";
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
type Props = {
|
||||
passesEvidenceTest: boolean,
|
||||
observations: Array<object>,
|
||||
currentObservation: RealmObservation,
|
||||
currentObservationIndex: number,
|
||||
passesEvidenceTest: boolean;
|
||||
observations: Array<object>;
|
||||
currentObservation: RealmObservation;
|
||||
currentObservationIndex: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
setCurrentObservationIndex: Function,
|
||||
setCurrentObservationIndex: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
transitionAnimation: Function
|
||||
transitionAnimation: Function;
|
||||
}
|
||||
|
||||
const BottomButtonsContainer = ( {
|
||||
|
||||
@@ -12,9 +12,9 @@ import useTranslation from "sharedHooks/useTranslation";
|
||||
type Geoprivacy = null | GEOPRIVACY_OPEN | GEOPRIVACY_OBSCURED | GEOPRIVACY_PRIVATE;
|
||||
|
||||
type Props = {
|
||||
onPressClose: ( ) => void,
|
||||
selectedValue?: Geoprivacy,
|
||||
updateGeoprivacyStatus: ( Geoprivacy ) => void
|
||||
onPressClose: ( ) => void;
|
||||
selectedValue?: Geoprivacy;
|
||||
updateGeoprivacyStatus: ( Geoprivacy ) => void;
|
||||
}
|
||||
|
||||
const GeoprivacySheet = ( {
|
||||
|
||||
@@ -7,21 +7,21 @@ import ObsGridItem from "./ObsGridItem";
|
||||
import ObsListItem from "./ObsListItem";
|
||||
|
||||
type Props = {
|
||||
currentUser: object,
|
||||
queued: boolean,
|
||||
explore: boolean,
|
||||
hideMetadata?: boolean,
|
||||
hideObsUploadStatus?: boolean,
|
||||
hideObsStatus?: boolean,
|
||||
isSimpleObsStatus?: boolean,
|
||||
hideRGLabel?: boolean,
|
||||
onUploadButtonPress: ( ) => void,
|
||||
onItemPress: ( ) => void,
|
||||
gridItemStyle: object,
|
||||
layout: "list" | "grid",
|
||||
observation: RealmObservation,
|
||||
uploadProgress: number,
|
||||
unsynced: boolean
|
||||
currentUser: object;
|
||||
queued: boolean;
|
||||
explore: boolean;
|
||||
hideMetadata?: boolean;
|
||||
hideObsUploadStatus?: boolean;
|
||||
hideObsStatus?: boolean;
|
||||
isSimpleObsStatus?: boolean;
|
||||
hideRGLabel?: boolean;
|
||||
onUploadButtonPress: ( ) => void;
|
||||
onItemPress: ( ) => void;
|
||||
gridItemStyle: object;
|
||||
layout: "list" | "grid";
|
||||
observation: RealmObservation;
|
||||
uploadProgress: number;
|
||||
unsynced: boolean;
|
||||
};
|
||||
|
||||
const ObsPressable = ( {
|
||||
|
||||
@@ -6,7 +6,7 @@ import React, { useState } from "react";
|
||||
import { useCurrentUser, useTranslation } from "sharedHooks";
|
||||
|
||||
interface Props {
|
||||
rule: object
|
||||
rule: object;
|
||||
}
|
||||
|
||||
const ProjectRuleItem = ( { rule }: Props ) => {
|
||||
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
import ProjectListItem from "./ProjectListItem";
|
||||
|
||||
interface Props {
|
||||
projects: Array<object>
|
||||
ListEmptyComponent?: React.JSX.Element
|
||||
ListFooterComponent?: React.JSX.Element
|
||||
onEndReached?: ( ) => void
|
||||
onPress?: ( project: ApiProject ) => void
|
||||
accessibilityLabel?: string
|
||||
projects: Array<object>;
|
||||
ListEmptyComponent?: React.JSX.Element;
|
||||
ListFooterComponent?: React.JSX.Element;
|
||||
onEndReached?: ( ) => void;
|
||||
onPress?: ( project: ApiProject ) => void;
|
||||
accessibilityLabel?: string;
|
||||
}
|
||||
|
||||
const ProjectList = ( {
|
||||
|
||||
@@ -34,11 +34,11 @@ interface Props {
|
||||
isFetchingNextPage: boolean;
|
||||
isLoading: boolean;
|
||||
memberId?: number;
|
||||
projects: object[],
|
||||
projects: object[];
|
||||
requestPermissions: () => void;
|
||||
searchInput: string;
|
||||
setSearchInput: ( _text: string ) => void;
|
||||
tabs: Tab[],
|
||||
tabs: Tab[];
|
||||
}
|
||||
|
||||
const Projects = ( {
|
||||
|
||||
@@ -19,15 +19,15 @@ import Animated, {
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
type ConfettiProps = PropsWithChildren<{
|
||||
count: number
|
||||
duration?: number
|
||||
count: number;
|
||||
duration?: number;
|
||||
}>
|
||||
|
||||
type AnimatedElementProps = PropsWithChildren<{
|
||||
index: number
|
||||
count: number
|
||||
animation: SharedValue<number>
|
||||
duration: number
|
||||
index: number;
|
||||
count: number;
|
||||
animation: SharedValue<number>;
|
||||
duration: number;
|
||||
}>
|
||||
|
||||
const AnimatedElement = memo(
|
||||
|
||||
@@ -15,7 +15,7 @@ interface ButtonProps {
|
||||
disabled?: boolean;
|
||||
forceDark?: boolean;
|
||||
icon?: string;
|
||||
iconPosition?: string,
|
||||
iconPosition?: string;
|
||||
level?: string;
|
||||
loading?: boolean;
|
||||
onPress: ( _event?: GestureResponderEvent ) => void;
|
||||
|
||||
@@ -23,7 +23,7 @@ interface Props extends PropsWithChildren {
|
||||
// There is probably a better way to indicate that this tailwind prop is
|
||||
// supported everywhere, but I haven't found it yet. ~~~kueda 20241016
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
className?: string,
|
||||
className?: string;
|
||||
color?: string;
|
||||
disabled?: boolean;
|
||||
height?: number;
|
||||
|
||||
@@ -5,10 +5,10 @@ import React from "react";
|
||||
import { useTranslation } from "sharedHooks";
|
||||
|
||||
interface Props {
|
||||
layout?: string,
|
||||
isConnected?: boolean | null,
|
||||
hideLoadingWheel: boolean,
|
||||
explore?: boolean
|
||||
layout?: string;
|
||||
isConnected?: boolean | null;
|
||||
hideLoadingWheel: boolean;
|
||||
explore?: boolean;
|
||||
}
|
||||
|
||||
const InfiniteScrollLoadingWheel = ( {
|
||||
|
||||
@@ -22,20 +22,20 @@ import { getShadow } from "styles/global";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props {
|
||||
closeModal: () => void,
|
||||
coordinateString?: string,
|
||||
headerTitle?: React.ReactNode,
|
||||
closeModal: () => void;
|
||||
coordinateString?: string;
|
||||
headerTitle?: React.ReactNode;
|
||||
// TODO MOB-1038: reconcile the type issues here requiring the intersection
|
||||
observation?: Observation & RealmObservation,
|
||||
region?: Region,
|
||||
tileMapParams: Record<string, string> | null,
|
||||
observation?: Observation & RealmObservation;
|
||||
region?: Region;
|
||||
tileMapParams: Record<string, string> | null;
|
||||
}
|
||||
|
||||
interface FloatingActionButtonProps {
|
||||
accessibilityLabel: string,
|
||||
buttonClassName: string,
|
||||
icon: string,
|
||||
onPress: ( event?: GestureResponderEvent ) => void,
|
||||
accessibilityLabel: string;
|
||||
buttonClassName: string;
|
||||
icon: string;
|
||||
onPress: ( event?: GestureResponderEvent ) => void;
|
||||
}
|
||||
|
||||
const FloatingActionButton = ( {
|
||||
|
||||
@@ -7,11 +7,11 @@ import RNModal from "react-native-modal";
|
||||
interface Props {
|
||||
showModal: boolean;
|
||||
closeModal: () => void;
|
||||
modal: React.ReactNode,
|
||||
modal: React.ReactNode;
|
||||
backdropOpacity?: number;
|
||||
fullScreen?: boolean;
|
||||
onModalHide?: () => void,
|
||||
style?: ViewStyle,
|
||||
onModalHide?: () => void;
|
||||
style?: ViewStyle;
|
||||
animationIn?: string;
|
||||
animationOut?: string;
|
||||
disableSwipeDirection?: boolean;
|
||||
|
||||
@@ -10,9 +10,9 @@ import React from "react";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props extends PropsWithChildren {
|
||||
invertToWhiteBackground: boolean
|
||||
invertToWhiteBackground: boolean;
|
||||
headerRight?: React.JSX.Element;
|
||||
testID: string,
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const OverlayHeader = ( {
|
||||
|
||||
@@ -7,9 +7,9 @@ import * as React from "react";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
interface Props {
|
||||
qualityGrade: string | null,
|
||||
color?: string,
|
||||
opacity?: number
|
||||
qualityGrade: string | null;
|
||||
color?: string;
|
||||
opacity?: number;
|
||||
}
|
||||
|
||||
const qualityGradeSVG = (
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Props {
|
||||
containerClass?: string;
|
||||
handleTextChange: ( _text: string ) => void;
|
||||
hasShadow?: boolean;
|
||||
input?: React.RefObject<RNTextInput | null> | React.MutableRefObject<RNTextInput | undefined>,
|
||||
input?: React.RefObject<RNTextInput | null> | React.MutableRefObject<RNTextInput | undefined>;
|
||||
placeholder?: string;
|
||||
testID?: string;
|
||||
value: string;
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
props: BottomSheetBackdropProps,
|
||||
onPress: ( ) => void
|
||||
props: BottomSheetBackdropProps;
|
||||
onPress: ( ) => void;
|
||||
}
|
||||
|
||||
const BottomSheetStandardBackdrop = ( { props, onPress }: Props ) => (
|
||||
|
||||
@@ -8,25 +8,25 @@ import React, { useState } from "react";
|
||||
import useTranslation from "sharedHooks/useTranslation";
|
||||
|
||||
interface Props {
|
||||
bottomComponent?: React.JSX.Element
|
||||
buttonRowClassName?: string
|
||||
bottomComponent?: React.JSX.Element;
|
||||
buttonRowClassName?: string;
|
||||
confirm: ( _checkedValue: string ) => void;
|
||||
confirmText?: string;
|
||||
headerText: string,
|
||||
insideModal?: boolean,
|
||||
headerText: string;
|
||||
insideModal?: boolean;
|
||||
onPressClose?: ( ) => void;
|
||||
radioValues: {
|
||||
[key: string]: {
|
||||
value: string,
|
||||
icon?: string,
|
||||
label: string,
|
||||
text?: string,
|
||||
buttonText?: string,
|
||||
}
|
||||
},
|
||||
selectedValue?: string,
|
||||
testID?: string,
|
||||
topDescriptionText?: React.JSX.Element,
|
||||
value: string;
|
||||
icon?: string;
|
||||
label: string;
|
||||
text?: string;
|
||||
buttonText?: string;
|
||||
};
|
||||
};
|
||||
selectedValue?: string;
|
||||
testID?: string;
|
||||
topDescriptionText?: React.JSX.Element;
|
||||
}
|
||||
|
||||
const RadioButtonSheet = ( {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from "sharedHooks";
|
||||
|
||||
interface Props {
|
||||
observation: {
|
||||
private_place_guess?: string
|
||||
private_place_guess?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ interface Props {
|
||||
isLoading?: boolean;
|
||||
isLocal?: boolean;
|
||||
renderItem: (
|
||||
{ item, index }: { item: RealmTaxon, index: number }
|
||||
{ item, index }: { item: RealmTaxon; index: number }
|
||||
) => React.ReactElement<unknown>;
|
||||
taxa: RealmTaxon[]
|
||||
taxa: RealmTaxon[];
|
||||
}
|
||||
|
||||
const TaxonSearch = ( {
|
||||
|
||||
@@ -6,7 +6,7 @@ import colors from "styles/tailwindColors";
|
||||
const PROGRESS_BAR_STYLE = { backgroundColor: "transparent" };
|
||||
|
||||
type Props = {
|
||||
progress: number
|
||||
progress: number;
|
||||
}
|
||||
|
||||
const UploadProgressBar = ( { progress }: Props ): Node => (
|
||||
|
||||
@@ -4,8 +4,8 @@ import { View } from "components/styledComponents";
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
iconClasses: Array<string>,
|
||||
completeColor: string
|
||||
iconClasses: Array<string>;
|
||||
completeColor: string;
|
||||
}
|
||||
|
||||
const UploadCompleteIcon = ( {
|
||||
|
||||
@@ -26,7 +26,7 @@ interface Props extends PropsWithChildren {
|
||||
progress: number;
|
||||
uniqueKey: string;
|
||||
queued: boolean;
|
||||
obsStatus: ReactComponent
|
||||
obsStatus: ReactComponent;
|
||||
}
|
||||
|
||||
const UploadStatus = ( {
|
||||
|
||||
@@ -98,8 +98,8 @@ const LINKIFY_OPTIONS: Opts = {
|
||||
};
|
||||
|
||||
interface Props extends React.PropsWithChildren {
|
||||
text: string,
|
||||
htmlStyle?: object,
|
||||
text: string;
|
||||
htmlStyle?: object;
|
||||
}
|
||||
|
||||
const UserText = ( {
|
||||
|
||||
@@ -57,7 +57,7 @@ export type Suggestion = {
|
||||
taxon: {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export type TopSuggestionType = string;
|
||||
|
||||
@@ -14,36 +14,36 @@ import Attribution from "./Attribution";
|
||||
|
||||
type Props = {
|
||||
debugData: {
|
||||
onlineFetchStatus: string,
|
||||
offlineFetchStatus: string,
|
||||
selectedPhotoUri: string,
|
||||
onlineSuggestionsUpdatedAt: Date,
|
||||
timedOut: boolean,
|
||||
shouldUseEvidenceLocation: boolean,
|
||||
topSuggestionType: string,
|
||||
onlineSuggestions: [],
|
||||
usingOfflineSuggestions: boolean,
|
||||
onlineSuggestionsError: Error,
|
||||
onlineFetchStatus: string;
|
||||
offlineFetchStatus: string;
|
||||
selectedPhotoUri: string;
|
||||
onlineSuggestionsUpdatedAt: Date;
|
||||
timedOut: boolean;
|
||||
shouldUseEvidenceLocation: boolean;
|
||||
topSuggestionType: string;
|
||||
onlineSuggestions: [];
|
||||
usingOfflineSuggestions: boolean;
|
||||
onlineSuggestionsError: Error;
|
||||
suggestions: {
|
||||
otherSuggestions: [],
|
||||
otherSuggestions: [];
|
||||
topSuggestion: {
|
||||
taxon: {
|
||||
id: number,
|
||||
name: string
|
||||
},
|
||||
combined_score: number
|
||||
}
|
||||
}
|
||||
},
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
combined_score: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
handleSkip: Function,
|
||||
handleSkip: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
hideLocationToggleButton: Function,
|
||||
hideSkip?: boolean,
|
||||
observers: Array<string>,
|
||||
shouldUseEvidenceLocation: boolean,
|
||||
hideLocationToggleButton: Function;
|
||||
hideSkip?: boolean;
|
||||
observers: Array<string>;
|
||||
shouldUseEvidenceLocation: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
toggleLocation: Function
|
||||
toggleLocation: Function;
|
||||
};
|
||||
|
||||
const SuggestionsFooter = ( {
|
||||
|
||||
@@ -7,10 +7,10 @@ const outputPath = computerVisionPath;
|
||||
|
||||
type FlattenUploadArgs = {
|
||||
image: {
|
||||
uri: string,
|
||||
name: string,
|
||||
type: string
|
||||
}
|
||||
uri: string;
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
|
||||
const flattenUploadParams = async (
|
||||
|
||||
@@ -9,7 +9,7 @@ const useNavigateWithTaxonSelected = (
|
||||
// mysterious background nonsense happening after this screen loses focus
|
||||
unselectTaxon: () => void,
|
||||
options: {
|
||||
vision: boolean
|
||||
vision: boolean;
|
||||
}
|
||||
) => {
|
||||
const navigation = useNavigation( );
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from "react";
|
||||
interface Props {
|
||||
commonName: string;
|
||||
scientificNameFirst?: boolean;
|
||||
isCurrentTaxon?: boolean
|
||||
isCurrentTaxon?: boolean;
|
||||
}
|
||||
|
||||
const TaxonomyCommonName = ( {
|
||||
|
||||
@@ -9,7 +9,7 @@ import TaxonomyCommonName from "./TaxonomyCommonName";
|
||||
import TaxonomyScientificName from "./TaxonomyScientificName";
|
||||
|
||||
interface Props {
|
||||
currentUser: { login: string, id: number };
|
||||
currentUser: { login: string; id: number };
|
||||
isChild?: boolean;
|
||||
isCurrentTaxon?: boolean;
|
||||
navigateToTaxonDetails: ( _taxonId: number ) => void;
|
||||
|
||||
@@ -14,15 +14,15 @@ const CONTAINER_STYLE = {
|
||||
};
|
||||
|
||||
interface Props {
|
||||
ListEmptyComponent?: React.JSX.Element
|
||||
ListFooterComponent?: React.JSX.Element
|
||||
onEndReached?: ( ) => void
|
||||
refreshing?: boolean
|
||||
users: Array<object>
|
||||
onPress?: ( ) => void
|
||||
accessibilityLabel?: string
|
||||
keyboardShouldPersistTaps?: string
|
||||
contentContainerStyle?: ViewStyle
|
||||
ListEmptyComponent?: React.JSX.Element;
|
||||
ListFooterComponent?: React.JSX.Element;
|
||||
onEndReached?: ( ) => void;
|
||||
refreshing?: boolean;
|
||||
users: Array<object>;
|
||||
onPress?: ( ) => void;
|
||||
accessibilityLabel?: string;
|
||||
keyboardShouldPersistTaps?: string;
|
||||
contentContainerStyle?: ViewStyle;
|
||||
}
|
||||
|
||||
const UserList = ( {
|
||||
|
||||
@@ -8,12 +8,12 @@ import User from "realmModels/User";
|
||||
import { useTranslation } from "sharedHooks";
|
||||
|
||||
interface Props {
|
||||
item: object
|
||||
countText: string
|
||||
item: object;
|
||||
countText: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
onPress?: Function
|
||||
accessibilityLabel?: string
|
||||
pressable?: boolean
|
||||
onPress?: Function;
|
||||
accessibilityLabel?: string;
|
||||
pressable?: boolean;
|
||||
}
|
||||
|
||||
const UserListItem = ( {
|
||||
|
||||
@@ -751,6 +751,9 @@ Navigates-to-AI-camera = Navigates to AI camera
|
||||
Navigates-to-bulk-importer = Navigates to bulk importer
|
||||
Navigates-to-camera = Navigates to camera
|
||||
Navigates-to-explore = Navigates to explore
|
||||
Navigates-to-log-in-screen = Navigates to log in screen
|
||||
# Accessibility hint for the main menu bottom tab
|
||||
Navigates-to-main-menu = Navigates to main menu.
|
||||
Navigates-to-match-screen = Navigates to match screen
|
||||
Navigates-to-notifications = Navigates to notifications
|
||||
Navigates-to-observation-details = Navigates to observation details screen
|
||||
@@ -886,7 +889,6 @@ Opens-AI-camera = Opens AI camera.
|
||||
# Accessibility hint for a button that opens a form for editing a comment
|
||||
Opens-edit-comment-form = Opens edit comment form.
|
||||
Opens-location-permission-prompt = Opens location permission prompt
|
||||
Opens-the-side-drawer-menu = Opens the side drawer menu.
|
||||
OR-SIGN-IN-WITH = OR SIGN IN WITH
|
||||
Or-you-can-try-to-get-a-clearer-photo-by-zooming-in-getting-closer = Or, you can try to get a clearer photo by zooming in, getting closer, or trying a different angle.
|
||||
# Picker prompt on observation edit
|
||||
@@ -1141,7 +1143,6 @@ Show-observation-options = Show observation options.
|
||||
Showing-offline-search-results--taxa = Showing offline search results. To search for more species, try again when connected to the Internet.
|
||||
# Label for button that shows identification suggestions
|
||||
Shows-identification-suggestions = Shows identification suggestions
|
||||
Shows-iNaturalist-bird-logo = Shows iNaturalist bird logo.
|
||||
# Accessibility hint for button that shows observation creation options
|
||||
Shows-observation-creation-options = Shows observation creation options
|
||||
# Accessibility label for a button that allows user to sign in with their Apple account
|
||||
|
||||
@@ -436,6 +436,8 @@
|
||||
"Navigates-to-bulk-importer": "Navigates to bulk importer",
|
||||
"Navigates-to-camera": "Navigates to camera",
|
||||
"Navigates-to-explore": "Navigates to explore",
|
||||
"Navigates-to-log-in-screen": "Navigates to log in screen",
|
||||
"Navigates-to-main-menu": "Navigates to main menu.",
|
||||
"Navigates-to-match-screen": "Navigates to match screen",
|
||||
"Navigates-to-notifications": "Navigates to notifications",
|
||||
"Navigates-to-observation-details": "Navigates to observation details screen",
|
||||
@@ -516,7 +518,6 @@
|
||||
"Opens-AI-camera": "Opens AI camera.",
|
||||
"Opens-edit-comment-form": "Opens edit comment form.",
|
||||
"Opens-location-permission-prompt": "Opens location permission prompt",
|
||||
"Opens-the-side-drawer-menu": "Opens the side drawer menu.",
|
||||
"OR-SIGN-IN-WITH": "OR SIGN IN WITH",
|
||||
"Or-you-can-try-to-get-a-clearer-photo-by-zooming-in-getting-closer": "Or, you can try to get a clearer photo by zooming in, getting closer, or trying a different angle.",
|
||||
"Organism-is-captive": "Organism is captive",
|
||||
@@ -716,7 +717,6 @@
|
||||
"Show-observation-options": "Show observation options.",
|
||||
"Showing-offline-search-results--taxa": "Showing offline search results. To search for more species, try again when connected to the Internet.",
|
||||
"Shows-identification-suggestions": "Shows identification suggestions",
|
||||
"Shows-iNaturalist-bird-logo": "Shows iNaturalist bird logo.",
|
||||
"Shows-observation-creation-options": "Shows observation creation options",
|
||||
"Sign-in-with-Apple": "Sign in with Apple",
|
||||
"Sign-in-with-Apple-Failed": "Sign in with Apple Failed",
|
||||
|
||||
@@ -751,6 +751,9 @@ Navigates-to-AI-camera = Navigates to AI camera
|
||||
Navigates-to-bulk-importer = Navigates to bulk importer
|
||||
Navigates-to-camera = Navigates to camera
|
||||
Navigates-to-explore = Navigates to explore
|
||||
Navigates-to-log-in-screen = Navigates to log in screen
|
||||
# Accessibility hint for the main menu bottom tab
|
||||
Navigates-to-main-menu = Navigates to main menu.
|
||||
Navigates-to-match-screen = Navigates to match screen
|
||||
Navigates-to-notifications = Navigates to notifications
|
||||
Navigates-to-observation-details = Navigates to observation details screen
|
||||
@@ -886,7 +889,6 @@ Opens-AI-camera = Opens AI camera.
|
||||
# Accessibility hint for a button that opens a form for editing a comment
|
||||
Opens-edit-comment-form = Opens edit comment form.
|
||||
Opens-location-permission-prompt = Opens location permission prompt
|
||||
Opens-the-side-drawer-menu = Opens the side drawer menu.
|
||||
OR-SIGN-IN-WITH = OR SIGN IN WITH
|
||||
Or-you-can-try-to-get-a-clearer-photo-by-zooming-in-getting-closer = Or, you can try to get a clearer photo by zooming in, getting closer, or trying a different angle.
|
||||
# Picker prompt on observation edit
|
||||
@@ -1141,7 +1143,6 @@ Show-observation-options = Show observation options.
|
||||
Showing-offline-search-results--taxa = Showing offline search results. To search for more species, try again when connected to the Internet.
|
||||
# Label for button that shows identification suggestions
|
||||
Shows-identification-suggestions = Shows identification suggestions
|
||||
Shows-iNaturalist-bird-logo = Shows iNaturalist bird logo.
|
||||
# Accessibility hint for button that shows observation creation options
|
||||
Shows-observation-creation-options = Shows observation creation options
|
||||
# Accessibility label for a button that allows user to sign in with their Apple account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BottomTabBarProps } from "@react-navigation/bottom-tabs";
|
||||
import { useDrawerStatus } from "@react-navigation/drawer";
|
||||
import {
|
||||
SCREEN_NAME_MENU,
|
||||
SCREEN_NAME_NOTIFICATIONS,
|
||||
SCREEN_NAME_OBS_LIST,
|
||||
SCREEN_NAME_ROOT_EXPLORE
|
||||
@@ -11,8 +11,6 @@ import { useCurrentUser, useTranslation } from "sharedHooks";
|
||||
|
||||
import CustomTabBar from "./CustomTabBar";
|
||||
|
||||
const DRAWER_ID = "OPEN_DRAWER";
|
||||
|
||||
interface TabConfig {
|
||||
icon: string;
|
||||
testID: string;
|
||||
@@ -24,11 +22,12 @@ interface TabConfig {
|
||||
userIconUri?: string;
|
||||
}
|
||||
|
||||
type TabName = "ObservationsTab" | "ExploreTab" | "NotificationsTab";
|
||||
type TabName = "MenuTab" | "ExploreTab" | "ObservationsTab" | "NotificationsTab";
|
||||
|
||||
type ScreenName =
|
||||
| typeof SCREEN_NAME_OBS_LIST
|
||||
| typeof SCREEN_NAME_MENU
|
||||
| typeof SCREEN_NAME_ROOT_EXPLORE
|
||||
| typeof SCREEN_NAME_OBS_LIST
|
||||
| typeof SCREEN_NAME_NOTIFICATIONS;
|
||||
|
||||
type Props = BottomTabBarProps;
|
||||
@@ -36,7 +35,6 @@ type Props = BottomTabBarProps;
|
||||
const CustomTabBarContainer: React.FC<Props> = ( { navigation, state } ) => {
|
||||
const { t } = useTranslation( );
|
||||
const currentUser = useCurrentUser( );
|
||||
const isDrawerOpen = useDrawerStatus() === "open";
|
||||
|
||||
const activeTabIndex = state?.index;
|
||||
const activeTabName = state?.routes[activeTabIndex]?.name as TabName;
|
||||
@@ -45,8 +43,9 @@ const CustomTabBarContainer: React.FC<Props> = ( { navigation, state } ) => {
|
||||
|
||||
const getActiveTab = ( ): ScreenName => {
|
||||
switch ( activeTabName ) {
|
||||
case "ObservationsTab": return SCREEN_NAME_OBS_LIST;
|
||||
case "MenuTab": return SCREEN_NAME_MENU;
|
||||
case "ExploreTab": return SCREEN_NAME_ROOT_EXPLORE;
|
||||
case "ObservationsTab": return SCREEN_NAME_OBS_LIST;
|
||||
case "NotificationsTab": return SCREEN_NAME_NOTIFICATIONS;
|
||||
default: return SCREEN_NAME_OBS_LIST;
|
||||
}
|
||||
@@ -57,14 +56,16 @@ const CustomTabBarContainer: React.FC<Props> = ( { navigation, state } ) => {
|
||||
const tabs: TabConfig[] = useMemo( ( ) => ( [
|
||||
{
|
||||
icon: "hamburger-menu",
|
||||
testID: DRAWER_ID,
|
||||
testID: SCREEN_NAME_MENU,
|
||||
accessibilityLabel: t( "Menu" ),
|
||||
accessibilityHint: t( "Opens-the-side-drawer-menu" ),
|
||||
accessibilityHint: t( "Navigates-to-main-menu" ),
|
||||
size: 32,
|
||||
onPress: ( ) => {
|
||||
navigation.openDrawer( );
|
||||
navigation.navigate( "MenuTab", {
|
||||
screen: "Menu"
|
||||
} );
|
||||
},
|
||||
active: isDrawerOpen
|
||||
active: SCREEN_NAME_MENU === activeTab
|
||||
},
|
||||
{
|
||||
icon: "magnifying-glass",
|
||||
@@ -109,7 +110,6 @@ const CustomTabBarContainer: React.FC<Props> = ( { navigation, state } ) => {
|
||||
] ), [
|
||||
activeTab,
|
||||
userIconUri,
|
||||
isDrawerOpen,
|
||||
navigation,
|
||||
t
|
||||
] );
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { BottomTabBarProps } from "@react-navigation/bottom-tabs";
|
||||
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
||||
import Mortal from "components/SharedComponents/Mortal";
|
||||
import TabStackNavigator, {
|
||||
SCREEN_NAME_MENU,
|
||||
SCREEN_NAME_NOTIFICATIONS,
|
||||
SCREEN_NAME_OBS_LIST,
|
||||
SCREEN_NAME_ROOT_EXPLORE
|
||||
@@ -29,19 +30,25 @@ const BottomTabs = ( ) => {
|
||||
screenOptions={{
|
||||
lazy: true,
|
||||
freezeOnBlur: true,
|
||||
headerShown: false
|
||||
headerShown: false,
|
||||
animation: "fade"
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="ObservationsTab"
|
||||
name="MenuTab"
|
||||
component={TabStackNavigator}
|
||||
initialParams={{ initialRouteName: SCREEN_NAME_OBS_LIST }}
|
||||
initialParams={{ initialRouteName: SCREEN_NAME_MENU }}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="ExploreTab"
|
||||
component={TabStackNavigator}
|
||||
initialParams={{ initialRouteName: SCREEN_NAME_ROOT_EXPLORE }}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="ObservationsTab"
|
||||
component={TabStackNavigator}
|
||||
initialParams={{ initialRouteName: SCREEN_NAME_OBS_LIST }}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="NotificationsTab"
|
||||
component={TabStackNavigator}
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
import { useNetInfo } from "@react-native-community/netinfo";
|
||||
import {
|
||||
DrawerContentScrollView,
|
||||
DrawerItem
|
||||
} from "@react-navigation/drawer";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import classnames from "classnames";
|
||||
import {
|
||||
signOut
|
||||
} from "components/LoginSignUp/AuthenticationService";
|
||||
import {
|
||||
Body1,
|
||||
Heading4,
|
||||
INatIcon,
|
||||
INatIconButton,
|
||||
List2, TextInputSheet,
|
||||
UserIcon,
|
||||
WarningSheet
|
||||
} from "components/SharedComponents";
|
||||
import { Pressable, View } from "components/styledComponents";
|
||||
import { RealmContext } from "providers/contexts";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import type { ViewStyle } from "react-native";
|
||||
import {
|
||||
Alert, Dimensions
|
||||
} from "react-native";
|
||||
import User from "realmModels/User";
|
||||
import { BREAKPOINTS } from "sharedHelpers/breakpoint";
|
||||
import { log } from "sharedHelpers/logger";
|
||||
import { useCurrentUser, useTranslation } from "sharedHooks";
|
||||
import useStore, { zustandStorage } from "stores/useStore";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
const { useRealm } = RealmContext;
|
||||
|
||||
const { width } = Dimensions.get( "screen" );
|
||||
|
||||
function isDefaultMode( ) {
|
||||
return useStore.getState( ).layout.isDefaultMode === true;
|
||||
}
|
||||
|
||||
const createDrawerStyle = ( isDark: boolean ) => ( {
|
||||
backgroundColor: isDark
|
||||
? colors.darkModeGray
|
||||
: "white",
|
||||
borderTopRightRadius: 20,
|
||||
borderBottomRightRadius: 20,
|
||||
minHeight: "100%"
|
||||
} as const );
|
||||
|
||||
interface Props {
|
||||
state: object;
|
||||
navigation: object;
|
||||
descriptors: object;
|
||||
colorScheme?: string;
|
||||
}
|
||||
|
||||
const feedbackLogger = log.extend( "feedback" );
|
||||
|
||||
function showOfflineAlert( t ) {
|
||||
Alert.alert( t( "You-are-offline" ), t( "Please-try-again-when-you-are-online" ) );
|
||||
}
|
||||
|
||||
const CustomDrawerContent = ( {
|
||||
state, navigation, descriptors, colorScheme
|
||||
}: Props ) => {
|
||||
const isDebug = zustandStorage.getItem( "debugMode" ) === "true";
|
||||
const isDarkMode = colorScheme === "dark" && isDebug;
|
||||
const drawerScrollViewStyle = createDrawerStyle( isDarkMode );
|
||||
const realm = useRealm( );
|
||||
const queryClient = useQueryClient( );
|
||||
const currentUser = useCurrentUser( );
|
||||
const { t } = useTranslation( );
|
||||
|
||||
const { isConnected } = useNetInfo( );
|
||||
|
||||
const [showConfirm, setShowConfirm] = useState( false );
|
||||
const [showFeedback, setShowFeedback] = useState( false );
|
||||
|
||||
const drawerItemStyle = useMemo( ( ) => ( {
|
||||
marginBottom: width <= BREAKPOINTS.lg
|
||||
? -15
|
||||
: -5
|
||||
} as const ), [] );
|
||||
|
||||
interface DrawerItem {
|
||||
label: string;
|
||||
navigation?: string;
|
||||
icon: string;
|
||||
color?: string;
|
||||
style?: ViewStyle;
|
||||
onPress?: ( ) => void;
|
||||
testID?: string;
|
||||
}
|
||||
const drawerItems = useMemo( ( ) => {
|
||||
const items: {
|
||||
[key: string]: DrawerItem;
|
||||
} = {
|
||||
projects: {
|
||||
label: t( "PROJECTS" ),
|
||||
navigation: "Projects",
|
||||
icon: "briefcase"
|
||||
},
|
||||
about: {
|
||||
label: t( "ABOUT" ),
|
||||
navigation: "About",
|
||||
icon: "inaturalist"
|
||||
},
|
||||
donate: {
|
||||
label: t( "DONATE" ),
|
||||
navigation: "Donate",
|
||||
icon: "heart"
|
||||
},
|
||||
help: {
|
||||
label: t( "HELP" ),
|
||||
navigation: "Help",
|
||||
icon: "help-circle"
|
||||
},
|
||||
settings: {
|
||||
testID: "settings",
|
||||
label: t( "SETTINGS" ),
|
||||
navigation: "Settings",
|
||||
icon: "gear"
|
||||
}
|
||||
};
|
||||
items.feedback = {
|
||||
label: t( "FEEDBACK" ),
|
||||
icon: "feedback",
|
||||
onPress: ( ) => {
|
||||
if ( isConnected ) {
|
||||
setShowFeedback( true );
|
||||
} else {
|
||||
showOfflineAlert( t );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if ( currentUser ) {
|
||||
items.logout = {
|
||||
label: t( "LOG-OUT" ),
|
||||
icon: "door-exit",
|
||||
style: {
|
||||
opacity: 0.5,
|
||||
display: "flex"
|
||||
},
|
||||
onPress: ( ) => setShowConfirm( true )
|
||||
};
|
||||
} else {
|
||||
items.login = {
|
||||
label: t( "LOG-IN" ),
|
||||
icon: "door-enter",
|
||||
color: colors.inatGreen,
|
||||
style: {
|
||||
display: "flex"
|
||||
},
|
||||
onPress: ( ) => {
|
||||
navigation.navigate( "LoginStackNavigator" );
|
||||
}
|
||||
};
|
||||
}
|
||||
if ( isDebug ) {
|
||||
items.debug = {
|
||||
label: "DEBUG",
|
||||
navigation: "Debug",
|
||||
icon: "triangle-exclamation",
|
||||
color: "deeppink"
|
||||
};
|
||||
}
|
||||
return items;
|
||||
}, [currentUser, isConnected, isDebug, navigation, t] );
|
||||
|
||||
const onSignOut = async ( ) => {
|
||||
await signOut( { realm, clearRealm: true, queryClient } );
|
||||
setShowConfirm( false );
|
||||
|
||||
// TODO might be necessary to restart the app at this point. We just
|
||||
// deleted the realm file on disk, but the RealmProvider may still have a
|
||||
// copy of realm in local state
|
||||
navigation.goBack( );
|
||||
};
|
||||
|
||||
const renderIcon = useCallback( ( key: string ) => (
|
||||
<INatIcon
|
||||
name={drawerItems[key].icon}
|
||||
size={22}
|
||||
color={isDarkMode
|
||||
? colors.white
|
||||
: drawerItems[key].color}
|
||||
/>
|
||||
), [drawerItems, isDarkMode] );
|
||||
|
||||
const renderLabel = useCallback( ( label: string ) => (
|
||||
<Heading4 className={classnames(
|
||||
isDarkMode && "dark:text-white"
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</Heading4>
|
||||
), [isDarkMode] );
|
||||
|
||||
const renderTopBanner = useCallback( ( ) => (
|
||||
<Pressable
|
||||
testID="drawer-top-banner"
|
||||
accessibilityRole="button"
|
||||
className={classnames(
|
||||
currentUser
|
||||
? "ml-5"
|
||||
: "ml-3",
|
||||
"mb-5",
|
||||
"flex-row",
|
||||
"flex-nowrap",
|
||||
"mr-3"
|
||||
)}
|
||||
onPress={( ) => {
|
||||
if ( !currentUser ) {
|
||||
navigation.navigate( "LoginStackNavigator" );
|
||||
} else {
|
||||
navigation.navigate( "TabNavigator", {
|
||||
screen: "ObservationsTab",
|
||||
params: {
|
||||
screen: "UserProfile",
|
||||
params: { userId: currentUser.id }
|
||||
}
|
||||
} );
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentUser
|
||||
? (
|
||||
<UserIcon
|
||||
uri={User.uri( currentUser )}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<INatIconButton
|
||||
icon="inaturalist"
|
||||
size={40}
|
||||
color={colors.inatGreen}
|
||||
accessibilityLabel="iNaturalist"
|
||||
accessibilityHint={t( "Shows-iNaturalist-bird-logo" )}
|
||||
/>
|
||||
) }
|
||||
<View className="ml-3 justify-center">
|
||||
<Body1 className={classnames(
|
||||
isDarkMode && "dark:text-white"
|
||||
)}
|
||||
>
|
||||
{currentUser
|
||||
? currentUser?.login
|
||||
: t( "Log-in-to-iNaturalist" )}
|
||||
</Body1>
|
||||
{currentUser && (
|
||||
<List2>
|
||||
{t( "X-Observations", { count: currentUser.observations_count } )}
|
||||
</List2>
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
), [currentUser, navigation, t, isDarkMode] );
|
||||
|
||||
const renderDrawerItem = useCallback( ( key: string ) => (
|
||||
<View
|
||||
className="mb-6"
|
||||
key={drawerItems[key].label}
|
||||
>
|
||||
<DrawerItem
|
||||
testID={drawerItems[key].testID}
|
||||
accessibilityLabel={drawerItems[key].label}
|
||||
icon={( ) => renderIcon( key )}
|
||||
label={() => renderLabel( drawerItems[key].label )}
|
||||
onPress={( ) => {
|
||||
if ( drawerItems[key].navigation ) {
|
||||
navigation.navigate( "TabNavigator", {
|
||||
screen: "ObservationsTab",
|
||||
params: {
|
||||
screen: drawerItems[key].navigation
|
||||
}
|
||||
} );
|
||||
}
|
||||
if ( drawerItems[key].onPress ) {
|
||||
drawerItems[key].onPress();
|
||||
}
|
||||
}}
|
||||
style={[drawerItemStyle, drawerItems[key].style]}
|
||||
/>
|
||||
</View>
|
||||
), [
|
||||
drawerItemStyle,
|
||||
renderLabel,
|
||||
renderIcon,
|
||||
drawerItems,
|
||||
navigation
|
||||
] );
|
||||
|
||||
const submitFeedback = useCallback( ( text: string ) => {
|
||||
if ( !isConnected ) {
|
||||
showOfflineAlert( t );
|
||||
return false;
|
||||
}
|
||||
const mode = isDefaultMode( )
|
||||
? "DEFAULT:"
|
||||
: "ADVANCED:";
|
||||
feedbackLogger.info( mode, text );
|
||||
Alert.alert( t( "Feedback-Submitted" ), t( "Thank-you-for-sharing-your-feedback" ) );
|
||||
setShowFeedback( false );
|
||||
return true;
|
||||
}, [isConnected, t] );
|
||||
|
||||
return (
|
||||
<DrawerContentScrollView
|
||||
state={state}
|
||||
navigation={navigation}
|
||||
descriptors={descriptors}
|
||||
contentContainerStyle={drawerScrollViewStyle}
|
||||
>
|
||||
<View className="py-5 flex">
|
||||
{renderTopBanner( )}
|
||||
<View className="ml-3">
|
||||
{Object.keys( drawerItems ).map( item => renderDrawerItem( item ) )}
|
||||
</View>
|
||||
</View>
|
||||
{showConfirm && (
|
||||
<WarningSheet
|
||||
onPressClose={() => setShowConfirm( false )}
|
||||
headerText={t( "LOG-OUT--question" )}
|
||||
text={t( "Are-you-sure-you-want-to-log-out" )}
|
||||
handleSecondButtonPress={() => setShowConfirm( false )}
|
||||
secondButtonText={t( "CANCEL" )}
|
||||
confirm={onSignOut}
|
||||
buttonText={t( "LOG-OUT" )}
|
||||
/>
|
||||
)}
|
||||
{showFeedback && (
|
||||
<TextInputSheet
|
||||
hidden={!showFeedback}
|
||||
buttonText={t( "SUBMIT" )}
|
||||
onPressClose={() => setShowFeedback( false )}
|
||||
headerText={t( "FEEDBACK" )}
|
||||
confirm={submitFeedback}
|
||||
description={t( "Thanks-for-using-any-suggestions" )}
|
||||
maxLength={1000}
|
||||
/>
|
||||
)}
|
||||
</DrawerContentScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomDrawerContent;
|
||||
@@ -5,7 +5,7 @@ import * as React from "react";
|
||||
import { Animated } from "react-native";
|
||||
|
||||
interface Props {
|
||||
children: React.JSX.Element
|
||||
children: React.JSX.Element;
|
||||
}
|
||||
|
||||
const FadeInView = ( { children }: Props ) => {
|
||||
|
||||
48
src/navigation/RootStackNavigator.tsx
Normal file
48
src/navigation/RootStackNavigator.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import LoginStackNavigator from "navigation/StackNavigators/LoginStackNavigator";
|
||||
import NoBottomTabStackNavigator from "navigation/StackNavigators/NoBottomTabStackNavigator";
|
||||
import OnboardingStackNavigator from "navigation/StackNavigators/OnboardingStackNavigator";
|
||||
import * as React from "react";
|
||||
import { useOnboardingShown } from "sharedHelpers/installData";
|
||||
|
||||
import BottomTabNavigator from "./BottomTabNavigator";
|
||||
import { hideHeader, preventSwipeToGoBack } from "./navigationOptions";
|
||||
|
||||
const Stack = createNativeStackNavigator( );
|
||||
|
||||
// DEVELOPERS: do you need to add any screens here? This is the RootStack.
|
||||
// All the rest of our screens live in:
|
||||
// NoBottomTabStackNavigator, TabStackNavigator, OnboardingStackNavigator, or LoginStackNavigator
|
||||
|
||||
const RootStackNavigator = ( ) => {
|
||||
const [onboardingShown] = useOnboardingShown( );
|
||||
|
||||
return (
|
||||
<Stack.Navigator screenOptions={{ ...hideHeader, ...preventSwipeToGoBack, animation: "none" }}>
|
||||
{!onboardingShown
|
||||
? (
|
||||
<Stack.Screen
|
||||
name="OnboardingStackNavigator"
|
||||
component={OnboardingStackNavigator}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Stack.Screen
|
||||
name="TabNavigator"
|
||||
component={BottomTabNavigator}
|
||||
/>
|
||||
|
||||
)}
|
||||
<Stack.Screen
|
||||
name="NoBottomTabStackNavigator"
|
||||
component={NoBottomTabStackNavigator}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="LoginStackNavigator"
|
||||
component={LoginStackNavigator}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
export default RootStackNavigator;
|
||||
@@ -15,6 +15,7 @@ import ExploreProjectSearch from "components/Explore/SearchScreens/ExploreProjec
|
||||
import ExploreTaxonSearch from "components/Explore/SearchScreens/ExploreTaxonSearch";
|
||||
import ExploreUserSearch from "components/Explore/SearchScreens/ExploreUserSearch";
|
||||
import Help from "components/Help/Help";
|
||||
import Menu from "components/Menu/Menu";
|
||||
import MyObservationsContainer from "components/MyObservations/MyObservationsContainer";
|
||||
import Notifications from "components/Notifications/Notifications";
|
||||
import DQAContainer from "components/ObsDetails/DQAContainer";
|
||||
@@ -38,7 +39,6 @@ import {
|
||||
fadeInComponent,
|
||||
hideHeader,
|
||||
hideHeaderLeft,
|
||||
isDrawerScreen,
|
||||
preventSwipeToGoBack,
|
||||
removeBottomBorder,
|
||||
showHeader,
|
||||
@@ -124,11 +124,7 @@ const logTitle = () => <Heading4 className="text-white">LOG</Heading4>;
|
||||
|
||||
// note: react navigation 7 will have a layout prop
|
||||
// which should replace all of these individual wrappers
|
||||
const FadeInNotifications = ( ) => fadeInComponent( <Notifications /> );
|
||||
const FadeInRootExplore = ( ) => fadeInComponent( <RootExploreContainer /> );
|
||||
const FadeInMyObservations = ( ) => fadeInComponent( <MyObservationsContainer /> );
|
||||
const FadeInUserProfile = ( ) => fadeInComponent( <UserProfile /> );
|
||||
const FadeInExploreContainer = ( ) => fadeInComponent( <ExploreContainer /> );
|
||||
const FadeInObsDetailsDefaultModeScreensWrapper = ( ) => fadeInComponent(
|
||||
<ObsDetailsDefaultModeScreensWrapper />
|
||||
);
|
||||
@@ -152,8 +148,7 @@ const NOTIFICATIONS_OPTIONS = {
|
||||
...preventSwipeToGoBack,
|
||||
...hideHeaderLeft,
|
||||
headerTitle: notificationsTitle,
|
||||
headerTitleAlign: "center",
|
||||
animation: "none"
|
||||
headerTitleAlign: "center"
|
||||
};
|
||||
|
||||
const DQA_OPTIONS = {
|
||||
@@ -180,8 +175,9 @@ const OBS_DETAILS_OPTIONS = {
|
||||
|
||||
const Stack = createNativeStackNavigator( );
|
||||
|
||||
export const SCREEN_NAME_OBS_LIST = "ObsList";
|
||||
export const SCREEN_NAME_MENU = "Menu";
|
||||
export const SCREEN_NAME_ROOT_EXPLORE = "RootExplore";
|
||||
export const SCREEN_NAME_OBS_LIST = "ObsList";
|
||||
export const SCREEN_NAME_NOTIFICATIONS = "Notifications";
|
||||
|
||||
const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
@@ -202,9 +198,17 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
<Stack.Group
|
||||
screenOptions={{ ...hideHeader }}
|
||||
>
|
||||
<Stack.Screen
|
||||
name={SCREEN_NAME_MENU}
|
||||
component={Menu}
|
||||
options={{
|
||||
...preventSwipeToGoBack,
|
||||
animation: "none"
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={SCREEN_NAME_OBS_LIST}
|
||||
component={FadeInMyObservations}
|
||||
component={MyObservationsContainer}
|
||||
options={{
|
||||
...preventSwipeToGoBack,
|
||||
animation: "none"
|
||||
@@ -212,7 +216,7 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
/>
|
||||
<Stack.Screen
|
||||
name={SCREEN_NAME_ROOT_EXPLORE}
|
||||
component={FadeInRootExplore}
|
||||
component={RootExploreContainer}
|
||||
options={{
|
||||
...preventSwipeToGoBack,
|
||||
animation: "none"
|
||||
@@ -220,7 +224,7 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Explore"
|
||||
component={FadeInExploreContainer}
|
||||
component={ExploreContainer}
|
||||
/>
|
||||
{isDefaultMode
|
||||
? (
|
||||
@@ -240,7 +244,7 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
</Stack.Group>
|
||||
<Stack.Screen
|
||||
name={SCREEN_NAME_NOTIFICATIONS}
|
||||
component={FadeInNotifications}
|
||||
component={Notifications}
|
||||
options={NOTIFICATIONS_OPTIONS}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -264,7 +268,6 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
name="Projects"
|
||||
component={FadeInProjectsContainer}
|
||||
options={{
|
||||
...isDrawerScreen,
|
||||
...removeBottomBorder,
|
||||
...preventSwipeToGoBack
|
||||
}}
|
||||
@@ -308,7 +311,6 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
{/* Developer Stack Group */}
|
||||
<Stack.Group
|
||||
screenOptions={{
|
||||
...isDrawerScreen,
|
||||
headerStyle: { backgroundColor: "deeppink", color: "white" },
|
||||
headerTintColor: "white",
|
||||
headerTitleStyle: { color: "white" }
|
||||
@@ -375,7 +377,6 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
name="Settings"
|
||||
component={FadeInSettings}
|
||||
options={{
|
||||
...isDrawerScreen,
|
||||
headerTitle: settingsTitle
|
||||
}}
|
||||
/>
|
||||
@@ -383,7 +384,6 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
name="About"
|
||||
component={FadeInAbout}
|
||||
options={{
|
||||
...isDrawerScreen,
|
||||
headerTitle: aboutTitle
|
||||
}}
|
||||
/>
|
||||
@@ -391,7 +391,6 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
name="Donate"
|
||||
component={FadeInDonate}
|
||||
options={{
|
||||
...isDrawerScreen,
|
||||
headerTitle: donateTitle
|
||||
}}
|
||||
/>
|
||||
@@ -399,7 +398,6 @@ const TabStackNavigator = ( { route }: TabStackNavigatorProps ): Node => {
|
||||
name="Help"
|
||||
component={FadeInHelp}
|
||||
options={{
|
||||
...isDrawerScreen,
|
||||
headerTitle: helpTitle
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { fontMedium } from "appConstants/fontFamilies";
|
||||
import FullPageWebViewHeader from "components/FullPageWebView/FullPageWebViewHeader";
|
||||
import BackButton from "components/SharedComponents/Buttons/BackButton";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import colors from "styles/tailwindColors";
|
||||
|
||||
import FadeInView from "./FadeInView";
|
||||
@@ -71,27 +70,14 @@ const removeBottomBorder = {
|
||||
)
|
||||
} as const;
|
||||
|
||||
// this removes the default hamburger menu from header
|
||||
const hideDrawerHeaderLeft = {
|
||||
headerLeft: ( ) => (
|
||||
<View />
|
||||
)
|
||||
} as const;
|
||||
|
||||
const preventSwipeToGoBack = {
|
||||
gestureEnabled: false
|
||||
} as const;
|
||||
|
||||
const isDrawerScreen = {
|
||||
animation: "none"
|
||||
} as const;
|
||||
|
||||
export {
|
||||
blankHeaderTitle,
|
||||
fadeInComponent,
|
||||
hideDrawerHeaderLeft,
|
||||
hideHeader,
|
||||
isDrawerScreen,
|
||||
preventSwipeToGoBack,
|
||||
removeBottomBorder,
|
||||
showHeader,
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { createDrawerNavigator } from "@react-navigation/drawer";
|
||||
import {
|
||||
hideDrawerHeaderLeft, hideHeader
|
||||
} from "navigation/navigationOptions";
|
||||
import LoginStackNavigator from "navigation/StackNavigators/LoginStackNavigator";
|
||||
import NoBottomTabStackNavigator from "navigation/StackNavigators/NoBottomTabStackNavigator";
|
||||
import OnboardingStackNavigator from "navigation/StackNavigators/OnboardingStackNavigator";
|
||||
import type { Node } from "react";
|
||||
import * as React from "react";
|
||||
import { useColorScheme } from "react-native";
|
||||
import { useOnboardingShown } from "sharedHelpers/installData";
|
||||
|
||||
import BottomTabNavigator from "./BottomTabNavigator";
|
||||
import CustomDrawerContent from "./CustomDrawerContent";
|
||||
|
||||
const drawerOptions = {
|
||||
...hideHeader,
|
||||
...hideDrawerHeaderLeft,
|
||||
drawerType: "front",
|
||||
drawerStyle: {
|
||||
backgroundColor: "transparent"
|
||||
},
|
||||
swipeEnabled: false
|
||||
};
|
||||
|
||||
const Drawer = createDrawerNavigator( );
|
||||
|
||||
// DEVELOPERS: do you need to add any screens here? All the rest of our screens live in
|
||||
// NoBottomTabStackNavigator, TabStackNavigator, OnboardingStackNavigator, or LoginStackNavigator
|
||||
|
||||
const RootDrawerNavigator = ( ): Node => {
|
||||
const [onboardingShown] = useOnboardingShown( );
|
||||
const colorScheme = useColorScheme( );
|
||||
|
||||
const drawerRenderer = ( {
|
||||
state, navigation, descriptors
|
||||
} ) => (
|
||||
<CustomDrawerContent
|
||||
state={state}
|
||||
navigation={navigation}
|
||||
descriptors={descriptors}
|
||||
colorScheme={colorScheme}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Drawer.Navigator
|
||||
screenOptions={drawerOptions}
|
||||
name="Drawer"
|
||||
drawerContent={drawerRenderer}
|
||||
>
|
||||
{!onboardingShown
|
||||
? (
|
||||
<Drawer.Screen
|
||||
name="OnboardingStackNavigator"
|
||||
component={OnboardingStackNavigator}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<Drawer.Screen
|
||||
name="TabNavigator"
|
||||
component={BottomTabNavigator}
|
||||
/>
|
||||
|
||||
)}
|
||||
<Drawer.Screen
|
||||
name="NoBottomTabStackNavigator"
|
||||
component={NoBottomTabStackNavigator}
|
||||
/>
|
||||
<Drawer.Screen
|
||||
name="LoginStackNavigator"
|
||||
component={LoginStackNavigator}
|
||||
/>
|
||||
</Drawer.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
export default RootDrawerNavigator;
|
||||
@@ -157,134 +157,134 @@ export interface MapBoundaries {
|
||||
}
|
||||
|
||||
interface PLACE {
|
||||
display_name: string,
|
||||
id: number,
|
||||
place_type: number,
|
||||
display_name: string;
|
||||
id: number;
|
||||
place_type: number;
|
||||
point_geojson: {
|
||||
coordinates: Array<number>
|
||||
},
|
||||
coordinates: Array<number>;
|
||||
};
|
||||
bounding_box_geojson?: {
|
||||
coordinates: Array<number>
|
||||
},
|
||||
type: string
|
||||
coordinates: Array<number>;
|
||||
};
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface DefaultLocation {
|
||||
placeMode: PLACE_MODE,
|
||||
lat?: number,
|
||||
lng?: number,
|
||||
radius?: number
|
||||
placeMode: PLACE_MODE;
|
||||
lat?: number;
|
||||
lng?: number;
|
||||
radius?: number;
|
||||
}
|
||||
|
||||
type ExploreProviderProps = {children: React.ReactNode}
|
||||
type State = {
|
||||
casual: boolean,
|
||||
created_d1: string | null | undefined,
|
||||
created_d2: string | null | undefined,
|
||||
created_on: string | null | undefined,
|
||||
d1: string | null | undefined,
|
||||
d2: string | null | undefined,
|
||||
dateObserved: DATE_OBSERVED,
|
||||
dateUploaded: DATE_UPLOADED,
|
||||
establishmentMean: ESTABLISHMENT_MEAN,
|
||||
hrank: TAXONOMIC_RANK | undefined | null,
|
||||
iconic_taxa: string[] | undefined,
|
||||
lat?: number,
|
||||
lng?: number,
|
||||
lrank: TAXONOMIC_RANK | undefined | null,
|
||||
media: MEDIA,
|
||||
months: number[] | null | undefined,
|
||||
needsID: boolean,
|
||||
nelat?: number,
|
||||
nelng?: number,
|
||||
observed_on: string | null | undefined,
|
||||
photoLicense: PHOTO_LICENSE,
|
||||
place: PLACE | null | undefined,
|
||||
place_guess: string,
|
||||
placeMode: PLACE_MODE,
|
||||
place_id: number | null | undefined,
|
||||
casual: boolean;
|
||||
created_d1: string | null | undefined;
|
||||
created_d2: string | null | undefined;
|
||||
created_on: string | null | undefined;
|
||||
d1: string | null | undefined;
|
||||
d2: string | null | undefined;
|
||||
dateObserved: DATE_OBSERVED;
|
||||
dateUploaded: DATE_UPLOADED;
|
||||
establishmentMean: ESTABLISHMENT_MEAN;
|
||||
hrank: TAXONOMIC_RANK | undefined | null;
|
||||
iconic_taxa: string[] | undefined;
|
||||
lat?: number;
|
||||
lng?: number;
|
||||
lrank: TAXONOMIC_RANK | undefined | null;
|
||||
media: MEDIA;
|
||||
months: number[] | null | undefined;
|
||||
needsID: boolean;
|
||||
nelat?: number;
|
||||
nelng?: number;
|
||||
observed_on: string | null | undefined;
|
||||
photoLicense: PHOTO_LICENSE;
|
||||
place: PLACE | null | undefined;
|
||||
place_guess: string;
|
||||
placeMode: PLACE_MODE;
|
||||
place_id: number | null | undefined;
|
||||
// TODO: technically this is not any object but a "Project"
|
||||
// and should be typed as such (e.g., in realm model)
|
||||
project: object | undefined | null,
|
||||
project_id: number | undefined | null,
|
||||
radius?: number,
|
||||
researchGrade: boolean,
|
||||
return_bounds: boolean | undefined,
|
||||
reviewedFilter: REVIEWED,
|
||||
sortBy: SORT_BY,
|
||||
swlat?: number,
|
||||
swlng?: number,
|
||||
project: object | undefined | null;
|
||||
project_id: number | undefined | null;
|
||||
radius?: number;
|
||||
researchGrade: boolean;
|
||||
return_bounds: boolean | undefined;
|
||||
reviewedFilter: REVIEWED;
|
||||
sortBy: SORT_BY;
|
||||
swlat?: number;
|
||||
swlng?: number;
|
||||
// TODO: technically this is not any object but a "Taxon"
|
||||
// and should be typed as such (e.g., in realm model)
|
||||
taxon: object | undefined | null,
|
||||
taxon_id: number | undefined | null,
|
||||
taxon: object | undefined | null;
|
||||
taxon_id: number | undefined | null;
|
||||
// TODO: technically this is not any object but a "User"
|
||||
// and should be typed as such (e.g., in realm model)
|
||||
user: object | undefined | null,
|
||||
user_id: number | undefined | null,
|
||||
excludeUser: object | undefined | null,
|
||||
verifiable: boolean,
|
||||
wildStatus: WILD_STATUS
|
||||
user: object | undefined | null;
|
||||
user_id: number | undefined | null;
|
||||
excludeUser: object | undefined | null;
|
||||
verifiable: boolean;
|
||||
wildStatus: WILD_STATUS;
|
||||
}
|
||||
type Action = {type: EXPLORE_ACTION.RESET}
|
||||
| {type: EXPLORE_ACTION.DISCARD, snapshot: State}
|
||||
| {type: EXPLORE_ACTION.SET_USER, user: object | null, userId: number | null, storedState: State}
|
||||
| {type: EXPLORE_ACTION.DISCARD; snapshot: State}
|
||||
| {type: EXPLORE_ACTION.SET_USER; user: object | null; userId: number | null; storedState: State}
|
||||
| {
|
||||
type: EXPLORE_ACTION.EXCLUDE_USER,
|
||||
user: null,
|
||||
userId: null,
|
||||
excludeUser: object,
|
||||
storedState: State
|
||||
type: EXPLORE_ACTION.EXCLUDE_USER;
|
||||
user: null;
|
||||
userId: null;
|
||||
excludeUser: object;
|
||||
storedState: State;
|
||||
}
|
||||
| {
|
||||
type: EXPLORE_ACTION.CHANGE_TAXON,
|
||||
taxon: { id: number } | null,
|
||||
taxonId: number,
|
||||
taxonName: string,
|
||||
storedState?: State
|
||||
type: EXPLORE_ACTION.CHANGE_TAXON;
|
||||
taxon: { id: number } | null;
|
||||
taxonId: number;
|
||||
taxonName: string;
|
||||
storedState?: State;
|
||||
}
|
||||
| { type: EXPLORE_ACTION.FILTER_BY_ICONIC_TAXON_UNKNOWN }
|
||||
| {type: EXPLORE_ACTION.SET_EXPLORE_LOCATION, exploreLocation: DefaultLocation}
|
||||
| {type: EXPLORE_ACTION.SET_EXPLORE_LOCATION; exploreLocation: DefaultLocation}
|
||||
| {
|
||||
type: EXPLORE_ACTION.SET_PLACE,
|
||||
place: PLACE,
|
||||
placeId: number,
|
||||
placeGuess?: string,
|
||||
lat: number,
|
||||
lng: number,
|
||||
radius: number,
|
||||
storedState: State
|
||||
type: EXPLORE_ACTION.SET_PLACE;
|
||||
place: PLACE;
|
||||
placeId: number;
|
||||
placeGuess?: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
radius: number;
|
||||
storedState: State;
|
||||
}
|
||||
| {type: EXPLORE_ACTION.SET_PLACE_MODE_NEARBY}
|
||||
| {type: EXPLORE_ACTION.SET_PLACE_MODE_WORLDWIDE}
|
||||
| {type: EXPLORE_ACTION.SET_PLACE_MODE_MAP_AREA}
|
||||
| {type: EXPLORE_ACTION.SET_PLACE_MODE_PLACE}
|
||||
| {
|
||||
type: EXPLORE_ACTION.SET_PROJECT,
|
||||
project: object | null,
|
||||
projectId: number | null,
|
||||
storedState: State
|
||||
type: EXPLORE_ACTION.SET_PROJECT;
|
||||
project: object | null;
|
||||
projectId: number | null;
|
||||
storedState: State;
|
||||
}
|
||||
| {type: EXPLORE_ACTION.CHANGE_SORT_BY, sortBy: SORT_BY}
|
||||
| {type: EXPLORE_ACTION.CHANGE_SORT_BY; sortBy: SORT_BY}
|
||||
| {type: EXPLORE_ACTION.TOGGLE_RESEARCH_GRADE}
|
||||
| {type: EXPLORE_ACTION.TOGGLE_NEEDS_ID}
|
||||
| {type: EXPLORE_ACTION.TOGGLE_CASUAL}
|
||||
| {type: EXPLORE_ACTION.SET_HIGHEST_TAXONOMIC_RANK, hrank: TAXONOMIC_RANK}
|
||||
| {type: EXPLORE_ACTION.SET_LOWEST_TAXONOMIC_RANK, lrank: TAXONOMIC_RANK}
|
||||
| {type: EXPLORE_ACTION.SET_HIGHEST_TAXONOMIC_RANK; hrank: TAXONOMIC_RANK}
|
||||
| {type: EXPLORE_ACTION.SET_LOWEST_TAXONOMIC_RANK; lrank: TAXONOMIC_RANK}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_ALL}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_EXACT, observedOn: string}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_RANGE, d1: string, d2: string}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_MONTHS, months: number[]}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_EXACT; observedOn: string}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_RANGE; d1: string; d2: string}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_OBSERVED_MONTHS; months: number[]}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_UPLOADED_ALL}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_UPLOADED_EXACT, createdOn: string}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_UPLOADED_RANGE, createdD1: string, createdD2: string}
|
||||
| {type: EXPLORE_ACTION.SET_MEDIA, media: MEDIA}
|
||||
| {type: EXPLORE_ACTION.SET_ESTABLISHMENT_MEAN, establishmentMean: ESTABLISHMENT_MEAN}
|
||||
| {type: EXPLORE_ACTION.SET_WILD_STATUS, wildStatus: WILD_STATUS}
|
||||
| {type: EXPLORE_ACTION.SET_REVIEWED, reviewedFilter: REVIEWED}
|
||||
| {type: EXPLORE_ACTION.SET_PHOTO_LICENSE, photoLicense: PHOTO_LICENSE}
|
||||
| {type: EXPLORE_ACTION.SET_MAP_BOUNDARIES, mapBoundaries: MapBoundaries}
|
||||
| {type: EXPLORE_ACTION.USE_STORED_STATE, storedState: State}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_UPLOADED_EXACT; createdOn: string}
|
||||
| {type: EXPLORE_ACTION.SET_DATE_UPLOADED_RANGE; createdD1: string; createdD2: string}
|
||||
| {type: EXPLORE_ACTION.SET_MEDIA; media: MEDIA}
|
||||
| {type: EXPLORE_ACTION.SET_ESTABLISHMENT_MEAN; establishmentMean: ESTABLISHMENT_MEAN}
|
||||
| {type: EXPLORE_ACTION.SET_WILD_STATUS; wildStatus: WILD_STATUS}
|
||||
| {type: EXPLORE_ACTION.SET_REVIEWED; reviewedFilter: REVIEWED}
|
||||
| {type: EXPLORE_ACTION.SET_PHOTO_LICENSE; photoLicense: PHOTO_LICENSE}
|
||||
| {type: EXPLORE_ACTION.SET_MAP_BOUNDARIES; mapBoundaries: MapBoundaries}
|
||||
| {type: EXPLORE_ACTION.USE_STORED_STATE; storedState: State}
|
||||
type Dispatch = ( action: Action ) => void
|
||||
|
||||
const ExploreContext = React.createContext<
|
||||
|
||||
@@ -79,8 +79,8 @@ class ObservationPhoto extends Realm.Object {
|
||||
// I think it is only called after certain transformations on the Realm result,
|
||||
// but it is not important for my current linear ticket so I'll skip typing it more
|
||||
static mapObservationPhotoForMyObsDefaultMode( observationPhoto: {
|
||||
photo?: { url?: string, localFilePath?: string },
|
||||
uuid?: string
|
||||
photo?: { url?: string; localFilePath?: string };
|
||||
uuid?: string;
|
||||
} ) {
|
||||
return {
|
||||
photo: {
|
||||
@@ -105,7 +105,7 @@ class ObservationPhoto extends Realm.Object {
|
||||
|
||||
static createObsPhotosWithPosition = async (
|
||||
photos: string[] | { image: { uri: string } }[],
|
||||
{ position, local }: { position: number, local: boolean }
|
||||
{ position, local }: { position: number; local: boolean }
|
||||
) => {
|
||||
let photoPosition = position;
|
||||
return Promise.all(
|
||||
@@ -128,7 +128,7 @@ class ObservationPhoto extends Realm.Object {
|
||||
// linear ticket so I'll skip typing it
|
||||
static async deleteRemotePhoto(
|
||||
uri: string,
|
||||
currentObservation?: { observationPhotos?: { photo: { url?: string }, uuid: string }[] }
|
||||
currentObservation?: { observationPhotos?: { photo: { url?: string }; uuid: string }[] }
|
||||
) {
|
||||
const obsPhotoToDelete = currentObservation?.observationPhotos?.find(
|
||||
p => p.photo?.url === uri
|
||||
@@ -155,7 +155,7 @@ class ObservationPhoto extends Realm.Object {
|
||||
// linear ticket so I'll skip typing it
|
||||
static async deletePhoto(
|
||||
uri: string,
|
||||
currentObservation?: { observationPhotos?: { photo: { url?: string }, uuid: string }[] }
|
||||
currentObservation?: { observationPhotos?: { photo: { url?: string }; uuid: string }[] }
|
||||
) {
|
||||
if ( uri.includes( "https://" ) ) {
|
||||
ObservationPhoto.deleteRemotePhoto( uri, currentObservation );
|
||||
@@ -170,8 +170,8 @@ class ObservationPhoto extends Realm.Object {
|
||||
// linear ticket so I'll skip typing it
|
||||
static mapObsPhotoUris(
|
||||
observation: {
|
||||
observationPhotos?: { photo: RealmPhoto }[],
|
||||
observation_photos?: { photo: RealmPhoto }[]
|
||||
observationPhotos?: { photo: RealmPhoto }[];
|
||||
observation_photos?: { photo: RealmPhoto }[];
|
||||
}
|
||||
) {
|
||||
const obsPhotos = observation?.observationPhotos || observation?.observation_photos;
|
||||
@@ -190,8 +190,8 @@ class ObservationPhoto extends Realm.Object {
|
||||
// linear ticket so I'll skip typing it
|
||||
static mapInnerPhotos(
|
||||
observation: {
|
||||
observationPhotos?: { photo: object }[],
|
||||
observation_photos?: { photo: object }[]
|
||||
observationPhotos?: { photo: object }[];
|
||||
observation_photos?: { photo: object }[];
|
||||
}
|
||||
) {
|
||||
const obsPhotos = observation?.observationPhotos || observation?.observation_photos;
|
||||
|
||||
@@ -11,14 +11,14 @@ const logger = log.extend( "clearCaches.ts" );
|
||||
interface RealmObservation {
|
||||
observationPhotos: {
|
||||
photo: {
|
||||
localFilePath: string
|
||||
}
|
||||
}[],
|
||||
localFilePath: string;
|
||||
};
|
||||
}[];
|
||||
observationSounds: {
|
||||
sound: {
|
||||
file_url: string
|
||||
}
|
||||
}[]
|
||||
file_url: string;
|
||||
};
|
||||
}[];
|
||||
}
|
||||
|
||||
const clearRotatedOriginalPhotosDirectory = async ( ) => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user