Skip to main content

ManuallyRequestSheet

The ManuallyRequestSheet module provides compound React components for creating customizable bottom sheet modals for manual health data authorization requests. Built on @gorhom/bottom-sheet with themed styling and platform-specific imagery.

Overview

The ManuallyRequestSheet module is built using a compound component pattern, providing maximum flexibility for different authorization flows while maintaining visual consistency across your healthcare application.

Key Features

  • Bottom Sheet Modal: Built on @gorhom/bottom-sheet for native-like experience
  • Compound Components: Flexible sub-components for custom layouts
  • Platform Support: Built-in imagery for Apple, Google, and Fitbit
  • Theme Integration: Automatically adapts to app theme colors and spacing
  • Safe Area Handling: Proper safe area insets for all device types
  • Dynamic Sizing: Automatically adjusts height based on content
  • Accessibility: Full accessibility support with proper labeling
  • TypeScript: Full type safety throughout all components

Basic Example

import { ObservationCode } from "@ovok/core";
import { AppleHealthSyncProps, DataSync } from "@ovok/native";
import { BottomSheetModal } from "@gorhom/bottom-sheet";
import { HKQuantityTypeIdentifier } from "@kingstinct/react-native-healthkit";
import React from "react";

const dataToSync: AppleHealthSyncProps["dataToSync"] = {
"body-temperature": {
typeIdentifiers: [
{
typeIdentifier: HKQuantityTypeIdentifier.bodyTemperature,
code: ObservationCode.BODY_TEMPERATURE,
},
],
minDate: new Date("2025-01-01"),
},
};

const HealthDataSync = () => {
const [skipRequest, setSkipRequest] = React.useState(false);
const manuallyRequestSheetRef = React.useRef<BottomSheetModal | null>(null);
const shouldOpenManuallyRequestSheet = React.useRef(true);

const onRefManuallyRequestSheet = React.useCallback(
(ref: BottomSheetModal | null) => {
// This is a workaround to ensure the sheet is expanded when the ref is set (after the component is mounted)
// if you need to open sheet on button press, you can use the `shouldOpenManuallyRequestSheet` ref and remove the `if` check

manuallyRequestSheetRef.current = ref;

if (shouldOpenManuallyRequestSheet.current) {
ref?.present();
shouldOpenManuallyRequestSheet.current = false;
}
},
[],
);

const handleSkip = React.useCallback(() => {
// in this case, we want to show skipped state in screen
// but you can also navigate user to another screen
setSkipRequest(true);
manuallyRequestSheetRef.current?.dismiss();
}, []);

const renderManualRequestUI = React.useCallback(
(requestAccess: () => void) => {
return (
<DataSync.ManuallyRequestSheet ref={onRefManuallyRequestSheet}>
<DataSync.ManuallyRequestSheet.Container>
<DataSync.ManuallyRequestSheet.Title>
Manually Request
</DataSync.ManuallyRequestSheet.Title>
<DataSync.ManuallyRequestSheet.Description>
Sync your weight, steps, and other data to track your progress
effortlessly.
</DataSync.ManuallyRequestSheet.Description>
<DataSync.ManuallyRequestSheet.Image for="apple" />
<DataSync.ManuallyRequestSheet.RequestButton
onPress={requestAccess}
>
Request
</DataSync.ManuallyRequestSheet.RequestButton>
<DataSync.ManuallyRequestSheet.SkipButton onPress={handleSkip}>
Skip
</DataSync.ManuallyRequestSheet.SkipButton>
</DataSync.ManuallyRequestSheet.Container>
</DataSync.ManuallyRequestSheet>
);
},
[onRefManuallyRequestSheet, handleSkip],
);

return (
// This component is wrapped in the AppleHealthAuthorizationProvider
// to handle the authorization process
<DataSync.AppleHealthAuthorizationProvider
readIdentifiers={[HKQuantityTypeIdentifier.bodyMass]}
renderManualRequestUI={renderManualRequestUI}
skipRequest={skipRequest}
>
{/* You have to be authorized to sync the health data */}
<DataSync.AppleHealthSync dataToSync={dataToSync} />
</DataSync.AppleHealthAuthorizationProvider>
);
};

export default HealthDataSync;

Props

PropTypeRequiredDefaultDescription
childrenReact.ReactNode-Content to render in the bottom sheet
backgroundStyleViewStyle--Custom background styling (merged with theme)
enableDynamicSizingboolean-trueAutomatically adjust height based on content
enablePanDownToCloseboolean-falseAllow closing by panning down

Inherits all BottomSheetModalProps from @gorhom/bottom-sheet

Component Behavior

The ManuallyRequestSheet component provides a modal container that:

  1. Modal Presentation: Overlays content above the current screen
  2. Dynamic Height: Automatically adjusts to fit content when enableDynamicSizing is true
  3. Theme Integration: Uses app theme colors for background and styling
  4. Safe Area Handling: Respects device safe areas for proper layout
  5. Gesture Control: Optional pan-down-to-close gesture support

Background Styling

The component automatically applies theme-based background styling:

// Default background style (merged with custom backgroundStyle)
{
backgroundColor: theme.colors.background,
borderTopLeftRadius: theme.borderRadius(5),
borderTopRightRadius: theme.borderRadius(5),
}

Ref API

The component forwards ref to the underlying BottomSheetModal, providing access to:

Methods

  • present() - Opens the bottom sheet
  • dismiss() - Closes the bottom sheet
  • expand() - Expands to full height
  • collapse() - Collapses to snap point
  • close() - Alias for dismiss()

Styling

Theme Colors Used

  • theme.colors.background - Modal background color
  • theme.borderRadius(5) - Top corner radius for modal appearance

Components

Common Patterns

Authorization Flow States

The ManuallyRequestSheet component supports several common authorization flow states:

  1. Introduction: Shows platform information and benefits
  2. Permission Selection: Optional step for granular permission control
  3. Authorization Request: Primary action to grant permissions
  4. Error Handling: Graceful handling of permission denials
  5. Success/Completion: Post-authorization feedback

Platform Support

The component supports various health platforms:

  • Apple Health (for="apple")
  • Google Health Connect (for="google")
  • Custom providers (with custom imagery)