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)]
18pub enum ShareError {
19 #[error("sharing is not supported on this platform")]
21 Unsupported,
22 #[error("failed to present the share sheet: {0}")]
24 Failed(String),
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct ShareContent {
30 pub file_name: String,
32 pub mime_type: String,
34 pub bytes: Vec<u8>,
36 pub text: Option<String>,
38}
39
40impl ShareContent {
41 pub fn file(
43 file_name: impl Into<String>,
44 mime_type: impl Into<String>,
45 bytes: Vec<u8>,
46 ) -> Self {
47 Self {
48 file_name: file_name.into(),
49 mime_type: mime_type.into(),
50 bytes,
51 text: None,
52 }
53 }
54
55 pub fn with_text(mut self, text: impl Into<String>) -> Self {
57 self.text = Some(text.into());
58 self
59 }
60}
61
62pub trait ShareSheet {
65 fn share(&self, content: ShareContent) -> Result<(), ShareError>;
68
69 fn is_supported(&self) -> bool;
72}
73
74pub type ShareSheetRef = Rc<dyn ShareSheet>;
76
77pub fn set_platform_share_sheet(share_sheet: ShareSheetRef) {
80 crate::thread_state::with(|state| *state.share_sheet.borrow_mut() = Some(share_sheet));
81}
82
83pub fn clear_platform_share_sheet() {
85 crate::thread_state::with(|state| *state.share_sheet.borrow_mut() = None);
86}
87
88fn registered_platform_share_sheet() -> Option<ShareSheetRef> {
89 crate::thread_state::with(|state| state.share_sheet.borrow().clone())
90}
91
92struct PlatformShareSheet;
93
94impl ShareSheet for PlatformShareSheet {
95 fn share(&self, content: ShareContent) -> Result<(), ShareError> {
96 match registered_platform_share_sheet() {
97 Some(handler) => handler.share(content),
98 None => Err(ShareError::Unsupported),
99 }
100 }
101
102 fn is_supported(&self) -> bool {
103 registered_platform_share_sheet().is_some_and(|handler| handler.is_supported())
104 }
105}
106
107pub fn default_share_sheet() -> ShareSheetRef {
110 Rc::new(PlatformShareSheet)
111}
112
113pub fn local_share_sheet() -> CompositionLocal<ShareSheetRef> {
116 crate::composition_locals::cached_local(
117 |locals| &locals.share_sheet,
118 || compositionLocalOfWithPolicy(default_share_sheet, Rc::ptr_eq),
119 )
120}
121
122#[composable]
124pub fn ProvideShareSheet(content: impl FnOnce()) {
125 let share_sheet = cranpose_core::remember(default_share_sheet).with(|state| state.clone());
126 let local = local_share_sheet();
127
128 CompositionLocalProvider([local.provides(share_sheet)], move || {
129 content();
130 });
131}
132
133#[cfg(test)]
134#[path = "tests/share_sheet_tests.rs"]
135mod tests;