1use chrono::{DateTime, Utc};
4use macros::make_path_parts;
5use serde::{Deserialize, Serialize};
6
7use crate::api::endpoints::PathPart;
8use strum_macros::Display;
9
10use super::{asset::UserOrMe, image::ImageId, user::UserId};
11
12wrap_uuid! {
13 #[serde(rename_all = "camelCase")]
15 pub struct CircleId
16}
17
18#[derive(Serialize, Deserialize, Debug, Clone)]
20#[serde(rename_all = "camelCase")]
21pub struct Circle {
22 pub id: CircleId,
24
25 pub display_name: String,
27
28 pub created_by: UserId,
30
31 pub description: String,
33
34 pub member_count: u32,
36
37 pub image: ImageId,
39
40 pub created_at: DateTime<Utc>,
42
43 pub last_edited: Option<DateTime<Utc>>,
45
46 pub joined: bool,
48}
49
50make_path_parts!(CircleCreatePath => "/v1/circle");
51
52#[derive(Serialize, Deserialize, Debug)]
56#[serde(rename_all = "camelCase")]
57pub struct CircleCreateRequest {
58 pub display_name: String,
60
61 pub description: String,
63
64 pub image: ImageId,
66}
67
68make_path_parts!(UpdateCirclePath => "/v1/circle/{}" => CircleId);
69
70#[derive(Serialize, Deserialize, Debug, Default)]
72#[serde(rename_all = "camelCase")]
73pub struct CircleUpdateRequest {
74 #[serde(skip_serializing_if = "Option::is_none")]
76 #[serde(default)]
77 pub display_name: Option<String>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 #[serde(default)]
82 pub description: Option<String>,
83
84 #[serde(skip_serializing_if = "Option::is_none")]
86 #[serde(default)]
87 pub image: Option<ImageId>,
88}
89
90make_path_parts!(CircleBrowsePath => "/v1/circle/browse");
91
92#[derive(Serialize, Deserialize, Clone, Debug, Default)]
94#[serde(rename_all = "camelCase")]
95pub struct CircleBrowseQuery {
96 #[serde(default)]
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub creator_id: Option<UserOrMe>,
100
101 #[serde(default)]
104 #[serde(serialize_with = "super::csv_encode_uuids")]
105 #[serde(deserialize_with = "super::from_csv")]
106 #[serde(skip_serializing_if = "Vec::is_empty")]
107 pub users: Vec<UserId>,
108
109 #[serde(default)]
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub page: Option<u32>,
113
114 #[serde(default)]
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub page_limit: Option<u32>,
118
119 #[serde(default)]
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub order_by: Option<OrderBy>,
123}
124
125#[derive(Serialize, Deserialize, Clone, Debug)]
127#[serde(rename_all = "camelCase")]
128pub struct CircleBrowseResponse {
129 pub circles: Vec<Circle>,
131
132 pub pages: u32,
134
135 pub total_circle_count: u64,
137}
138
139make_path_parts!(CircleSearchPath => "/v1/circle");
140
141#[derive(Serialize, Deserialize, Clone, Debug, Default)]
143#[serde(rename_all = "camelCase")]
144pub struct CircleSearchQuery {
145 #[serde(default)]
147 #[serde(skip_serializing_if = "String::is_empty")]
148 pub q: String,
149
150 #[serde(default)]
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub page: Option<u32>,
154
155 #[serde(default)]
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub creator_id: Option<UserOrMe>,
159
160 #[serde(default)]
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub creator_name: Option<String>,
164
165 #[serde(default)]
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub page_limit: Option<u32>,
169}
170
171#[derive(Serialize, Deserialize, Clone, Debug)]
173#[serde(rename_all = "camelCase")]
174pub struct CircleSearchResponse {
175 pub circles: Vec<Circle>,
177
178 pub pages: u32,
180
181 pub total_circle_count: u64,
183}
184
185make_path_parts!(CircleDeletePath => "/v1/circle/{}" => CircleId);
186
187make_path_parts!(CircleGetPath => "/v1/circle/{}" => CircleId);
188
189make_path_parts!(JoinCirclePath => "/v1/circle/{}/join" => CircleId);
190
191make_path_parts!(LeaveCirclePath => "/v1/circle/{}/leave" => CircleId);
192
193make_path_parts!(CircleRemoveMemberPath => "/v1/circle/{}/members/{}" => CircleId, UserId);
194
195make_path_parts!(CircleBrowseMembersPath => "/v1/circle/{}/members" => CircleId);
196
197#[derive(Serialize, Deserialize, Clone, Debug, Default)]
199#[serde(rename_all = "camelCase")]
200pub struct BrowseMembersQuery {
201 #[serde(default)]
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub admin: Option<bool>,
205}
206
207#[derive(Serialize, Deserialize)]
209pub struct BrowseMembersResponse {
210 pub members: Vec<UserId>,
212
213 pub count: u32,
215}
216
217#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Debug, Display)]
219#[cfg_attr(feature = "backend", derive(sqlx::Type))]
220#[serde(rename_all = "camelCase")]
221#[repr(i16)]
222pub enum OrderBy {
223 #[strum(serialize = "MemberCount")]
225 MemberCount = 0,
226}