Skip to main content

tilo_app/framework/views/
kept.rs

1//! Query views a person kept: their own views beside the app's designed
2//! ones, such as a smart playlist or a saved filter.
3//!
4//! A kept view is the person's, not the workspace's: it follows them to their
5//! other devices and no collaborator sees it. It is a personal setting of the
6//! app ([`KeptViews`]), read with `settings::<KeptViews>()` from a view or
7//! surface context, so it survives a restart.
8
9use serde::{Deserialize, Serialize};
10use ui_core::storage::{Scope, SettingsSchema};
11
12use super::kind::{KindDecl, filters};
13use super::query::QuerySpec;
14
15/// One kept query view.
16#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
17pub struct KeptView {
18    pub id: String,
19    /// What the person calls it; at first what was searched for.
20    pub name: String,
21    pub query: QuerySpec,
22}
23
24/// The query views a person kept in one app, oldest first.
25#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
26pub struct KeptViews {
27    #[serde(default)]
28    pub kept_views: Vec<KeptView>,
29}
30
31impl SettingsSchema for KeptViews {
32    const SCOPE: Scope = Scope::Personal;
33}
34
35impl KeptViews {
36    /// Keep `query` as a view named `name`, and return its id. A query kept
37    /// already is not kept twice: its id comes back.
38    pub fn keep(&mut self, query: &QuerySpec, name: impl Into<String>) -> String {
39        let key = query.key();
40        if let Some(kept) = self.kept_views.iter().find(|kept| kept.query.key() == key) {
41            return kept.id.clone();
42        }
43        let mut stamp = ui_core::time::unix_now_ms();
44        let mut id = format!("kept-{stamp}");
45        while self.get(&id).is_some() {
46            stamp = stamp.wrapping_add(1);
47            id = format!("kept-{stamp}");
48        }
49        self.kept_views.push(KeptView {
50            id: id.clone(),
51            name: name.into(),
52            query: query.clone(),
53        });
54        id
55    }
56
57    #[must_use]
58    pub fn get(&self, id: &str) -> Option<&KeptView> {
59        self.kept_views.iter().find(|kept| kept.id == id)
60    }
61
62    /// Stop keeping the view `id`. Returns whether it was kept.
63    pub fn forget(&mut self, id: &str) -> bool {
64        let before = self.kept_views.len();
65        self.kept_views.retain(|kept| kept.id != id);
66        self.kept_views.len() != before
67    }
68
69    /// Call the view `id` `name`; a blank name changes nothing.
70    pub fn rename(&mut self, id: &str, name: &str) -> bool {
71        let name = name.trim();
72        match self.kept_views.iter_mut().find(|kept| kept.id == id) {
73            Some(kept) if !name.is_empty() && kept.name != name => {
74                name.clone_into(&mut kept.name);
75                true
76            }
77            _ => false,
78        }
79    }
80
81    /// The name a view of `query` starts with: what was typed, then its
82    /// chips as they read ("ada", "Liked").
83    #[must_use]
84    pub fn default_name(query: &QuerySpec, kinds: &[KindDecl]) -> String {
85        let fields = filters(kinds);
86        let mut parts = Vec::new();
87        let text = query.text.trim();
88        if !text.is_empty() {
89            parts.push(text.to_owned());
90        }
91        for chip in &query.chips {
92            let field = fields.iter().copied().find(|field| field.key == chip.key);
93            parts.push(chip.label(field));
94        }
95        parts.join(", ")
96    }
97}