cranpose_services/
share_sheet.rs1use std::rc::Rc;
12
13use cranpose_core::{CompositionLocal, CompositionLocalProvider, compositionLocalOfWithPolicy};
14use cranpose_macros::composable;
15
16#[derive(thiserror::Error, Debug)]
17pub enum ShareError {
18 #[error("sharing is not supported on this platform")]
19 Unsupported,
20 #[error("failed to present the share sheet: {0}")]
21 Failed(String),
22}
23
24#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct ShareContent {
27 pub file_name: String,
29 pub mime_type: String,
31 pub bytes: Vec<u8>,
33 pub text: Option<String>,
35}
36
37impl ShareContent {
38 pub fn file(
40 file_name: impl Into<String>,
41 mime_type: impl Into<String>,
42 bytes: Vec<u8>,
43 ) -> Self {
44 Self {
45 file_name: file_name.into(),
46 mime_type: mime_type.into(),
47 bytes,
48 text: None,
49 }
50 }
51
52 pub fn with_text(mut self, text: impl Into<String>) -> Self {
54 self.text = Some(text.into());
55 self
56 }
57}
58
59pub trait ShareSheet {
62 fn share(&self, content: ShareContent) -> Result<(), ShareError>;
65
66 fn is_supported(&self) -> bool;
69}
70
71pub type ShareSheetRef = Rc<dyn ShareSheet>;
72
73pub fn set_platform_share_sheet(share_sheet: ShareSheetRef) {
76 crate::thread_state::with(|state| *state.share_sheet.borrow_mut() = Some(share_sheet));
77}
78
79pub fn clear_platform_share_sheet() {
81 crate::thread_state::with(|state| *state.share_sheet.borrow_mut() = None);
82}
83
84fn registered_platform_share_sheet() -> Option<ShareSheetRef> {
85 crate::thread_state::with(|state| state.share_sheet.borrow().clone())
86}
87
88struct PlatformShareSheet;
89
90impl ShareSheet for PlatformShareSheet {
91 fn share(&self, content: ShareContent) -> Result<(), ShareError> {
92 match registered_platform_share_sheet() {
93 Some(handler) => handler.share(content),
94 None => Err(ShareError::Unsupported),
95 }
96 }
97
98 fn is_supported(&self) -> bool {
99 registered_platform_share_sheet().is_some_and(|handler| handler.is_supported())
100 }
101}
102
103pub fn default_share_sheet() -> ShareSheetRef {
104 Rc::new(PlatformShareSheet)
105}
106
107pub fn local_share_sheet() -> CompositionLocal<ShareSheetRef> {
108 crate::composition_locals::cached_local(
109 |locals| &locals.share_sheet,
110 || compositionLocalOfWithPolicy(default_share_sheet, Rc::ptr_eq),
111 )
112}
113
114#[composable]
115pub fn ProvideShareSheet(content: impl FnOnce()) {
116 let share_sheet = cranpose_core::remember(default_share_sheet).with(|state| state.clone());
117 let local = local_share_sheet();
118
119 CompositionLocalProvider([local.provides(share_sheet)], move || {
120 content();
121 });
122}
123
124#[cfg(test)]
125#[path = "tests/share_sheet_tests.rs"]
126mod tests;