aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/sort.rs
blob: 3ab6ceff555463086e3724ce354430667a8328a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use jellycommon::{NodeKind, NodePublic};
use rocket::{
    http::uri::fmt::{Query, UriDisplay},
    FromForm, FromFormField, UriDisplayQuery,
};

#[derive(FromForm, UriDisplayQuery)]
pub struct NodeFilterSort {
    sort_by: Option<SortProperty>,
    filter_kind: Option<Vec<NodeKind>>,
    sort_order: Option<SortOrder>,
}

#[rustfmt::skip]
#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)]
enum SortProperty {
    #[field(value = "release_date")] ReleaseDate,
    #[field(value = "title")] Title,
}

#[rustfmt::skip]
#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)]
enum SortOrder {
    #[field(value = "ascending")] Ascending,
    #[field(value = "descending")] Descending,
}

pub fn filter_and_sort_nodes(f: &NodeFilterSort, nodes: &mut Vec<(String, NodePublic)>) {
    nodes.retain(|(_id, node)| {
        let mut o = true;
        if let Some(kind) = &f.filter_kind {
            o &= kind.contains(&node.kind)
        }
        o
    });
    if let Some(sort_prop) = f.sort_by {
        match sort_prop {
            SortProperty::ReleaseDate => nodes.sort_by_key(|(_, _n)| 0), // TODO
            SortProperty::Title => nodes.sort_by(|(_, a), (_, b)| a.title.cmp(&b.title)),
        }
    }
    match f.sort_order.unwrap_or(SortOrder::Ascending) {
        SortOrder::Ascending => (),
        SortOrder::Descending => nodes.reverse(),
    }
}

markup::define! {
    NodeFilterSortForm<'a>(f: &'a NodeFilterSort) {
        details {
            summary { "Filter and Sort" }
            form[method="GET", action=""] {
                fieldset {
                    legend { "Filter" }
                    @use NodeKind::*;
                    @for (value, label) in [(Movie, "Movie"), (Episode, "Episode"), (Video, "Video"), (Channel, "Channel")] {
                        label { input[type="checkbox", name="filter_kind", value=A(value), checked=f.filter_kind.as_ref().map(|k|k.contains(&value)).unwrap_or(true)]; @label } br;
                    }
                }
                fieldset {
                    legend { "Sort By" }
                    @use SortProperty::*;
                    @for (value, label) in [(Title, "Title"), (ReleaseDate, "Release Date")] {
                        label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by]; @label } br;
                    }
                }
                fieldset {
                    legend { "Sort Order" }
                    @use SortOrder::*;
                    @for (value, label) in [(Ascending, "Ascending"), (Descending, "Descending")] {
                        label { input[type="radio", name="sort_order", value=value, checked=Some(value)==f.sort_order]; @label } br;
                    }
                }
                input[type="submit", value="Apply"];
            }
        }
    }
}

impl markup::Render for SortProperty {
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>))
    }
}
impl markup::Render for SortOrder {
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>))
    }
}
struct A(NodeKind);
impl markup::Render for A {
    fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
        writer.write_fmt(format_args!("{}", &self.0 as &dyn UriDisplay<Query>))
    }
}