Skip to content

chore: expo audio#1660

Merged
cimigree merged 18 commits into
developfrom
chore/expo-audio
Feb 10, 2026
Merged

chore: expo audio#1660
cimigree merged 18 commits into
developfrom
chore/expo-audio

Conversation

@cimigree

@cimigree cimigree commented Jan 20, 2026

Copy link
Copy Markdown
Contributor

Migration from expo-av to expo-audio

stacked on #1655
stacked on #1651

It doesn't really have to be stacked -- just thought it made sense to deal with this after the expo upgrade.
Closes #1652
Some of the differences, aside from hook and method names:

  • No permission hooks - manual state management required
  • Time units: seconds (requires conversion to milliseconds for existing hooks)
  • No auto-reset - must call seekTo(0) manually for replay
  • Need prepareToRecordAsync() call before record()

Also, to make the React Compiler happy, I had to refactor the try/ catch blocks:

  • Minimized creating/ changing values inside catch statements
  • Removed finally blocks (not supported by React Compiler)
  • Eliminated throw statements in favor of navigation to error screen

@awana-lockfile-bot

awana-lockfile-bot Bot commented Jan 20, 2026

Copy link
Copy Markdown

package-lock.json changes

Click to toggle table visibility
Name Status Previous Current
expo-audio ADDED - 1.1.1
expo-av REMOVED 16.0.8 -

@socket-security

socket-security Bot commented Jan 20, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @browserstack/ai-sdk-node is 100.0% likely obfuscated

Confidence: 1.00

Location: Package overview

From: package-lock.jsonnpm/@wdio/browserstack-service@9.21.0npm/@browserstack/ai-sdk-node@1.5.17

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@browserstack/ai-sdk-node@1.5.17. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @browserstack/ai-sdk-node is 98.0% likely obfuscated

Confidence: 0.98

Location: Package overview

From: package-lock.jsonnpm/@wdio/browserstack-service@9.21.0npm/@browserstack/ai-sdk-node@1.5.17

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@browserstack/ai-sdk-node@1.5.17. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @react-native/debugger-frontend is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: package-lock.jsonnpm/@react-native/debugger-frontend@0.81.5

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@react-native/debugger-frontend@0.81.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@cimigree
cimigree requested a review from ErikSin January 20, 2026 18:28
@cimigree cimigree changed the title Chore/expo audio chore: expo audio Jan 21, 2026

@ErikSin ErikSin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good first pass, but I think the advantage of the new API is that it simplifies state management and we have not taken full advantage of that here.

The main thing is that we do not need to manually set states related to the recording/audio file (aka timing, whether its playing, whether its recording etc). That is now all exposed in the expo hooks. Both of the hooks in the comapeo code base are now just state syncing and creating states that are already available via the expo hooks.

Let me know if you want to cowork on this together!

