1use std::sync::Arc;
2
3use hyphae::{Cell, CellImmutable, MapExt};
4use myko_macros::{myko_command, myko_report, myko_report_output};
5
6use crate::{
7 entities::server::{Server, ServerId},
8 prelude::*,
9 report::{ReportContext, ReportHandler},
10};
11
12#[myko_item]
13pub struct Client {
14 #[belongs_to(Server)]
15 pub server_id: ServerId,
16
17 pub windback: Option<Arc<str>>,
20}
21
22#[myko_report_output]
27pub struct ClientStatusOutput {
28 pub online: bool,
29}
30
31#[myko_report(ClientStatusOutput)]
33pub struct ClientStatus {
34 pub client_id: ClientId,
35}
36
37impl ReportHandler for ClientStatus {
38 type Output = ClientStatusOutput;
39
40 fn compute(&self, ctx: ReportContext) -> Cell<Arc<Self::Output>, CellImmutable> {
41 let client_id = self.client_id.clone();
42
43 ctx.query_map(GetAllClients {})
45 .entries()
46 .map(move |clients| {
47 let online = clients
48 .iter()
49 .any(|(_, c)| c.id.as_ref() == client_id.as_ref());
50 Arc::new(ClientStatusOutput { online })
51 })
52 }
53}
54
55#[myko_report_output]
62pub struct WindbackStatusOutput {
63 pub windback: Option<Arc<str>>,
65}
66
67#[myko_report(WindbackStatusOutput)]
68pub struct WindbackStatus {}
69
70impl ReportHandler for WindbackStatus {
71 type Output = WindbackStatusOutput;
72
73 fn compute(&self, ctx: ReportContext) -> Cell<Arc<Self::Output>, CellImmutable> {
74 let client_id = ctx
75 .client_id()
76 .map(|id| ClientId::from(Arc::<str>::from(id)));
77
78 ctx.query_map(GetAllClients {})
80 .entries()
81 .map(move |clients| {
82 let windback = client_id
83 .as_ref()
84 .and_then(|cid| clients.iter().find(|(_, c)| c.id.as_ref() == cid.as_ref()))
85 .and_then(|(_, c)| c.windback.clone());
86 Arc::new(WindbackStatusOutput { windback })
87 })
88 }
89}
90
91#[myko_command(bool)]
94pub struct SetClientWindbackTime {
95 pub windback: Arc<str>,
97}
98
99impl crate::command::CommandHandler for SetClientWindbackTime {
100 fn execute(
101 self,
102 ctx: crate::command::CommandContext,
103 ) -> Result<bool, crate::command::CommandError> {
104 let client_id = ctx
105 .client_id()
106 .ok_or_else(|| crate::command::CommandError {
107 tx: ctx.tx().to_string(),
108 command_id: "SetClientWindbackTime".to_string(),
109 message: "No client_id in context - windback requires a WebSocket connection"
110 .to_string(),
111 })?;
112
113 let client = ctx
115 .exec_report(GetClientById {
116 id: ClientId::from(Arc::<str>::from(client_id.clone())),
117 })?
118 .ok_or_else(|| CommandError {
119 tx: ctx.tx().to_string(),
120 command_id: "SetClientWindbackTime".to_string(),
121 message: format!("Client {} not found", client_id),
122 })?;
123
124 let updated_client = Client {
126 id: client.id.clone(),
127 server_id: client.server_id.clone(),
128 windback: Some(self.windback.clone()),
129 };
130
131 ctx.emit_set(&updated_client)?;
132
133 Ok(true)
134 }
135}
136
137#[myko_command(bool)]
140pub struct ClearClientWindbackTime {}
141
142impl crate::command::CommandHandler for ClearClientWindbackTime {
143 fn execute(
144 self,
145 ctx: crate::command::CommandContext,
146 ) -> Result<bool, crate::command::CommandError> {
147 let client_id = ctx
148 .client_id()
149 .ok_or_else(|| crate::command::CommandError {
150 tx: ctx.tx().to_string(),
151 command_id: "ClearClientWindbackTime".to_string(),
152 message: "No client_id in context - windback requires a WebSocket connection"
153 .to_string(),
154 })?;
155
156 let client = ctx
158 .exec_report(GetClientById {
159 id: ClientId::from(Arc::<str>::from(client_id.clone())),
160 })?
161 .ok_or_else(|| CommandError {
162 tx: ctx.tx().to_string(),
163 command_id: "SetClientWindbackTime".to_string(),
164 message: format!("Client {} not found", client_id),
165 })?;
166 let updated_client = Client {
168 id: client.id.clone(),
169 server_id: client.server_id.clone(),
170 windback: None,
171 };
172
173 ctx.emit_set(&updated_client)?;
174
175 Ok(true)
176 }
177}