From 7066956197d3832a75596c4a488466caea9f940b Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:02:47 -0600 Subject: [PATCH 1/7] MOB-1104: rm unused code from ObsDetailsDefaultModeContainer --- .../ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js b/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js index e968ffbf6..728c9e2ab 100644 --- a/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js +++ b/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js @@ -94,7 +94,6 @@ const reducer = ( state, action ) => { case SET_ADD_COMMENT_SHEET: return { ...state, - commentIsOptional: action.commentIsOptional, showAddCommentSheet: action.showAddCommentSheet, }; default: @@ -228,18 +227,16 @@ const ObsDetailsDefaultModeContainer = ( props: Props ): Node => { !!currentUser && !!observation, ); - const openAddCommentSheet = useCallback( ( { isOptional = false } ) => { + const openAddCommentSheet = useCallback( ( ) => { dispatch( { type: SET_ADD_COMMENT_SHEET, showAddCommentSheet: true, - commentIsOptional: isOptional || false, } ); }, [] ); const hideAddCommentSheet = useCallback( ( ) => dispatch( { type: SET_ADD_COMMENT_SHEET, showAddCommentSheet: false, - comment: null, } ), [] ); const openAgreeWithIdSheet = useCallback( taxon => { From 70110dab0d08283cf8404e5c67b1a29e14979912 Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:31:52 -0600 Subject: [PATCH 2/7] MOB-1104: use hook for shared logic --- .../ObsDetails/ObsDetailsContainer.js | 309 ++------------- .../ObsDetailsDefaultModeContainer.js | 295 ++------------ .../hooks/useObsDetailsSharedLogic.js | 374 ++++++++++++++++++ 3 files changed, 444 insertions(+), 534 deletions(-) create mode 100644 src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js diff --git a/src/components/ObsDetails/ObsDetailsContainer.js b/src/components/ObsDetails/ObsDetailsContainer.js index e5b9d9e83..149ee3e24 100644 --- a/src/components/ObsDetails/ObsDetailsContainer.js +++ b/src/components/ObsDetails/ObsDetailsContainer.js @@ -2,41 +2,29 @@ import { useNetInfo, } from "@react-native-community/netinfo"; -import { useFocusEffect, useNavigation, useRoute } from "@react-navigation/native"; -import { useQueryClient } from "@tanstack/react-query"; -import { fetchSubscriptions } from "api/observations"; +import { useRoute } from "@react-navigation/native"; import IdentificationSheets from "components/ObsDetailsDefaultMode/IdentificationSheets"; import useMarkViewedMutation from "components/ObsDetailsSharedComponents/hooks/useMarkViewedMutation"; -import { RealmContext } from "providers/contexts"; +import useObsDetailsSharedLogic + from "components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic"; import type { Node } from "react"; import React, { - useCallback, - useEffect, - useReducer, useState, } from "react"; import { LogBox } from "react-native"; import Observation from "realmModels/Observation"; -import safeRealmWrite from "sharedHelpers/safeRealmWrite"; import { - useAuthenticatedQuery, useCurrentUser, useLayoutPrefs, useLocalObservation, - useObservationsUpdates, useTranslation, } from "sharedHooks"; -import useRemoteObservation, { - fetchRemoteObservationKey, -} from "sharedHooks/useRemoteObservation"; +import useRemoteObservation from "sharedHooks/useRemoteObservation"; import { OBS_DETAILS_TAB } from "stores/createLayoutSlice"; -import useStore from "stores/useStore"; import ObsDetails from "./ObsDetails"; -const { useRealm } = RealmContext; - // this is getting triggered by passing dates, like _created_at, through // react navigation via the observation object. it doesn't seem to // actually be breaking anything, for the moment (May 2, 2022) @@ -44,77 +32,7 @@ LogBox.ignoreLogs( [ "Non-serializable values were found in the navigation state", ] ); -const sortItems = ( ids, comments ) => ids.concat( [...comments] ).sort( - ( a, b ) => ( new Date( a.created_at ) - new Date( b.created_at ) ), -); - -const initialState = { - activityItems: [], - addingActivityItem: false, - agreeIdentification: null, - observationShown: null, - showAddCommentSheet: false, - showAgreeWithIdSheet: false, -}; - -const SHOW_AGREE_SHEET = "SHOW_AGREE_SHEET"; -const HIDE_AGREE_SHEET = "HIDE_AGREE_SHEET"; -const SET_ADD_COMMENT_SHEET = "SET_ADD_COMMENT_SHEET"; -const SET_INITIAL_OBSERVATION = "SET_INITIAL_OBSERVATION"; -const ADD_ACTIVITY_ITEM = "ADD_ACTIVITY_ITEM"; -const LOADING_ACTIVITY_ITEM = "LOADING_ACTIVITY_ITEM"; - -const reducer = ( state, action ) => { - switch ( action.type ) { - case SET_INITIAL_OBSERVATION: - return { - ...state, - observationShown: action.observationShown, - activityItems: sortItems( - action.observationShown?.identifications || [], - action.observationShown?.comments || [], - ), - }; - case ADD_ACTIVITY_ITEM: - return { - ...state, - observationShown: action.observationShown, - addingActivityItem: false, - activityItems: sortItems( - action.observationShown?.identifications || [], - action.observationShown?.comments || [], - ), - }; - case LOADING_ACTIVITY_ITEM: - return { - ...state, - addingActivityItem: true, - }; - - case SHOW_AGREE_SHEET: - return { - ...state, - showAgreeWithIdSheet: true, - agreeIdentification: action.agreeIdentification, - }; - case HIDE_AGREE_SHEET: - return { - ...state, - showAgreeWithIdSheet: false, - agreeIdentification: null, - }; - case SET_ADD_COMMENT_SHEET: - return { - ...state, - showAddCommentSheet: action.showAddCommentSheet, - }; - default: - throw new Error( ); - } -}; - const ObsDetailsContainer = ( ): Node => { - const setObservations = useStore( state => state.setObservations ); const { obsDetailsTab, setObsDetailsTab, @@ -125,23 +43,10 @@ const ObsDetailsContainer = ( ): Node => { targetActivityItemID, uuid, } = params; - const navigation = useNavigation( ); - const realm = useRealm( ); const { t } = useTranslation( ); const { isConnected } = useNetInfo( ); - const [state, dispatch] = useReducer( reducer, initialState ); const [remoteObsWasDeleted, setRemoteObsWasDeleted] = useState( false ); - const { - activityItems, - addingActivityItem, - agreeIdentification, - observationShown, - showAddCommentSheet, - showAgreeWithIdSheet, - } = state; - const queryClient = useQueryClient( ); - const { localObservation, markDeletedLocally, @@ -163,23 +68,6 @@ const ObsDetailsContainer = ( ): Node => { useMarkViewedMutation( localObservation, markViewedLocally, remoteObservation ); - // If we tried to get a remote observation but it no longer exists, the user - // can't do anything so we need to send them back and remove the local - // copy of this observation - useEffect( ( ) => { - setRemoteObsWasDeleted( fetchRemoteObservationError?.status === 404 ); - }, [fetchRemoteObservationError?.status] ); - const confirmRemoteObsWasDeleted = useCallback( ( ) => { - if ( localObservation ) { - markDeletedLocally( ); - } - if ( navigation.canGoBack( ) ) navigation.goBack( ); - }, [ - localObservation, - markDeletedLocally, - navigation, - ] ); - const observation = localObservation || Observation.mapApiToRealm( remoteObservation ); // In theory the only situation in which an observation would not have a @@ -191,51 +79,42 @@ const ObsDetailsContainer = ( ): Node => { || ( !observation?.user && !observation?.id ) ); - const { data: subscriptions, refetch: refetchSubscriptions } = useAuthenticatedQuery( - [ - "fetchSubscriptions", - ], - optsWithAuth => fetchSubscriptions( { uuid, fields: "user_id" }, optsWithAuth ), - { - enabled: !!( currentUser ) && !belongsToCurrentUser, - }, - ); - - const invalidateRemoteObservationFetch = useCallback( ( ) => { - if ( observation?.uuid ) { - queryClient.invalidateQueries( { - queryKey: [fetchRemoteObservationKey, observation.uuid], - } ); - } - }, [queryClient, observation?.uuid] ); - - useFocusEffect( - // this ensures activity items load after a user taps suggest id - // and adds a remote id on the Suggestions screen - useCallback( ( ) => { - invalidateRemoteObservationFetch( ); - }, [invalidateRemoteObservationFetch] ), - ); - - useEffect( ( ) => { - if ( !observationShown ) { - dispatch( { - type: SET_INITIAL_OBSERVATION, - observationShown: observation, - } ); - } - }, [observation, observationShown] ); - - useEffect( ( ) => { - // if observation does not belong to current user, show - // new activity items after a refetch - if ( remoteObservation && !isRefetching ) { - dispatch( { - type: ADD_ACTIVITY_ITEM, - observationShown: Observation.mapApiToRealm( remoteObservation ), - } ); - } - }, [remoteObservation, isRefetching] ); + const { + activityItems, + addingActivityItem, + agreeIdentification, + observationShown, + showAddCommentSheet, + showAgreeWithIdSheet, + subscriptionResults, + openAddCommentSheet, + hideAddCommentSheet, + openAgreeWithIdSheet, + closeAgreeWithIdSheet, + navToSuggestions, + invalidateQueryAndRefetch, + handleIdentificationMutationSuccess, + handleCommentMutationSuccess, + confirmRemoteObsWasDeleted, + loadActivityItem, + refetchSubscriptions, + } = useObsDetailsSharedLogic( { + observation, + uuid, + targetActivityItemID, + localObservation, + remoteObservation, + markViewedLocally, + markDeletedLocally, + setRemoteObsWasDeleted, + fetchRemoteObservationError, + currentUser, + belongsToCurrentUser, + isRefetching, + refetchRemoteObservation, + isConnected, + remoteObsWasDeleted, + } ); const tabs = [ { @@ -252,116 +131,8 @@ const ObsDetailsContainer = ( ): Node => { }, ]; - const hasPhotos = observation?.observationPhotos?.length > 0; - - const { refetch: refetchObservationUpdates } = useObservationsUpdates( - !!currentUser && !!observation, - ); - - const openAddCommentSheet = useCallback( ( ) => { - dispatch( { - type: SET_ADD_COMMENT_SHEET, - showAddCommentSheet: true, - } ); - }, [] ); - - const hideAddCommentSheet = useCallback( ( ) => dispatch( { - type: SET_ADD_COMMENT_SHEET, - showAddCommentSheet: false, - } ), [] ); - - const openAgreeWithIdSheet = useCallback( taxon => { - dispatch( { - type: SHOW_AGREE_SHEET, - agreeIdentification: { taxon }, - } ); - }, [] ); - - const navToSuggestions = useCallback( ( ) => { - setObservations( [observation] ); - if ( hasPhotos ) { - navigation.push( "Suggestions", { - entryScreen: "ObsDetails", - lastScreen: "ObsDetails", - hideSkip: true, - } ); - } else { - // Go directly to taxon search in case there are no photos - navigation.navigate( "SuggestionsTaxonSearch", { lastScreen: "ObsDetails" } ); - } - }, [hasPhotos, navigation, observation, setObservations] ); - const showActivityTab = obsDetailsTab === OBS_DETAILS_TAB.ACTIVITY; - const invalidateQueryAndRefetch = useCallback( ( ) => { - invalidateRemoteObservationFetch( ); - refetchRemoteObservation( ); - refetchObservationUpdates( ); - }, [invalidateRemoteObservationFetch, refetchObservationUpdates, refetchRemoteObservation] ); - - const subscriptionResults = !belongsToCurrentUser - ? subscriptions?.results - : []; - - const handleIdentificationMutationSuccess = useCallback( data => { - refetchRemoteObservation( ); - if ( belongsToCurrentUser ) { - const createdIdent = data[0]; - // Try to find an existing taxon b/c otherwise realm will try to - // create the taxon when updating the observation and error out - let taxon; - if ( createdIdent.taxon?.id ) { - taxon = realm?.objectForPrimaryKey( "Taxon", createdIdent.taxon.id ); - } - taxon = taxon || createdIdent.taxon; - safeRealmWrite( realm, ( ) => { - createdIdent.user = currentUser; - if ( taxon ) createdIdent.taxon = taxon; - localObservation?.identifications?.push( createdIdent ); - }, "setting local identification in ObsDetailsContainer" ); - if ( uuid ) { - const updatedLocalObservation = realm.objectForPrimaryKey( "Observation", uuid ); - dispatch( { type: ADD_ACTIVITY_ITEM, observationShown: updatedLocalObservation } ); - } - } - }, [ - belongsToCurrentUser, - currentUser, - localObservation?.identifications, - realm, - refetchRemoteObservation, - uuid, - ] ); - - const handleCommentMutationSuccess = useCallback( data => { - refetchRemoteObservation( ); - if ( belongsToCurrentUser ) { - safeRealmWrite( realm, ( ) => { - const localComments = localObservation?.comments; - const newComment = data[0]; - newComment.user = currentUser; - localComments?.push( newComment ); - }, "setting local comment in ObsDetailsContainer" ); - const updatedLocalObservation = realm.objectForPrimaryKey( "Observation", uuid ); - dispatch( { type: ADD_ACTIVITY_ITEM, observationShown: updatedLocalObservation } ); - } - }, [ - belongsToCurrentUser, - currentUser, - localObservation?.comments, - realm, - refetchRemoteObservation, - uuid, - ] ); - - const closeAgreeWithIdSheet = useCallback( ( ) => { - dispatch( { type: HIDE_AGREE_SHEET } ); - }, [] ); - - const loadActivityItem = useCallback( ( ) => { - dispatch( { type: LOADING_ACTIVITY_ITEM } ); - }, [] ); - return observationShown && ( <> ids.concat( [...comments] ).sort( - ( a, b ) => ( new Date( a.created_at ) - new Date( b.created_at ) ), -); - -const SHOW_AGREE_SHEET = "SHOW_AGREE_SHEET"; -const HIDE_AGREE_SHEET = "HIDE_AGREE_SHEET"; -const SET_ADD_COMMENT_SHEET = "SET_ADD_COMMENT_SHEET"; -const SET_INITIAL_OBSERVATION = "SET_INITIAL_OBSERVATION"; -const ADD_ACTIVITY_ITEM = "ADD_ACTIVITY_ITEM"; -const LOADING_ACTIVITY_ITEM = "LOADING_ACTIVITY_ITEM"; - -const initialState = { - activityItems: [], - addingActivityItem: false, - observationShown: null, - showAddCommentSheet: false, -}; - -const reducer = ( state, action ) => { - switch ( action.type ) { - case SET_INITIAL_OBSERVATION: - return { - ...state, - observationShown: action.observationShown, - activityItems: sortItems( - action.observationShown?.identifications || [], - action.observationShown?.comments || [], - ), - }; - case ADD_ACTIVITY_ITEM: - return { - ...state, - observationShown: action.observationShown, - addingActivityItem: false, - activityItems: sortItems( - action.observationShown?.identifications || [], - action.observationShown?.comments || [], - ), - }; - case LOADING_ACTIVITY_ITEM: - return { - ...state, - addingActivityItem: true, - }; - case SHOW_AGREE_SHEET: - return { - ...state, - showAgreeWithIdSheet: true, - agreeIdentification: action.agreeIdentification, - }; - case HIDE_AGREE_SHEET: - return { - ...state, - showAgreeWithIdSheet: false, - agreeIdentification: null, - }; - case SET_ADD_COMMENT_SHEET: - return { - ...state, - showAddCommentSheet: action.showAddCommentSheet, - }; - default: - throw new Error( ); - } -}; - type Props = { belongsToCurrentUser: boolean, currentUser: ?Object, @@ -120,11 +36,6 @@ type Props = { } const ObsDetailsDefaultModeContainer = ( props: Props ): Node => { - const setObservations = useStore( state => state.setObservations ); - const navigation = useNavigation( ); - const realm = useRealm( ); - const [state, dispatch] = useReducer( reducer, initialState ); - const { observation, targetActivityItemID, @@ -143,6 +54,8 @@ const ObsDetailsDefaultModeContainer = ( props: Props ): Node => { remoteObsWasDeleted, } = props; + useMarkViewedMutation( localObservation, markViewedLocally, remoteObservation ); + const { activityItems, addingActivityItem, @@ -150,184 +63,36 @@ const ObsDetailsDefaultModeContainer = ( props: Props ): Node => { observationShown, showAddCommentSheet, showAgreeWithIdSheet, - } = state; - const queryClient = useQueryClient( ); - - useMarkViewedMutation( localObservation, markViewedLocally, remoteObservation ); - - // If we tried to get a remote observation but it no longer exists, the user - // can't do anything so we need to send them back and remove the local - // copy of this observation - useEffect( ( ) => { - setRemoteObsWasDeleted( fetchRemoteObservationError?.status === 404 ); - }, [fetchRemoteObservationError?.status, setRemoteObsWasDeleted] ); - - const confirmRemoteObsWasDeleted = useCallback( ( ) => { - if ( localObservation ) { - markDeletedLocally( ); - } - if ( navigation.canGoBack( ) ) navigation.goBack( ); - }, [ + subscriptionResults, + wasSynced, + openAddCommentSheet, + hideAddCommentSheet, + openAgreeWithIdSheet, + closeAgreeWithIdSheet, + navToSuggestions, + invalidateQueryAndRefetch, + handleIdentificationMutationSuccess, + handleCommentMutationSuccess, + confirmRemoteObsWasDeleted, + loadActivityItem, + refetchSubscriptions, + } = useObsDetailsSharedLogic( { + observation, + uuid, + targetActivityItemID, localObservation, + remoteObservation, + markViewedLocally, markDeletedLocally, - navigation, - ] ); - - const wasSynced = !!( localObservation && localObservation?.wasSynced() ); - - const hasPhotos = observation?.observationPhotos?.length > 0; - - const { data: subscriptions, refetch: refetchSubscriptions } = useAuthenticatedQuery( - [ - "fetchSubscriptions", - ], - optsWithAuth => fetchSubscriptions( { uuid, fields: "user_id" }, optsWithAuth ), - { - enabled: !!( currentUser ) && !belongsToCurrentUser, - }, - ); - - const invalidateRemoteObservationFetch = useCallback( ( ) => { - if ( observation?.uuid ) { - queryClient.invalidateQueries( { - queryKey: [fetchRemoteObservationKey, observation.uuid], - } ); - } - }, [queryClient, observation?.uuid] ); - - useFocusEffect( - // this ensures activity items load after a user taps suggest id - // and adds a remote id on the Suggestions screen - useCallback( ( ) => { - invalidateRemoteObservationFetch( ); - }, [invalidateRemoteObservationFetch] ), - ); - - useEffect( ( ) => { - if ( !observationShown ) { - dispatch( { - type: SET_INITIAL_OBSERVATION, - observationShown: observation, - } ); - } - }, [observation, observationShown] ); - - useEffect( ( ) => { - // if observation does not belong to current user, show - // new activity items after a refetch - if ( remoteObservation && !isRefetching ) { - dispatch( { - type: ADD_ACTIVITY_ITEM, - observationShown: Observation.mapApiToRealm( remoteObservation ), - } ); - } - }, [remoteObservation, isRefetching] ); - - const { refetch: refetchObservationUpdates } = useObservationsUpdates( - !!currentUser && !!observation, - ); - - const openAddCommentSheet = useCallback( ( ) => { - dispatch( { - type: SET_ADD_COMMENT_SHEET, - showAddCommentSheet: true, - } ); - }, [] ); - - const hideAddCommentSheet = useCallback( ( ) => dispatch( { - type: SET_ADD_COMMENT_SHEET, - showAddCommentSheet: false, - } ), [] ); - - const openAgreeWithIdSheet = useCallback( taxon => { - dispatch( { - type: SHOW_AGREE_SHEET, - agreeIdentification: { taxon }, - } ); - }, [] ); - - const navToSuggestions = useCallback( ( ) => { - setObservations( [observation] ); - if ( hasPhotos ) { - navigation.push( "Suggestions", { - entryScreen: "ObsDetails", - lastScreen: "ObsDetails", - hideSkip: true, - } ); - } else { - // Go directly to taxon search in case there are no photos - navigation.navigate( "SuggestionsTaxonSearch", { lastScreen: "ObsDetails" } ); - } - }, [hasPhotos, navigation, observation, setObservations] ); - - const invalidateQueryAndRefetch = useCallback( ( ) => { - invalidateRemoteObservationFetch( ); - refetchRemoteObservation( ); - refetchObservationUpdates( ); - }, [invalidateRemoteObservationFetch, refetchObservationUpdates, refetchRemoteObservation] ); - - const subscriptionResults = !belongsToCurrentUser - ? subscriptions?.results - : []; - - const handleIdentificationMutationSuccess = useCallback( data => { - refetchRemoteObservation( ); - if ( belongsToCurrentUser ) { - const createdIdent = data[0]; - // Try to find an existing taxon b/c otherwise realm will try to - // create the taxon when updating the observation and error out - let taxon; - if ( createdIdent.taxon?.id ) { - taxon = realm?.objectForPrimaryKey( "Taxon", createdIdent.taxon.id ); - } - taxon = taxon || createdIdent.taxon; - safeRealmWrite( realm, ( ) => { - createdIdent.user = currentUser; - if ( taxon ) createdIdent.taxon = taxon; - localObservation?.identifications?.push( createdIdent ); - }, "setting local identification in ObsDetailsContainer" ); - if ( uuid ) { - const updatedLocalObservation = realm.objectForPrimaryKey( "Observation", uuid ); - dispatch( { type: ADD_ACTIVITY_ITEM, observationShown: updatedLocalObservation } ); - } - } - }, [ - belongsToCurrentUser, + setRemoteObsWasDeleted, + fetchRemoteObservationError, currentUser, - localObservation?.identifications, - realm, - refetchRemoteObservation, - uuid, - ] ); - - const handleCommentMutationSuccess = useCallback( data => { - refetchRemoteObservation( ); - if ( belongsToCurrentUser ) { - safeRealmWrite( realm, ( ) => { - const localComments = localObservation?.comments; - const newComment = data[0]; - newComment.user = currentUser; - localComments?.push( newComment ); - }, "setting local comment in ObsDetailsContainer" ); - const updatedLocalObservation = realm.objectForPrimaryKey( "Observation", uuid ); - dispatch( { type: ADD_ACTIVITY_ITEM, observationShown: updatedLocalObservation } ); - } - }, [ belongsToCurrentUser, - currentUser, - localObservation?.comments, - realm, + isRefetching, refetchRemoteObservation, - uuid, - ] ); - - const closeAgreeWithIdSheet = useCallback( ( ) => { - dispatch( { type: HIDE_AGREE_SHEET } ); - }, [] ); - - const loadActivityItem = useCallback( ( ) => { - dispatch( { type: LOADING_ACTIVITY_ITEM } ); - }, [] ); + isConnected, + remoteObsWasDeleted, + } ); return observationShown && ( <> diff --git a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js new file mode 100644 index 000000000..c84a1a7d4 --- /dev/null +++ b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js @@ -0,0 +1,374 @@ +// @flow +/* eslint-disable @typescript-eslint/consistent-type-definitions */ +import { useFocusEffect, useNavigation } from "@react-navigation/native"; +import { useQueryClient } from "@tanstack/react-query"; +import { fetchSubscriptions } from "api/observations"; +import { RealmContext } from "providers/contexts"; +import { + useCallback, + useEffect, + useReducer, +} from "react"; +import Observation from "realmModels/Observation"; +import safeRealmWrite from "sharedHelpers/safeRealmWrite"; +import { + useAuthenticatedQuery, + useObservationsUpdates, +} from "sharedHooks"; +import { + fetchRemoteObservationKey, +} from "sharedHooks/useRemoteObservation"; +import useStore from "stores/useStore"; + +const { useRealm } = RealmContext; + +const sortItems = ( ids, comments ) => ids.concat( [...comments] ).sort( + ( a, b ) => ( new Date( a.created_at ) - new Date( b.created_at ) ), +); + +const SHOW_AGREE_SHEET = "SHOW_AGREE_SHEET"; +const HIDE_AGREE_SHEET = "HIDE_AGREE_SHEET"; +const SET_ADD_COMMENT_SHEET = "SET_ADD_COMMENT_SHEET"; +const SET_INITIAL_OBSERVATION = "SET_INITIAL_OBSERVATION"; +const ADD_ACTIVITY_ITEM = "ADD_ACTIVITY_ITEM"; +const LOADING_ACTIVITY_ITEM = "LOADING_ACTIVITY_ITEM"; + +const initialState = { + activityItems: [], + addingActivityItem: false, + agreeIdentification: null, + observationShown: null, + showAddCommentSheet: false, + showAgreeWithIdSheet: false, +}; + +const reducer = ( state, action ) => { + switch ( action.type ) { + case SET_INITIAL_OBSERVATION: + return { + ...state, + observationShown: action.observationShown, + activityItems: sortItems( + action.observationShown?.identifications || [], + action.observationShown?.comments || [], + ), + }; + case ADD_ACTIVITY_ITEM: + return { + ...state, + observationShown: action.observationShown, + addingActivityItem: false, + activityItems: sortItems( + action.observationShown?.identifications || [], + action.observationShown?.comments || [], + ), + }; + case LOADING_ACTIVITY_ITEM: + return { + ...state, + addingActivityItem: true, + }; + case SHOW_AGREE_SHEET: + return { + ...state, + showAgreeWithIdSheet: true, + agreeIdentification: action.agreeIdentification, + }; + case HIDE_AGREE_SHEET: + return { + ...state, + showAgreeWithIdSheet: false, + agreeIdentification: null, + }; + case SET_ADD_COMMENT_SHEET: + return { + ...state, + showAddCommentSheet: action.showAddCommentSheet, + }; + default: + throw new Error( ); + } +}; + +type UseObsDetailsSharedLogicParams = { + observation: Object, + uuid: string, + targetActivityItemID?: ?number, + localObservation: ?Object, + remoteObservation: ?Object, + markViewedLocally: ( ) => void, + markDeletedLocally: ( ) => void, + setRemoteObsWasDeleted: ( deleted: boolean ) => void, + fetchRemoteObservationError: ?Object, + currentUser: ?Object, + belongsToCurrentUser: boolean, + isRefetching: boolean, + refetchRemoteObservation: ( ) => void, + isConnected: boolean, + remoteObsWasDeleted: boolean, +} + +type UseObsDetailsSharedLogicReturn = { + // State + activityItems: Object[], + addingActivityItem: boolean, + agreeIdentification: ?Object, + observationShown: ?Object, + showAddCommentSheet: boolean, + showAgreeWithIdSheet: boolean, + + // Computed + hasPhotos: boolean, + subscriptionResults: Object[], + wasSynced: boolean, + + // Callbacks + openAddCommentSheet: () => void, + hideAddCommentSheet: () => void, + openAgreeWithIdSheet: ( taxon: Object ) => void, + closeAgreeWithIdSheet: () => void, + navToSuggestions: () => void, + invalidateQueryAndRefetch: () => void, + handleIdentificationMutationSuccess: ( data: Object[] ) => void, + handleCommentMutationSuccess: ( data: Object[] ) => void, + confirmRemoteObsWasDeleted: () => void, + loadActivityItem: () => void, + refetchSubscriptions: () => void, +} + +const useObsDetailsSharedLogic = ( + params: UseObsDetailsSharedLogicParams, +): UseObsDetailsSharedLogicReturn => { + const { + observation, + uuid, + localObservation, + markDeletedLocally, + remoteObservation, + setRemoteObsWasDeleted, + fetchRemoteObservationError, + currentUser, + belongsToCurrentUser, + isRefetching, + refetchRemoteObservation, + } = params; + + const setObservations = useStore( state => state.setObservations ); + const navigation = useNavigation( ); + const realm = useRealm( ); + const [state, dispatch] = useReducer( reducer, initialState ); + const queryClient = useQueryClient( ); + + const { + activityItems, + addingActivityItem, + agreeIdentification, + observationShown, + showAddCommentSheet, + showAgreeWithIdSheet, + } = state; + + // If we tried to get a remote observation but it no longer exists, the user + // can't do anything so we need to send them back and remove the local + // copy of this observation + useEffect( ( ) => { + setRemoteObsWasDeleted( fetchRemoteObservationError?.status === 404 ); + }, [fetchRemoteObservationError?.status, setRemoteObsWasDeleted] ); + + const confirmRemoteObsWasDeleted = useCallback( ( ) => { + if ( localObservation ) { + markDeletedLocally( ); + } + if ( navigation.canGoBack( ) ) navigation.goBack( ); + }, [ + localObservation, + markDeletedLocally, + navigation, + ] ); + + const wasSynced = !!( localObservation && localObservation?.wasSynced() ); + + const hasPhotos = observation?.observationPhotos?.length > 0; + + const { data: subscriptions, refetch: refetchSubscriptions } = useAuthenticatedQuery( + [ + "fetchSubscriptions", + ], + optsWithAuth => fetchSubscriptions( { uuid, fields: "user_id" }, optsWithAuth ), + { + enabled: !!( currentUser ) && !belongsToCurrentUser, + }, + ); + + const invalidateRemoteObservationFetch = useCallback( ( ) => { + if ( observation?.uuid ) { + queryClient.invalidateQueries( { + queryKey: [fetchRemoteObservationKey, observation.uuid], + } ); + } + }, [queryClient, observation?.uuid] ); + + useFocusEffect( + // this ensures activity items load after a user taps suggest id + // and adds a remote id on the Suggestions screen + useCallback( ( ) => { + invalidateRemoteObservationFetch( ); + }, [invalidateRemoteObservationFetch] ), + ); + + useEffect( ( ) => { + if ( !observationShown ) { + dispatch( { + type: SET_INITIAL_OBSERVATION, + observationShown: observation, + } ); + } + }, [observation, observationShown] ); + + useEffect( ( ) => { + // if observation does not belong to current user, show + // new activity items after a refetch + if ( remoteObservation && !isRefetching ) { + dispatch( { + type: ADD_ACTIVITY_ITEM, + observationShown: Observation.mapApiToRealm( remoteObservation ), + } ); + } + }, [remoteObservation, isRefetching] ); + + const { refetch: refetchObservationUpdates } = useObservationsUpdates( + !!currentUser && !!observation, + ); + + const openAddCommentSheet = useCallback( ( ) => { + dispatch( { + type: SET_ADD_COMMENT_SHEET, + showAddCommentSheet: true, + } ); + }, [] ); + + const hideAddCommentSheet = useCallback( ( ) => dispatch( { + type: SET_ADD_COMMENT_SHEET, + showAddCommentSheet: false, + } ), [] ); + + const openAgreeWithIdSheet = useCallback( taxon => { + dispatch( { + type: SHOW_AGREE_SHEET, + agreeIdentification: { taxon }, + } ); + }, [] ); + + const navToSuggestions = useCallback( ( ) => { + setObservations( [observation] ); + if ( hasPhotos ) { + navigation.push( "Suggestions", { + entryScreen: "ObsDetails", + lastScreen: "ObsDetails", + hideSkip: true, + } ); + } else { + // Go directly to taxon search in case there are no photos + navigation.navigate( "SuggestionsTaxonSearch", { lastScreen: "ObsDetails" } ); + } + }, [hasPhotos, navigation, observation, setObservations] ); + + const invalidateQueryAndRefetch = useCallback( ( ) => { + invalidateRemoteObservationFetch( ); + refetchRemoteObservation( ); + refetchObservationUpdates( ); + }, [invalidateRemoteObservationFetch, refetchObservationUpdates, refetchRemoteObservation] ); + + const subscriptionResults = !belongsToCurrentUser + ? subscriptions?.results + : []; + + const handleIdentificationMutationSuccess = useCallback( data => { + refetchRemoteObservation( ); + if ( belongsToCurrentUser ) { + const createdIdent = data[0]; + // Try to find an existing taxon b/c otherwise realm will try to + // create the taxon when updating the observation and error out + let taxon; + if ( createdIdent.taxon?.id ) { + taxon = realm?.objectForPrimaryKey( "Taxon", createdIdent.taxon.id ); + } + taxon = taxon || createdIdent.taxon; + safeRealmWrite( realm, ( ) => { + createdIdent.user = currentUser; + if ( taxon ) createdIdent.taxon = taxon; + localObservation?.identifications?.push( createdIdent ); + }, "setting local identification in ObsDetailsContainer" ); + if ( uuid ) { + const updatedLocalObservation = realm.objectForPrimaryKey( "Observation", uuid ); + dispatch( { type: ADD_ACTIVITY_ITEM, observationShown: updatedLocalObservation } ); + } + } + }, [ + belongsToCurrentUser, + currentUser, + localObservation?.identifications, + realm, + refetchRemoteObservation, + uuid, + ] ); + + const handleCommentMutationSuccess = useCallback( data => { + refetchRemoteObservation( ); + if ( belongsToCurrentUser ) { + safeRealmWrite( realm, ( ) => { + const localComments = localObservation?.comments; + const newComment = data[0]; + newComment.user = currentUser; + localComments?.push( newComment ); + }, "setting local comment in ObsDetailsContainer" ); + const updatedLocalObservation = realm.objectForPrimaryKey( "Observation", uuid ); + dispatch( { type: ADD_ACTIVITY_ITEM, observationShown: updatedLocalObservation } ); + } + }, [ + belongsToCurrentUser, + currentUser, + localObservation?.comments, + realm, + refetchRemoteObservation, + uuid, + ] ); + + const closeAgreeWithIdSheet = useCallback( ( ) => { + dispatch( { type: HIDE_AGREE_SHEET } ); + }, [] ); + + const loadActivityItem = useCallback( ( ) => { + dispatch( { type: LOADING_ACTIVITY_ITEM } ); + }, [] ); + + return { + // State + activityItems, + addingActivityItem, + agreeIdentification, + observationShown, + showAddCommentSheet, + showAgreeWithIdSheet, + + // Computed + hasPhotos, + subscriptionResults, + wasSynced, + + // Callbacks + openAddCommentSheet, + hideAddCommentSheet, + openAgreeWithIdSheet, + closeAgreeWithIdSheet, + navToSuggestions, + invalidateQueryAndRefetch, + handleIdentificationMutationSuccess, + handleCommentMutationSuccess, + confirmRemoteObsWasDeleted, + loadActivityItem, + refetchSubscriptions, + }; +}; + +export default useObsDetailsSharedLogic; From fc2ee2105b4a58972953fb462bf54118946e38eb Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:40:05 -0600 Subject: [PATCH 3/7] MOB-1104: hook to ts --- ...edLogic.js => useObsDetailsSharedLogic.ts} | 125 +++++++++++------- 1 file changed, 77 insertions(+), 48 deletions(-) rename src/components/ObsDetailsSharedComponents/hooks/{useObsDetailsSharedLogic.js => useObsDetailsSharedLogic.ts} (74%) diff --git a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts similarity index 74% rename from src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js rename to src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts index c84a1a7d4..65bca3b9f 100644 --- a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.js +++ b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts @@ -1,8 +1,8 @@ -// @flow -/* eslint-disable @typescript-eslint/consistent-type-definitions */ +import type { NavigationProp, ParamListBase } from "@react-navigation/native"; import { useFocusEffect, useNavigation } from "@react-navigation/native"; import { useQueryClient } from "@tanstack/react-query"; import { fetchSubscriptions } from "api/observations"; +import type { ApiComment, ApiIdentification } from "api/types"; import { RealmContext } from "providers/contexts"; import { useCallback, @@ -10,6 +10,7 @@ import { useReducer, } from "react"; import Observation from "realmModels/Observation"; +import type { RealmObservation, RealmTaxon, RealmUser } from "realmModels/types"; import safeRealmWrite from "sharedHelpers/safeRealmWrite"; import { useAuthenticatedQuery, @@ -22,8 +23,15 @@ import useStore from "stores/useStore"; const { useRealm } = RealmContext; -const sortItems = ( ids, comments ) => ids.concat( [...comments] ).sort( - ( a, b ) => ( new Date( a.created_at ) - new Date( b.created_at ) ), +interface ActivityItem { + created_at: string; +} + +const sortItems = ( + ids: ActivityItem[], + comments: ActivityItem[], +): ActivityItem[] => ids.concat( [...comments] ).sort( + ( a, b ) => ( new Date( a.created_at ).getTime() - new Date( b.created_at ).getTime() ), ); const SHOW_AGREE_SHEET = "SHOW_AGREE_SHEET"; @@ -33,7 +41,28 @@ const SET_INITIAL_OBSERVATION = "SET_INITIAL_OBSERVATION"; const ADD_ACTIVITY_ITEM = "ADD_ACTIVITY_ITEM"; const LOADING_ACTIVITY_ITEM = "LOADING_ACTIVITY_ITEM"; -const initialState = { +interface AgreeIdentification { + taxon: RealmTaxon; +} + +interface State { + activityItems: ActivityItem[]; + addingActivityItem: boolean; + agreeIdentification: AgreeIdentification | null; + observationShown: RealmObservation | null; + showAddCommentSheet: boolean; + showAgreeWithIdSheet: boolean; +} + +type Action = + | { type: typeof SET_INITIAL_OBSERVATION; observationShown: RealmObservation } + | { type: typeof ADD_ACTIVITY_ITEM; observationShown: RealmObservation } + | { type: typeof LOADING_ACTIVITY_ITEM } + | { type: typeof SHOW_AGREE_SHEET; agreeIdentification: AgreeIdentification } + | { type: typeof HIDE_AGREE_SHEET } + | { type: typeof SET_ADD_COMMENT_SHEET; showAddCommentSheet: boolean }; + +const initialState: State = { activityItems: [], addingActivityItem: false, agreeIdentification: null, @@ -42,7 +71,7 @@ const initialState = { showAgreeWithIdSheet: false, }; -const reducer = ( state, action ) => { +const reducer = ( state: State, action: Action ): State => { switch ( action.type ) { case SET_INITIAL_OBSERVATION: return { @@ -86,54 +115,54 @@ const reducer = ( state, action ) => { showAddCommentSheet: action.showAddCommentSheet, }; default: - throw new Error( ); + return state; } }; -type UseObsDetailsSharedLogicParams = { - observation: Object, - uuid: string, - targetActivityItemID?: ?number, - localObservation: ?Object, - remoteObservation: ?Object, - markViewedLocally: ( ) => void, - markDeletedLocally: ( ) => void, - setRemoteObsWasDeleted: ( deleted: boolean ) => void, - fetchRemoteObservationError: ?Object, - currentUser: ?Object, - belongsToCurrentUser: boolean, - isRefetching: boolean, - refetchRemoteObservation: ( ) => void, - isConnected: boolean, - remoteObsWasDeleted: boolean, +interface UseObsDetailsSharedLogicParams { + observation: RealmObservation; + uuid: string; + targetActivityItemID?: number | null; + localObservation: RealmObservation | null; + remoteObservation: RealmObservation | null; + markViewedLocally: ( ) => void; + markDeletedLocally: ( ) => void; + setRemoteObsWasDeleted: ( deleted: boolean ) => void; + fetchRemoteObservationError: { status?: number } | null; + currentUser: RealmUser | null; + belongsToCurrentUser: boolean; + isRefetching: boolean; + refetchRemoteObservation: ( ) => void; + isConnected: boolean; + remoteObsWasDeleted: boolean; } -type UseObsDetailsSharedLogicReturn = { +interface UseObsDetailsSharedLogicReturn { // State - activityItems: Object[], - addingActivityItem: boolean, - agreeIdentification: ?Object, - observationShown: ?Object, - showAddCommentSheet: boolean, - showAgreeWithIdSheet: boolean, + activityItems: ActivityItem[]; + addingActivityItem: boolean; + agreeIdentification: AgreeIdentification | null; + observationShown: RealmObservation | null; + showAddCommentSheet: boolean; + showAgreeWithIdSheet: boolean; // Computed - hasPhotos: boolean, - subscriptionResults: Object[], - wasSynced: boolean, + hasPhotos: boolean; + subscriptionResults: unknown[]; + wasSynced: boolean; // Callbacks - openAddCommentSheet: () => void, - hideAddCommentSheet: () => void, - openAgreeWithIdSheet: ( taxon: Object ) => void, - closeAgreeWithIdSheet: () => void, - navToSuggestions: () => void, - invalidateQueryAndRefetch: () => void, - handleIdentificationMutationSuccess: ( data: Object[] ) => void, - handleCommentMutationSuccess: ( data: Object[] ) => void, - confirmRemoteObsWasDeleted: () => void, - loadActivityItem: () => void, - refetchSubscriptions: () => void, + openAddCommentSheet: () => void; + hideAddCommentSheet: () => void; + openAgreeWithIdSheet: ( taxon: RealmTaxon ) => void; + closeAgreeWithIdSheet: () => void; + navToSuggestions: () => void; + invalidateQueryAndRefetch: () => void; + handleIdentificationMutationSuccess: ( data: ApiIdentification[] ) => void; + handleCommentMutationSuccess: ( data: ApiComment[] ) => void; + confirmRemoteObsWasDeleted: () => void; + loadActivityItem: () => void; + refetchSubscriptions: () => void; } const useObsDetailsSharedLogic = ( @@ -154,7 +183,7 @@ const useObsDetailsSharedLogic = ( } = params; const setObservations = useStore( state => state.setObservations ); - const navigation = useNavigation( ); + const navigation = useNavigation>( ); const realm = useRealm( ); const [state, dispatch] = useReducer( reducer, initialState ); const queryClient = useQueryClient( ); @@ -252,7 +281,7 @@ const useObsDetailsSharedLogic = ( showAddCommentSheet: false, } ), [] ); - const openAgreeWithIdSheet = useCallback( taxon => { + const openAgreeWithIdSheet = useCallback( ( taxon: RealmTaxon ) => { dispatch( { type: SHOW_AGREE_SHEET, agreeIdentification: { taxon }, @@ -283,7 +312,7 @@ const useObsDetailsSharedLogic = ( ? subscriptions?.results : []; - const handleIdentificationMutationSuccess = useCallback( data => { + const handleIdentificationMutationSuccess = useCallback( ( data: ApiIdentification[] ) => { refetchRemoteObservation( ); if ( belongsToCurrentUser ) { const createdIdent = data[0]; @@ -313,7 +342,7 @@ const useObsDetailsSharedLogic = ( uuid, ] ); - const handleCommentMutationSuccess = useCallback( data => { + const handleCommentMutationSuccess = useCallback( ( data: ApiComment[] ) => { refetchRemoteObservation( ); if ( belongsToCurrentUser ) { safeRealmWrite( realm, ( ) => { From e8b647c196112a7d040ff5f2ffacdeebd7924f0d Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:05:53 -0600 Subject: [PATCH 4/7] MOB-1104: destructure hook params in function definition --- .../hooks/useObsDetailsSharedLogic.ts | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts index 65bca3b9f..1fa40ec37 100644 --- a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts +++ b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts @@ -165,23 +165,19 @@ interface UseObsDetailsSharedLogicReturn { refetchSubscriptions: () => void; } -const useObsDetailsSharedLogic = ( - params: UseObsDetailsSharedLogicParams, -): UseObsDetailsSharedLogicReturn => { - const { - observation, - uuid, - localObservation, - markDeletedLocally, - remoteObservation, - setRemoteObsWasDeleted, - fetchRemoteObservationError, - currentUser, - belongsToCurrentUser, - isRefetching, - refetchRemoteObservation, - } = params; - +const useObsDetailsSharedLogic = ( { + observation, + uuid, + localObservation, + markDeletedLocally, + remoteObservation, + setRemoteObsWasDeleted, + fetchRemoteObservationError, + currentUser, + belongsToCurrentUser, + isRefetching, + refetchRemoteObservation, +}: UseObsDetailsSharedLogicParams ): UseObsDetailsSharedLogicReturn => { const setObservations = useStore( state => state.setObservations ); const navigation = useNavigation>( ); const realm = useRealm( ); From a966693ea487dd85b381900eb2975a42278be983 Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:55:01 -0600 Subject: [PATCH 5/7] clarify BottomSheetV2 --- src/components/SharedComponents/Sheets/BottomSheet.tsx | 1 + src/components/SharedComponents/Sheets/BottomSheetV2.tsx | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/components/SharedComponents/Sheets/BottomSheet.tsx b/src/components/SharedComponents/Sheets/BottomSheet.tsx index 1cf6a5cea..8ab83d829 100644 --- a/src/components/SharedComponents/Sheets/BottomSheet.tsx +++ b/src/components/SharedComponents/Sheets/BottomSheet.tsx @@ -95,6 +95,7 @@ const StandardBottomSheet = ( { handleSnapPress( ); }, [hidden, handleSnapPress] ); + // To me, this implies this is a good candidate for splitting into 2 components const BottomSheetComponent = insideModal ? BottomSheet : BottomSheetModal; diff --git a/src/components/SharedComponents/Sheets/BottomSheetV2.tsx b/src/components/SharedComponents/Sheets/BottomSheetV2.tsx index 3d5099c20..02d4dc3b5 100644 --- a/src/components/SharedComponents/Sheets/BottomSheetV2.tsx +++ b/src/components/SharedComponents/Sheets/BottomSheetV2.tsx @@ -11,6 +11,13 @@ import { Dimensions, StyleSheet } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useTranslation } from "sharedHooks"; +// This component is an iteration on BottomSheet. +// The main motivation was to avoid messing with the inconsistent use of the +// onPressClose prop, whis gets passed to onDismiss and called differently depending +// on whether the X button or backdrop is pressed. + +// To start, it contains just what's necessary for the usage in SuggestIDSheet. + const { width } = Dimensions.get( "window" ); const styles = StyleSheet.create( { From 02780c7dc30c38af260fd07e6d605fc12aad8ab0 Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:22:21 -0600 Subject: [PATCH 6/7] MOB-1104: pr followup --- src/components/ObsDetails/ObsDetailsContainer.js | 3 --- .../ObsDetailsDefaultModeContainer.js | 3 --- .../hooks/useObsDetailsSharedLogic.ts | 15 +++++---------- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/components/ObsDetails/ObsDetailsContainer.js b/src/components/ObsDetails/ObsDetailsContainer.js index 149ee3e24..84c607718 100644 --- a/src/components/ObsDetails/ObsDetailsContainer.js +++ b/src/components/ObsDetails/ObsDetailsContainer.js @@ -101,7 +101,6 @@ const ObsDetailsContainer = ( ): Node => { } = useObsDetailsSharedLogic( { observation, uuid, - targetActivityItemID, localObservation, remoteObservation, markViewedLocally, @@ -112,8 +111,6 @@ const ObsDetailsContainer = ( ): Node => { belongsToCurrentUser, isRefetching, refetchRemoteObservation, - isConnected, - remoteObsWasDeleted, } ); const tabs = [ diff --git a/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js b/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js index 1083dcf30..1001a10d5 100644 --- a/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js +++ b/src/components/ObsDetailsDefaultMode/ObsDetailsDefaultModeContainer.js @@ -79,7 +79,6 @@ const ObsDetailsDefaultModeContainer = ( props: Props ): Node => { } = useObsDetailsSharedLogic( { observation, uuid, - targetActivityItemID, localObservation, remoteObservation, markViewedLocally, @@ -90,8 +89,6 @@ const ObsDetailsDefaultModeContainer = ( props: Props ): Node => { belongsToCurrentUser, isRefetching, refetchRemoteObservation, - isConnected, - remoteObsWasDeleted, } ); return observationShown && ( diff --git a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts index 1fa40ec37..47b4f4ae7 100644 --- a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts +++ b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts @@ -2,7 +2,7 @@ import type { NavigationProp, ParamListBase } from "@react-navigation/native"; import { useFocusEffect, useNavigation } from "@react-navigation/native"; import { useQueryClient } from "@tanstack/react-query"; import { fetchSubscriptions } from "api/observations"; -import type { ApiComment, ApiIdentification } from "api/types"; +import type { ApiComment, ApiIdentification, ApiObservation } from "api/types"; import { RealmContext } from "providers/contexts"; import { useCallback, @@ -31,7 +31,7 @@ const sortItems = ( ids: ActivityItem[], comments: ActivityItem[], ): ActivityItem[] => ids.concat( [...comments] ).sort( - ( a, b ) => ( new Date( a.created_at ).getTime() - new Date( b.created_at ).getTime() ), + ( a, b ) => ( new Date( a.created_at ) - new Date( b.created_at ) ), ); const SHOW_AGREE_SHEET = "SHOW_AGREE_SHEET"; @@ -49,14 +49,14 @@ interface State { activityItems: ActivityItem[]; addingActivityItem: boolean; agreeIdentification: AgreeIdentification | null; - observationShown: RealmObservation | null; + observationShown: ApiObservation | null; showAddCommentSheet: boolean; showAgreeWithIdSheet: boolean; } type Action = - | { type: typeof SET_INITIAL_OBSERVATION; observationShown: RealmObservation } - | { type: typeof ADD_ACTIVITY_ITEM; observationShown: RealmObservation } + | { type: typeof SET_INITIAL_OBSERVATION; observationShown: ApiObservation } + | { type: typeof ADD_ACTIVITY_ITEM; observationShown: ApiObservation } | { type: typeof LOADING_ACTIVITY_ITEM } | { type: typeof SHOW_AGREE_SHEET; agreeIdentification: AgreeIdentification } | { type: typeof HIDE_AGREE_SHEET } @@ -122,7 +122,6 @@ const reducer = ( state: State, action: Action ): State => { interface UseObsDetailsSharedLogicParams { observation: RealmObservation; uuid: string; - targetActivityItemID?: number | null; localObservation: RealmObservation | null; remoteObservation: RealmObservation | null; markViewedLocally: ( ) => void; @@ -133,8 +132,6 @@ interface UseObsDetailsSharedLogicParams { belongsToCurrentUser: boolean; isRefetching: boolean; refetchRemoteObservation: ( ) => void; - isConnected: boolean; - remoteObsWasDeleted: boolean; } interface UseObsDetailsSharedLogicReturn { @@ -147,7 +144,6 @@ interface UseObsDetailsSharedLogicReturn { showAgreeWithIdSheet: boolean; // Computed - hasPhotos: boolean; subscriptionResults: unknown[]; wasSynced: boolean; @@ -377,7 +373,6 @@ const useObsDetailsSharedLogic = ( { showAgreeWithIdSheet, // Computed - hasPhotos, subscriptionResults, wasSynced, From 92b0b0b92d73355c2243213a08695ec04a01c146 Mon Sep 17 00:00:00 2001 From: sepeterson <10458078+sepeterson@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:29:01 -0600 Subject: [PATCH 7/7] MOB-1104: add logger to reducer default state --- .../hooks/useObsDetailsSharedLogic.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts index 47b4f4ae7..5edb139fe 100644 --- a/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts +++ b/src/components/ObsDetailsSharedComponents/hooks/useObsDetailsSharedLogic.ts @@ -11,6 +11,7 @@ import { } from "react"; import Observation from "realmModels/Observation"; import type { RealmObservation, RealmTaxon, RealmUser } from "realmModels/types"; +import { log } from "sharedHelpers/logger"; import safeRealmWrite from "sharedHelpers/safeRealmWrite"; import { useAuthenticatedQuery, @@ -71,6 +72,8 @@ const initialState: State = { showAgreeWithIdSheet: false, }; +const logger = log.extend( "useObsDetailsSharedLogic" ); + const reducer = ( state: State, action: Action ): State => { switch ( action.type ) { case SET_INITIAL_OBSERVATION: @@ -115,6 +118,7 @@ const reducer = ( state: State, action: Action ): State => { showAddCommentSheet: action.showAddCommentSheet, }; default: + logger.error( "Unknown action in useObsDetailsSharedLogic reducer: ", action ); return state; } };