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}