1use crate::plugins::{Capability, Command, Plugin, PluginRegistry};
2use crate::{
3 CoreEvent, MindMapData, MindMapError, MindMapState, Operation, OperationOptions,
4 OperationResult,
5};
6use dioxus::prelude::*;
7use std::{collections::BTreeMap, rc::Rc};
8
9type Before = Rc<dyn Fn(&Operation) -> bool>;
10type Listener = Rc<dyn Fn(CoreEvent)>;
11
12#[derive(Clone, Copy, PartialEq)]
15pub struct MindMapHandle {
16 state: Signal<MindMapState>,
17 before: Signal<Option<Before>>,
18 listener: Signal<Option<Listener>>,
19 editor_owner: Signal<Option<usize>>,
20 subscribers: Signal<BTreeMap<u64, Listener>>,
21 next_subscription: Signal<u64>,
22 plugins: Signal<PluginRegistry>,
23}
24
25pub fn use_mind_map(data: MindMapData) -> Result<MindMapHandle, MindMapError> {
28 use_hook(|| {
29 MindMapState::new(data).map(|state| MindMapHandle {
30 state: Signal::new(state),
31 before: Signal::new(None),
32 listener: Signal::new(None),
33 editor_owner: Signal::new(None),
34 subscribers: Signal::new(BTreeMap::new()),
35 next_subscription: Signal::new(0),
36 plugins: Signal::new(PluginRegistry::default()),
37 })
38 })
39}
40impl MindMapHandle {
41 pub fn document(self) -> Result<MindMapData, MindMapError> {
43 self.state
44 .try_read()
45 .map(|state| state.data().clone())
46 .map_err(|_| MindMapError::StaleOperation)
47 }
48 pub fn selection(self) -> Result<Vec<String>, MindMapError> {
50 self.state
51 .try_read()
52 .map(|state| state.selection().to_vec())
53 .map_err(|_| MindMapError::StaleOperation)
54 }
55 pub fn revision(self) -> Result<u64, MindMapError> {
57 self.state
58 .try_read()
59 .map(|state| state.revision())
60 .map_err(|_| MindMapError::StaleOperation)
61 }
62 pub fn is_read_only(self) -> Result<bool, MindMapError> {
64 self.state
65 .try_read()
66 .map(|state| state.is_read_only())
67 .map_err(|_| MindMapError::StaleOperation)
68 }
69 pub fn set_before(
72 mut self,
73 hook: impl Fn(&Operation) -> bool + 'static,
74 ) -> Result<(), MindMapError> {
75 *self
76 .before
77 .try_write()
78 .map_err(|_| MindMapError::StaleOperation)? = Some(Rc::new(hook));
79 Ok(())
80 }
81 pub fn clear_before(mut self) -> Result<(), MindMapError> {
83 *self
84 .before
85 .try_write()
86 .map_err(|_| MindMapError::StaleOperation)? = None;
87 Ok(())
88 }
89 pub fn set_listener(
91 mut self,
92 listener: impl Fn(CoreEvent) + 'static,
93 ) -> Result<(), MindMapError> {
94 *self
95 .listener
96 .try_write()
97 .map_err(|_| MindMapError::StaleOperation)? = Some(Rc::new(listener));
98 Ok(())
99 }
100 pub fn apply(self, operation: Operation) -> Result<OperationResult, MindMapError> {
102 self.apply_with(operation, OperationOptions::default())
103 }
104 pub fn apply_with(
106 mut self,
107 operation: Operation,
108 options: OperationOptions,
109 ) -> Result<OperationResult, MindMapError> {
110 let revision = self.revision()?;
111 if self.is_read_only()? {
112 return Err(MindMapError::ReadOnly);
113 }
114 let before = self
115 .before
116 .try_read()
117 .map_err(|_| MindMapError::StaleOperation)?
118 .clone();
119 if let Some(before) = before {
120 if !before(&operation) {
121 return Err(MindMapError::Rejected);
122 }
123 }
124 let result = {
125 let mut state = self
126 .state
127 .try_write()
128 .map_err(|_| MindMapError::StaleOperation)?;
129 if state.revision() != revision {
130 return Err(MindMapError::StaleOperation);
131 }
132 state.apply_with(operation, options)?
133 };
134 self.emit(&result.events)?;
135 Ok(result)
136 }
137 pub fn select(mut self, ids: &[String]) -> Result<(), MindMapError> {
139 let events = self
140 .state
141 .try_write()
142 .map_err(|_| MindMapError::StaleOperation)?
143 .select(ids)?;
144 self.emit(&events)
145 }
146 pub fn set_read_only(mut self, value: bool) -> Result<(), MindMapError> {
148 self.state
149 .try_write()
150 .map_err(|_| MindMapError::StaleOperation)?
151 .set_read_only(value);
152 Ok(())
153 }
154 pub fn undo(mut self) -> Result<(), MindMapError> {
156 let events = self
157 .state
158 .try_write()
159 .map_err(|_| MindMapError::StaleOperation)?
160 .undo()?;
161 self.emit(&events)
162 }
163 pub fn redo(mut self) -> Result<(), MindMapError> {
165 let events = self
166 .state
167 .try_write()
168 .map_err(|_| MindMapError::StaleOperation)?
169 .redo()?;
170 self.emit(&events)
171 }
172 pub fn refresh(mut self, data: MindMapData) -> Result<(), MindMapError> {
174 let events = self
175 .state
176 .try_write()
177 .map_err(|_| MindMapError::StaleOperation)?
178 .refresh(data)?;
179 self.emit(&events)
180 }
181 pub(crate) fn claim_editor(mut self, owner: usize) -> Result<(), MindMapError> {
182 *self
183 .editor_owner
184 .try_write()
185 .map_err(|_| MindMapError::StaleOperation)? = Some(owner);
186 Ok(())
187 }
188 pub(crate) fn owns_editor(self, owner: usize) -> Result<bool, MindMapError> {
189 Ok(*self
190 .editor_owner
191 .try_read()
192 .map_err(|_| MindMapError::StaleOperation)?
193 == Some(owner))
194 }
195 pub fn subscribe(
197 mut self,
198 listener: impl Fn(CoreEvent) + 'static,
199 ) -> Result<Subscription, MindMapError> {
200 let id = *self
201 .next_subscription
202 .try_read()
203 .map_err(|_| MindMapError::StaleOperation)?;
204 let next = id
205 .checked_add(1)
206 .ok_or_else(|| MindMapError::InvalidOperation("Subscription ID exhausted".into()))?;
207 *self
208 .next_subscription
209 .try_write()
210 .map_err(|_| MindMapError::StaleOperation)? = next;
211 self.subscribers
212 .try_write()
213 .map_err(|_| MindMapError::StaleOperation)?
214 .insert(id, Rc::new(listener));
215 Ok(Subscription { handle: self, id })
216 }
217 pub fn register_plugin(mut self, plugin: Plugin) -> Result<PluginGuard, MindMapError> {
219 let id = plugin.id.clone();
220 self.plugins
221 .try_write()
222 .map_err(|_| MindMapError::StaleOperation)?
223 .register(plugin)?;
224 Ok(PluginGuard {
225 handle: self,
226 id,
227 subscriptions: Vec::new(),
228 })
229 }
230 pub fn set_plugin_enabled(mut self, id: &str, enabled: bool) -> Result<(), MindMapError> {
232 self.plugins
233 .try_write()
234 .map_err(|_| MindMapError::StaleOperation)?
235 .set_enabled(id, enabled)
236 }
237 pub fn has_plugin(self, capability: Capability) -> bool {
239 self.plugins
240 .try_read()
241 .is_ok_and(|plugins| plugins.provides(capability))
242 }
243 pub fn plugin_commands(self) -> Result<Vec<Command>, MindMapError> {
245 Ok(self
246 .plugins
247 .try_read()
248 .map_err(|_| MindMapError::StaleOperation)?
249 .commands())
250 }
251 fn emit(self, events: &[CoreEvent]) -> Result<(), MindMapError> {
252 let listener = self
253 .listener
254 .try_read()
255 .map_err(|_| MindMapError::StaleOperation)?
256 .clone();
257 let subscribers = self
258 .subscribers
259 .try_read()
260 .map_err(|_| MindMapError::StaleOperation)?
261 .iter()
262 .map(|(id, listener)| (*id, listener.clone()))
263 .collect::<Vec<_>>();
264 for event in events {
265 if let Some(listener) = &listener {
266 listener(event.clone());
267 }
268 for (id, callback) in &subscribers {
269 if self
270 .subscribers
271 .try_read()
272 .is_ok_and(|current| current.contains_key(id))
273 {
274 callback(event.clone());
275 }
276 }
277 }
278 Ok(())
279 }
280}
281
282pub struct Subscription {
284 handle: MindMapHandle,
285 id: u64,
286}
287impl Drop for Subscription {
288 fn drop(&mut self) {
289 if let Ok(mut entries) = self.handle.subscribers.try_write() {
290 entries.remove(&self.id);
291 }
292 }
293}
294pub struct PluginGuard {
296 handle: MindMapHandle,
297 id: String,
298 subscriptions: Vec<Subscription>,
299}
300impl PluginGuard {
301 pub fn subscribe(
303 &mut self,
304 listener: impl Fn(CoreEvent) + 'static,
305 ) -> Result<(), MindMapError> {
306 let handle = self.handle;
307 let id = self.id.clone();
308 self.subscriptions.push(handle.subscribe(move |event| {
309 let enabled = handle
310 .plugins
311 .try_read()
312 .is_ok_and(|registry| registry.is_enabled(&id));
313 if enabled {
314 listener(event);
315 }
316 })?);
317 Ok(())
318 }
319 pub fn set_enabled(&self, enabled: bool) -> Result<(), MindMapError> {
321 self.handle.set_plugin_enabled(&self.id, enabled)
322 }
323}
324impl Drop for PluginGuard {
325 fn drop(&mut self) {
326 if let Ok(mut registry) = self.handle.plugins.try_write() {
327 let _ = registry.unregister(&self.id);
328 }
329 }
330}
331pub fn use_plugins(
333 handle: MindMapHandle,
334 plugins: Vec<Plugin>,
335) -> Result<Rc<Vec<PluginGuard>>, MindMapError> {
336 use_hook(|| {
337 plugins
338 .into_iter()
339 .map(|plugin| handle.register_plugin(plugin))
340 .collect::<Result<Vec<_>, _>>()
341 .map(Rc::new)
342 })
343}