const [status, setStatus] = useState<Audio.RecordingStatus | null>(null);
const recorder = useAudioRecorder(RECORDING_OPTIONS);
const status = useAudioRecorderState(recorder, 100);
const [isRecording, setIsRecording] = useState(false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we just use status instead of [isRecording, setIsRecording]? It seems like we are manually setting a state here, but that state is already accessible via the status

@cimigree cimigree Jan 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Done, 7672fd7

Comment thread src/frontend/hooks/useAudioPlayback.ts Outdated
Comment on lines +8 to +10
const [isPlaying, setPlaying] = useState(false);
const [duration, setDuration] = useState<number>(0);
const [currentPosition, setCurrentPosition] = useState(0);
const lastPositionRef = useRef(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need [isPlaying, setPlaying], [currentPosition, setCurrentPosition], or lastPositionRef.

The new Api has all of these properties exposed in the status, and I believe those are all stateful (because they are exposed via a hook)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im actually wondering if we even need this hook anymore. Everything is now exposed via the 2 hooks, and if we get rid of all these other useStates, and useRefs, were left with a useEffect at the top level (that is also no longer needed since we don't need to sync any states), and a start and stop function, which essentially just hit play and stop...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the hook. I am not crazy about that. I feel like we now have duplicated code. Granted there wasn't too much in that hook after I simplified the state, but there was something, and now it is in UI files twice.
aba77b6
0d48e32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cimigree If you think it is better to keep it as a hook, Im open to it. I was suggesting it with the minimal context of doing the code review. In otherwords, I trust your judgment on this and my opinion is never the final say.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I reverted back to a hook.

Comment on lines +51 to +54
React.useEffect(() => {
getRecordingPermissionsAsync().then(setAudioPermission);
}, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should do this check before navigating here, and then if there is no permission navigate to the permission screen, otherwise navigate here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually works exactly the same without the permission in there. It already is a Nav prop on the bottom sheet so I didn't think it was necessary as everything worked just fine without adding another nav prop.

@socket-security

socket-security Bot commented Jan 27, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​osm_borders/​maritime_10000m@​1.1.0501003776100
Addednpm/​@​formatjs/​cli@​6.8.2991004196100
Addednpm/​@​types/​lodash.isequal@​4.5.81001005780100
Addednpm/​@​react-native/​typescript-config@​0.76.91001006397100
Addednpm/​@​react-native/​metro-babel-transformer@​0.76.9991006597100
Addednpm/​@​types/​lint-staged@​13.3.01001006680100
Addednpm/​@​types/​react-native-zeroconf@​0.13.1971006978100
Addednpm/​@​comapeo/​nodejs-mobile-react-native@​18.20.4-26910010092100
Addednpm/​@​types/​react-native-indicators@​0.16.6891007178100
Addednpm/​@​react-native-documents/​picker@​11.0.01001007287100
Addednpm/​@​react-native/​metro-config@​0.79.51001007297100
Addednpm/​@​tanstack/​eslint-plugin-query@​5.91.21001007497100
Addednpm/​@​types/​semver@​7.7.11001007581100
Addednpm/​@​react-navigation/​native-stack@​7.3.211001007599100
Addednpm/​@​react-navigation/​native@​7.1.201001007599100
Addednpm/​@​mapeo/​mock-data@​5.0.0751009792100
Addednpm/​@​react-navigation/​bottom-tabs@​7.8.510010076100100
Addednpm/​@​comapeo/​cloud@​0.3.0761008889100
Addednpm/​@​formatjs/​intl-pluralrules@​5.4.61001007695100
Addednpm/​@​react-navigation/​stack@​7.6.410010076100100
Addednpm/​@​types/​jest@​30.0.01001007781100
Addednpm/​@​formatjs/​intl-getcanonicallocales@​2.5.61001007795100
Addednpm/​@​babel/​preset-env@​7.28.5971007797100
Addednpm/​@​types/​mocha@​10.0.101001007780100
Addednpm/​@​formatjs/​intl-locale@​4.2.131001007896100
Addednpm/​@​react-native-vector-icons/​octicons@​20.4.0781008392100
Addednpm/​@​types/​react@​19.2.71001007996100
Updatednpm/​@​babel/​runtime@​7.27.1 ⏵ 7.28.41001007996100
Addednpm/​@​react-native-vector-icons/​fontisto@​12.4.0791008487100
Addednpm/​@​types/​utm@​1.1.4921008180100
Addednpm/​@​formatjs/​intl-relativetimeformat@​11.4.131001008097100
Addednpm/​@​comapeo/​core-react@​7.2.0801008198100
See 34 more rows in the dashboard

View full report

@cimigree
cimigree requested a review from ErikSin January 27, 2026 23:13

@ErikSin ErikSin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job on the second pass, the code feel much cleaner now!

The main blocking comment is that your hooks are not agnostic and rely on the navigation context. I think both the hooks should not be dealing with navigating to the error bottom sheet and should be dealt with by the consuming screen.

I also noticed alot of checks for the existence of things. Your doing early throws and ternaries for objects that are never undefined/null (according to the type definitions), so i dont think they are necessary. I could be wrong, so let me know if there is some context that I am missing.

Lastly, the nature of the hooks have changed. They are both just forwarding other hooks, which is not a problem, and it does make the code simpler since they are being reused. BUT if we are doing that, I think we need to memoize the values that are being exposed OR expose the entire value. It is just misleading to expose only certain parts of the status when any update to the status is going to cause re-renders regardless.

Comment on lines +19 to +22
if (!recorder) {
navigate('ErrorBottomSheet');
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is necessary. If useAudioRecorder is not returning the recorder we have a bigger issue with expo...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stat => setStatus(stat),
);
setRecordingInstance(recording);
await recorder.prepareToRecordAsync();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this second try that re-attempts to prepare the recording I think is also unnecessary. If its failing once, trying again right after shouldn't make a difference and i think it just complicates the code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +39 to +42
if (!recorder || status.isRecording === false) {
navigate('ErrorBottomSheet');
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a worry that the recorder won't be instantiated? I think the check for a recorder and then throwing an error is not necessary, it feels like its just complicating the code.

I also don't think we need to check status.isRecording === false. if we just call recorder.stop(); its already in a try catch. So it should either do nothing, or if does throw an error, the catch will cause the error bottom sheet to open, so no point it checking it twice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the hook should be responsible for navigation to the error bottom sheet. It should be agnostic, and only deal with recorder states. And then the screen, which has access to the navigation should be catching the errors and should be in charge of those navigations. IT also will make the stack trace clearer and we will know what screen is causing the error

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/frontend/hooks/useAudioPlayback.ts Outdated
const startPlayback = useCallback(async () => {
if (!recordedSoundRef.current || isPlaying) return;
if (!player || status.playing) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we need to check for the existence of the player here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/frontend/hooks/useAudioPlayback.ts Outdated

const stopPlayback = useCallback(async () => {
if (!recordedSoundRef.current || !isPlaying) return;
if (!player || !status.playing) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above, i don't think we need check for the existence of the player

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/frontend/hooks/useAudioPlayback.ts Outdated
Comment on lines +39 to +41
duration: status.duration ? status.duration * 1000 : 0,
isPlaying: status.playing,
currentPosition: status.currentTime ? status.currentTime * 1000 : 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ternarary checks I don't think are necessary, duration and currentTime are never undefined or null.

Also, these are not memoized so any component consuming these hooks will be rerendered if the status is updated in any way, even if that status update does not update duration, playing, or current time. Which is not necessarily a problem (its a design flaw of react, not us), but it makes me question whether we need to only expose just these 3 things. It we return the entire status, its really clear that the component is consuming the entire status and we are being explicit that renders are effected by the status as a whole.

I'm open to alternatives if you feel strongly about, but IMO it just feels simpler and easier to expose the entire status object.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/frontend/hooks/useAudioPlayback.ts Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the useCallbacks are now unnecessary. Since the status is at the top level, the consuming components will be re-render called any time the status is updated. So memoizing these functions will not stop an unnecessary re-renders because it is going to happen anyways

Secondly, same comment about the error bottom sheet. I dont think the hook should be responsible for the navigation, the consuming component should be responsible for it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/frontend/hooks/useAudioPlayback.ts Outdated
Comment on lines 23 to 28
player,
status,
duration: status.duration * 1000,
isPlaying: status.playing,
currentPosition: status.currentTime * 1000,
startPlayback,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there was a misunderstanding in my comment. I was trying to say It is misleading that the duration, isPlaying, and currentPosition are there own return items. As a hook, it leads me to believe that if I use any of these 3 things, that they will statefully update the consuming component only when those 3 things change.

So i am suggesting that we should get rid of those 3 completely and ONLY return status. That way there is no ambiguity that consuming component will update with EVERY change in status.

Also if you want to return the player, then i don't think we need to expose startPayback() and stopPlayback(). But if we do that, I don't think we should be using this hook at all...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ErikSin ErikSin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks pretty good now, but i am getting an error thrown when trying to set permissions.

I wanted to check in about the removal of the useAudioPlayback hook. I'm a little confused because when I originally suggested removing it, you mentioned wanting to keep it. Later in review, I proposed that if we were going to expose the recorder, keeping the hook might not make sense—and I was hoping we could discuss the trade-offs together before deciding.

I realize my suggestion may have come across as a recommendation rather than an invitation to talk it through. My intent was to open a conversation so we could align on the architecture direction, especially since we had different initial takes. I'd love to find a smoother way for us to navigate these kinds of decisions when we have differing views—something I know I have talked to you about before.

Do you have suggestion of how we can make these architectural conversations more collaborative going forward?

@@ -40,7 +39,6 @@ export function AudioRecording({
const {startRecording, stopRecording, status} = useAudioRecording();
const timeElapsed = status?.durationMillis || 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status is no longer optional, so can change to const timeElapsed = status.durationMillis

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

try {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is 2 try/catches necessary here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

player.play();
}
} catch {
navigation.navigate('ErrorBottomSheet');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should pass the error to sentry as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +56 to +57
console.error('Failed to start recording:', error);
navigation.replace('ErrorBottomSheet');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can get rid of the console, and pass the error to sentry

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is happening thats causing an error to be thrown. When i grant my permission from this page, its attempting to navigate to the the audio record page and then an error is being thrown. My guess is that the permission is being set but the app hasn't registered that is set yet. The error that is getting thrown is:

ERROR Failed to start recording: [Error: Call to function 'AudioRecorder.record' has been rejected. → Caused by: The 1st argument cannot be cast to type expo.modules.audio.AudioRecorder (received class java.lang.Integer) → Caused by: Cannot use shared object that was already released]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9b0676c & ebcc82c

These changes were the only way I could get this to work. The double try... I think that is why that was there. I also had to take the error handling out of the screen in this situation. If I did two tries, I think I could leave the error handling in the screen, but I would need to have a catch in the hook, which I know you didn't want.
I know it's not what you had in mind. I tried a lot of things. A lot of things.
If you have other ideas, would you mind just taking over the PR?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I am not sure when I said that I didn't want a catch in a hook. Is there a comment that your specifically referring to, or was it something i said in a meeting? Regardless, I think that was a misunderstanding, because I think it is perfectly normal to have a catch in a hook.

Also, the screen should handle navigation to error bottom, but the hook can handle the actual error and just pass it to the screen. Which it was what your doing right now, no? I think I don't fully understand what you mean when you say "I also had to take the error handling out of the screen in this situation".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I am sure I misunderstood. I think what you didn't want was navigation. My mistake.
The issue here is that with the startRecording function in src/frontend/screens/Audio/AudioRecording/useAudioRecording.ts, there is always an error with enabling permissions. The recorder just isn't ready as soon as we arrive from granting permissions. The second try fixed the issue in the old code. In this pass of this code, I changed it to use a specific if/ then statement. But either way (two tries or the if/ then) an error comes from the hook. And when an error comes from the hook, if I have a try/ catch in that first useEffect in the AudioRecording/index.tsx file, then an error will be thrown and we will navigate to the error bottom sheet.
If I don't have a try catch in there, then it all works.
Is it ok to not have a try catch in that useEffect at the top of src/frontend/screens/Audio/AudioRecording/index.tsx?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So i updated expo-audio to the latest and it fixed all these problems. no need for a 2 try/catches. And now we can put the try catch back into the useEffect at the top of src/frontend/screens/Audio/AudioRecording/index.tsx

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ErikSin What is the latest? I tried 1.1.1 and I got the same thing when I removed the extra if/ then.

screen-20260205-234826.mp4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup 1.1.1. Works on both my emulator and physical device

Screen.Recording.2026-02-05.at.3.26.14.PM.mov

in the hook:


const startRecording = useCallback(async () => {
    await recorder.prepareToRecordAsync();
    recorder.record();
  }, [recorder]);

in the screen:

React.useEffect(() => {
    const start = async () => {
      await startRecording();
      setIsLoading(false);
    };
    try {
      start();
    } catch {
      navigation.navigate('ErrorBottomSheet');
    }
  }, [startRecording, navigation]);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok thanks. I had the try catch wrong. This worked and is now pushed up.

@cimigree
cimigree requested a review from ErikSin February 3, 2026 03:10

@ErikSin ErikSin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think everything is addressed now, thanks!

start();
} catch {
navigation.navigate('ErrorBottomSheet');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my example that i gave was just simplified to show that it was working. We should still catch the error and pass it to sentry

We should also do a navigation.replace() here so the user is navigated back to the observation create screen when they close the error bottom sheet.

@cimigree
cimigree requested a review from ErikSin February 10, 2026 16:57
@cimigree
cimigree merged commit b197710 into develop Feb 10, 2026
12 of 13 checks passed
@cimigree
cimigree deleted the chore/expo-audio branch February 10, 2026 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update Expo AV to Audio

2 participants