yewdux_utils/
lib.rs

1use std::{marker::PhantomData, rc::Rc};
2use yewdux::{prelude::*, Context};
3
4#[derive(Default)]
5pub struct HistoryListener<T: Store + PartialEq>(PhantomData<T>);
6
7struct HistoryChangeMessage<T: Store + PartialEq>(Rc<T>);
8
9impl<T: Store + PartialEq> Reducer<HistoryStore<T>> for HistoryChangeMessage<T> {
10    fn apply(self, mut state: Rc<HistoryStore<T>>) -> Rc<HistoryStore<T>> {
11        if state.matches_current(&self.0) {
12            return state;
13        }
14
15        let mut_state = Rc::make_mut(&mut state);
16        mut_state.index += 1;
17        mut_state.vector.truncate(mut_state.index);
18        mut_state.vector.push(self.0);
19
20        state
21    }
22}
23
24impl<T: Store + PartialEq> Listener for HistoryListener<T> {
25    type Store = T;
26
27    fn on_change(&self, cx: &Context, state: Rc<Self::Store>) {
28        Dispatch::<HistoryStore<T>>::new(cx).apply(HistoryChangeMessage::<T>(state))
29    }
30}
31
32#[derive(Debug, PartialEq)]
33pub struct HistoryStore<T: Store + PartialEq> {
34    vector: Vec<Rc<T>>,
35    index: usize,
36    dispatch: Dispatch<T>,
37}
38
39impl<T: Store + PartialEq> Clone for HistoryStore<T> {
40    fn clone(&self) -> Self {
41        Self {
42            vector: self.vector.clone(),
43            index: self.index,
44            dispatch: self.dispatch.clone(),
45        }
46    }
47}
48
49impl<T: Store + PartialEq> HistoryStore<T> {
50    pub fn can_apply(&self, message: &HistoryMessage) -> bool {
51        match message {
52            HistoryMessage::Undo => self.index > 0,
53            HistoryMessage::Redo => self.index + 1 < self.vector.len(),
54            HistoryMessage::Clear => self.vector.len() > 1,
55            HistoryMessage::JumpTo(index) => index != &self.index && index < &self.vector.len(),
56        }
57    }
58
59    fn matches_current(&self, state: &Rc<T>) -> bool {
60        let c = self.current();
61        Rc::ptr_eq(c, state)
62    }
63
64    fn current(&self) -> &Rc<T> {
65        &self.vector[self.index]
66    }
67
68    pub fn index(&self) -> usize {
69        self.index
70    }
71
72    pub fn states(&self) -> &[Rc<T>] {
73        self.vector.as_slice()
74    }
75}
76
77impl<T: Store + PartialEq> Store for HistoryStore<T> {
78    fn new(cx: &Context) -> Self {
79        let dispatch = Dispatch::<T>::new(cx);
80        let s1 = dispatch.get();
81        Self {
82            vector: vec![s1],
83            index: 0,
84            dispatch,
85        }
86    }
87
88    fn should_notify(&self, other: &Self) -> bool {
89        self != other
90    }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum HistoryMessage {
95    Undo,
96    Redo,
97    Clear,
98    JumpTo(usize),
99}
100
101impl<T: Store + PartialEq + Clone> Reducer<HistoryStore<T>> for HistoryMessage {
102    fn apply(self, mut state: Rc<HistoryStore<T>>) -> Rc<HistoryStore<T>> {
103        let mut_state = Rc::make_mut(&mut state);
104
105        let state_changed = match self {
106            HistoryMessage::Undo => {
107                if let Some(new_index) = mut_state.index.checked_sub(1) {
108                    mut_state.index = new_index;
109                    true
110                } else {
111                    false
112                }
113            }
114            HistoryMessage::Redo => {
115                let new_index = mut_state.index + 1;
116                if new_index < mut_state.vector.len() {
117                    mut_state.index = new_index;
118                    true
119                } else {
120                    false
121                }
122            }
123            HistoryMessage::Clear => {
124                let current = mut_state.vector[mut_state.index].clone();
125                mut_state.vector.clear();
126                mut_state.vector.push(current);
127                mut_state.index = 0;
128                false
129            }
130            HistoryMessage::JumpTo(index) => {
131                if index < mut_state.vector.len() {
132                    mut_state.index = index;
133
134                    true
135                } else {
136                    false
137                }
138            }
139        };
140
141        if state_changed {
142            mut_state.dispatch.reduce(|_| mut_state.current().clone());
143        }
144
145        state
146    }
147}