add: outbox tab on profile
tab is only visible to profile owner and mods
This commit is contained in:
parent
5dec98d698
commit
7bda718082
12 changed files with 264 additions and 9 deletions
|
@ -65,6 +65,7 @@ pub const PROFILE_BLOCKED: &str = include_str!("./public/html/profile/blocked.li
|
||||||
pub const PROFILE_BANNED: &str = include_str!("./public/html/profile/banned.lisp");
|
pub const PROFILE_BANNED: &str = include_str!("./public/html/profile/banned.lisp");
|
||||||
pub const PROFILE_REPLIES: &str = include_str!("./public/html/profile/replies.lisp");
|
pub const PROFILE_REPLIES: &str = include_str!("./public/html/profile/replies.lisp");
|
||||||
pub const PROFILE_MEDIA: &str = include_str!("./public/html/profile/media.lisp");
|
pub const PROFILE_MEDIA: &str = include_str!("./public/html/profile/media.lisp");
|
||||||
|
pub const PROFILE_OUTBOX: &str = include_str!("./public/html/profile/outbox.lisp");
|
||||||
|
|
||||||
pub const COMMUNITIES_LIST: &str = include_str!("./public/html/communities/list.lisp");
|
pub const COMMUNITIES_LIST: &str = include_str!("./public/html/communities/list.lisp");
|
||||||
pub const COMMUNITIES_BASE: &str = include_str!("./public/html/communities/base.lisp");
|
pub const COMMUNITIES_BASE: &str = include_str!("./public/html/communities/base.lisp");
|
||||||
|
@ -144,7 +145,13 @@ pub(crate) async fn pull_icon(icon: &str, icons_dir: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("download icon: {icon}");
|
println!("download icon: {icon}");
|
||||||
let svg = reqwest::get(icon_url).await.unwrap().text().await.unwrap();
|
let svg = reqwest::get(icon_url)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.replace("\n", "");
|
||||||
|
|
||||||
write(&file_path, &svg).unwrap();
|
write(&file_path, &svg).unwrap();
|
||||||
writer.insert(icon.to_string(), svg);
|
writer.insert(icon.to_string(), svg);
|
||||||
|
@ -331,6 +338,7 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD {
|
||||||
write_template!(html_path->"profile/banned.html"(crate::assets::PROFILE_BANNED) --config=config --lisp plugins);
|
write_template!(html_path->"profile/banned.html"(crate::assets::PROFILE_BANNED) --config=config --lisp plugins);
|
||||||
write_template!(html_path->"profile/replies.html"(crate::assets::PROFILE_REPLIES) --config=config --lisp plugins);
|
write_template!(html_path->"profile/replies.html"(crate::assets::PROFILE_REPLIES) --config=config --lisp plugins);
|
||||||
write_template!(html_path->"profile/media.html"(crate::assets::PROFILE_MEDIA) --config=config --lisp plugins);
|
write_template!(html_path->"profile/media.html"(crate::assets::PROFILE_MEDIA) --config=config --lisp plugins);
|
||||||
|
write_template!(html_path->"profile/outbox.html"(crate::assets::PROFILE_OUTBOX) --config=config --lisp plugins);
|
||||||
|
|
||||||
write_template!(html_path->"communities/list.html"(crate::assets::COMMUNITIES_LIST) -d "communities" --config=config --lisp plugins);
|
write_template!(html_path->"communities/list.html"(crate::assets::COMMUNITIES_LIST) -d "communities" --config=config --lisp plugins);
|
||||||
write_template!(html_path->"communities/base.html"(crate::assets::COMMUNITIES_BASE) --config=config --lisp plugins);
|
write_template!(html_path->"communities/base.html"(crate::assets::COMMUNITIES_BASE) --config=config --lisp plugins);
|
||||||
|
|
|
@ -70,6 +70,7 @@ version = "1.0.0"
|
||||||
"auth:label.posts" = "Posts"
|
"auth:label.posts" = "Posts"
|
||||||
"auth:label.replies" = "Replies"
|
"auth:label.replies" = "Replies"
|
||||||
"auth:label.media" = "Media"
|
"auth:label.media" = "Media"
|
||||||
|
"auth:label.outbox" = "Outbox"
|
||||||
"auth:label.before_you_view" = "Before you view"
|
"auth:label.before_you_view" = "Before you view"
|
||||||
"auth:label.private_profile" = "Private profile"
|
"auth:label.private_profile" = "Private profile"
|
||||||
"auth:label.private_profile_message" = "This profile is private, meaning you can only view it if they follow you."
|
"auth:label.private_profile_message" = "This profile is private, meaning you can only view it if they follow you."
|
||||||
|
|
|
@ -1292,10 +1292,25 @@ details summary::-webkit-details-marker {
|
||||||
}
|
}
|
||||||
|
|
||||||
details[open] summary {
|
details[open] summary {
|
||||||
background: hsla(var(--color-primary-hsl), 25%);
|
position: relative;
|
||||||
|
color: var(--color-primary);
|
||||||
|
background: var(--color-super-lowered);
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details[open] summary::after {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 5px;
|
||||||
|
content: "";
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-top-left-radius: var(--radius);
|
||||||
|
border-bottom-left-radius: var(--radius);
|
||||||
|
animation: fadein ease-in-out 1 0.1s forwards running;
|
||||||
|
}
|
||||||
|
|
||||||
details .card {
|
details .card {
|
||||||
background: var(--color-super-raised);
|
background: var(--color-super-raised);
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,8 +202,9 @@
|
||||||
(text "{%- endif %} {%- endif %}"))
|
(text "{%- endif %} {%- endif %}"))
|
||||||
(text "{{ self::post_media(upload_ids=post.uploads) }} {% else %}")
|
(text "{{ self::post_media(upload_ids=post.uploads) }} {% else %}")
|
||||||
(details
|
(details
|
||||||
|
("class" "card tiny tertiary w-full")
|
||||||
(summary
|
(summary
|
||||||
("class" "card flex gap-2 flex-wrap items-center tertiary red w-full")
|
("class" "flex gap-2 flex-wrap items-center red w-full")
|
||||||
(text "{{ icon \"triangle-alert\" }}")
|
(text "{{ icon \"triangle-alert\" }}")
|
||||||
(b
|
(b
|
||||||
(text "{{ post.context.content_warning }}")))
|
(text "{{ post.context.content_warning }}")))
|
||||||
|
@ -558,7 +559,7 @@
|
||||||
(text "{{ self::avatar(username=owner.username, selector_type=\"username\", size=\"52px\") }}"))
|
(text "{{ self::avatar(username=owner.username, selector_type=\"username\", size=\"52px\") }}"))
|
||||||
(text "{%- endif %}")
|
(text "{%- endif %}")
|
||||||
(div
|
(div
|
||||||
("class" "flex flex-col gap-1")
|
("class" "flex flex-col gap-1 w-full")
|
||||||
(div
|
(div
|
||||||
("class" "flex items-center gap-2 flex-wrap")
|
("class" "flex items-center gap-2 flex-wrap")
|
||||||
(span
|
(span
|
||||||
|
@ -606,6 +607,21 @@
|
||||||
("class" "no_p_margin")
|
("class" "no_p_margin")
|
||||||
("style" "font-weight: 500")
|
("style" "font-weight: 500")
|
||||||
(text "{{ question.content|markdown|safe }}"))
|
(text "{{ question.content|markdown|safe }}"))
|
||||||
|
; anonymous user ip thing
|
||||||
|
; this is only shown if the post author is anonymous AND we are a helper
|
||||||
|
(text "{% if is_helper and owner.id == 0 %}")
|
||||||
|
(details
|
||||||
|
("class" "card tiny tertiary w-full")
|
||||||
|
(summary
|
||||||
|
("class" "w-full flex gap-2 flex-wrap items-center")
|
||||||
|
(icon (text "shield"))
|
||||||
|
(span (text "View IP")))
|
||||||
|
|
||||||
|
(div
|
||||||
|
("class" "card secondary")
|
||||||
|
(pre (code (text "{{ question.ip }}")))))
|
||||||
|
(text "{% endif %}")
|
||||||
|
; ...
|
||||||
(div
|
(div
|
||||||
("class" "flex gap-2 items-center justify-between"))))
|
("class" "flex gap-2 items-center justify-between"))))
|
||||||
|
|
||||||
|
|
|
@ -211,5 +211,18 @@
|
||||||
(a
|
(a
|
||||||
("href" "/@{{ profile.username }}/media")
|
("href" "/@{{ profile.username }}/media")
|
||||||
("class" "{% if selected == 'media' -%}active{%- endif %}")
|
("class" "{% if selected == 'media' -%}active{%- endif %}")
|
||||||
(str (text "auth:label.media"))))
|
(str (text "auth:label.media")))
|
||||||
|
|
||||||
|
(text "{% if is_self or is_helper %}")
|
||||||
|
(a
|
||||||
|
("href" "/@{{ profile.username }}/outbox")
|
||||||
|
("class" "{% if selected == 'outbox' -%}active{%- endif %}")
|
||||||
|
(str (text "auth:label.outbox")))
|
||||||
|
(text "{% endif %}")
|
||||||
|
|
||||||
|
(text "{% if is_helper %}")
|
||||||
|
(a
|
||||||
|
("href" "/requests?id={{ profile.id }}")
|
||||||
|
(str (text "requests:label.requests")))
|
||||||
|
(text "{% endif %}"))
|
||||||
(text "{%- endmacro %}")
|
(text "{%- endmacro %}")
|
||||||
|
|
|
@ -5,6 +5,17 @@
|
||||||
(text "{% endblock %} {% block body %} {{ macros::nav(selected=\"requests\") }}")
|
(text "{% endblock %} {% block body %} {{ macros::nav(selected=\"requests\") }}")
|
||||||
(main
|
(main
|
||||||
("class" "flex flex-col gap-2")
|
("class" "flex flex-col gap-2")
|
||||||
|
|
||||||
|
; viewing other user's requests warning
|
||||||
|
(text "{% if profile.id != user.id -%}")
|
||||||
|
(div
|
||||||
|
("class" "card w-full red flex gap-2 items-center")
|
||||||
|
(text "{{ icon \"skull\" }}")
|
||||||
|
(b
|
||||||
|
(text "Viewing other user's requests! Please be careful.")))
|
||||||
|
(text "{%- endif %}")
|
||||||
|
|
||||||
|
; ...
|
||||||
(div
|
(div
|
||||||
("class" "card-nest")
|
("class" "card-nest")
|
||||||
(div
|
(div
|
||||||
|
@ -14,12 +25,14 @@
|
||||||
(text "{{ icon \"inbox\" }}")
|
(text "{{ icon \"inbox\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"requests:label.requests\" }}")))
|
(text "{{ text \"requests:label.requests\" }}")))
|
||||||
|
(text "{% if profile.id == user.id -%}")
|
||||||
(button
|
(button
|
||||||
("onclick" "clear_requests()")
|
("onclick" "clear_requests()")
|
||||||
("class" "small red quaternary")
|
("class" "small red quaternary")
|
||||||
(text "{{ icon \"bomb\" }}")
|
(text "{{ icon \"bomb\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"notifs:action.clear\" }}"))))
|
(text "{{ text \"notifs:action.clear\" }}")))
|
||||||
|
(text "{% endif %}"))
|
||||||
(div
|
(div
|
||||||
("class" "card tertiary flex flex-col gap-4")
|
("class" "card tertiary flex flex-col gap-4")
|
||||||
(text "{% for request in requests %} {% if request.action_type == \"CommunityJoin\" %}")
|
(text "{% for request in requests %} {% if request.action_type == \"CommunityJoin\" %}")
|
||||||
|
|
44
crates/app/src/public/html/profile/outbox.lisp
Normal file
44
crates/app/src/public/html/profile/outbox.lisp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
(text "{% extends \"profile/base.html\" %} {% block content %} {% if profile.settings.enable_questions and (user or profile.settings.allow_anonymous_questions) %}")
|
||||||
|
(div
|
||||||
|
("style" "display: contents")
|
||||||
|
(text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header) }}"))
|
||||||
|
|
||||||
|
(text "{%- endif %} {{ macros::profile_nav(selected=\"outbox\") }}")
|
||||||
|
(div
|
||||||
|
("class" "card-nest")
|
||||||
|
(div
|
||||||
|
("class" "card small flex gap-2 justify-between items-center")
|
||||||
|
(div
|
||||||
|
("class" "flex gap-2 items-center")
|
||||||
|
(text "{{ icon \"send\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"auth:label.outbox\" }}"))))
|
||||||
|
(div
|
||||||
|
("class" "card tertiary flex flex-col gap-4")
|
||||||
|
(text "{% for question in questions %}")
|
||||||
|
(div
|
||||||
|
("class" "card-nest")
|
||||||
|
|
||||||
|
; show the actual question
|
||||||
|
(text "{{ components::question(question=question[0], owner=question[1], profile=user, secondary=true) }}")
|
||||||
|
|
||||||
|
; options
|
||||||
|
(div
|
||||||
|
("class" "card small flex justify-between items-center gap-2")
|
||||||
|
; show the avatar of the person we sent the question to
|
||||||
|
(a
|
||||||
|
("class" "flex items-center gap-2 flush")
|
||||||
|
("href" "/api/v1/auth/user/find/{{ question[0].receiver }}")
|
||||||
|
(icon (text "send"))
|
||||||
|
(text "{{ components::avatar(username=question[0].receiver, selector_type='id') }}"))
|
||||||
|
|
||||||
|
; show button to delete question
|
||||||
|
(button
|
||||||
|
("class" "quaternary small red")
|
||||||
|
("onclick" "trigger('me::remove_question', ['{{ question[0].id }}'])")
|
||||||
|
(icon (text "trash"))
|
||||||
|
(str (text "general:action.delete")))))
|
||||||
|
(text "{% endfor %}")
|
||||||
|
(text "{{ components::pagination(page=page, items=questions|length) }}")))
|
||||||
|
|
||||||
|
(text "{% endblock %}")
|
|
@ -133,7 +133,7 @@ media_theme_pref();
|
||||||
|
|
||||||
element.setAttribute("title", then.toLocaleString());
|
element.setAttribute("title", then.toLocaleString());
|
||||||
|
|
||||||
let pretty = $.rel_date(then);
|
let pretty = $.rel_date(then) || "";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(screen.width < 900 && pretty !== undefined) |
|
(screen.width < 900 && pretty !== undefined) |
|
||||||
|
|
|
@ -387,10 +387,17 @@ pub async fn notifications_request(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct RequestsProps {
|
||||||
|
#[serde(default)]
|
||||||
|
pub id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// `/requests`
|
/// `/requests`
|
||||||
pub async fn requests_request(
|
pub async fn requests_request(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Extension(data): Extension<State>,
|
Extension(data): Extension<State>,
|
||||||
|
Query(props): Query<RequestsProps>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let data = data.read().await;
|
let data = data.read().await;
|
||||||
let user = match get_user_from_token!(jar, data.0) {
|
let user = match get_user_from_token!(jar, data.0) {
|
||||||
|
@ -402,7 +409,20 @@ pub async fn requests_request(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let requests = match data.0.get_requests_by_owner(user.id).await {
|
let profile = if props.id != 0 {
|
||||||
|
match data.0.get_user_by_id(props.id).await {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &None).await)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let requests = match data
|
||||||
|
.0
|
||||||
|
.get_requests_by_owner(if props.id != 0 { props.id } else { user.id })
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
};
|
};
|
||||||
|
@ -448,6 +468,8 @@ pub async fn requests_request(
|
||||||
|
|
||||||
let lang = get_lang!(jar, data.0);
|
let lang = get_lang!(jar, data.0);
|
||||||
let mut context = initial_context(&data.0.0, lang, &Some(user)).await;
|
let mut context = initial_context(&data.0.0, lang, &Some(user)).await;
|
||||||
|
|
||||||
|
context.insert("profile", &profile);
|
||||||
context.insert("requests", &requests);
|
context.insert("requests", &requests);
|
||||||
context.insert("questions", &questions);
|
context.insert("questions", &questions);
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ pub fn routes() -> Router {
|
||||||
.route("/settings", get(profile::settings_request))
|
.route("/settings", get(profile::settings_request))
|
||||||
.route("/@{username}", get(profile::posts_request))
|
.route("/@{username}", get(profile::posts_request))
|
||||||
.route("/@{username}/media", get(profile::media_request))
|
.route("/@{username}/media", get(profile::media_request))
|
||||||
|
.route("/@{username}/outbox", get(profile::outbox_request))
|
||||||
.route("/@{username}/replies", get(profile::replies_request))
|
.route("/@{username}/replies", get(profile::replies_request))
|
||||||
.route("/@{username}/following", get(profile::following_request))
|
.route("/@{username}/following", get(profile::following_request))
|
||||||
.route("/@{username}/followers", get(profile::followers_request))
|
.route("/@{username}/followers", get(profile::followers_request))
|
||||||
|
|
|
@ -573,6 +573,102 @@ pub async fn media_request(
|
||||||
Ok(Html(data.1.render("profile/media.html", &context).unwrap()))
|
Ok(Html(data.1.render("profile/media.html", &context).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `/@{username}/outbox`
|
||||||
|
pub async fn outbox_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Path(username): Path<String>,
|
||||||
|
Query(props): Query<PaginatedQuery>,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = data.read().await;
|
||||||
|
let user = match get_user_from_token!(jar, data.0) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => {
|
||||||
|
return Err(Html(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &None).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let other_user = match data.0.get_user_by_username(&username).await {
|
||||||
|
Ok(ua) => ua,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if user.id != other_user.id && !user.permissions.check(FinePermission::MANAGE_QUESTIONS) {
|
||||||
|
return Err(Html(
|
||||||
|
render_error(Error::NotAllowed, &jar, &data, &Some(user)).await,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
check_user_blocked_or_private!(Some(user.clone()), other_user, data, jar);
|
||||||
|
|
||||||
|
// fetch data
|
||||||
|
let ignore_users = crate::ignore_users_gen!(user!, data);
|
||||||
|
|
||||||
|
let questions = match data
|
||||||
|
.0
|
||||||
|
.get_questions_by_owner_paginated(other_user.id, 12, props.page)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(p) => match data.0.fill_questions(p, &ignore_users).await {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let communities = match data.0.get_memberships_by_owner(other_user.id).await {
|
||||||
|
Ok(m) => match data.0.fill_communities(m).await {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// init context
|
||||||
|
let lang = get_lang!(jar, data.0);
|
||||||
|
let mut context = initial_context(&data.0.0, lang, &Some(user.clone())).await;
|
||||||
|
|
||||||
|
let is_self = user.id == other_user.id;
|
||||||
|
|
||||||
|
let is_following = data
|
||||||
|
.0
|
||||||
|
.get_userfollow_by_initiator_receiver(user.id, other_user.id)
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
let is_following_you = data
|
||||||
|
.0
|
||||||
|
.get_userfollow_by_receiver_initiator(user.id, other_user.id)
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
let is_blocking = data
|
||||||
|
.0
|
||||||
|
.get_userblock_by_initiator_receiver(user.id, other_user.id)
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
context.insert("questions", &questions);
|
||||||
|
context.insert("page", &props.page);
|
||||||
|
profile_context(
|
||||||
|
&mut context,
|
||||||
|
&Some(user),
|
||||||
|
&other_user,
|
||||||
|
&communities,
|
||||||
|
is_self,
|
||||||
|
is_following,
|
||||||
|
is_following_you,
|
||||||
|
is_blocking,
|
||||||
|
);
|
||||||
|
|
||||||
|
// return
|
||||||
|
Ok(Html(
|
||||||
|
data.1.render("profile/outbox.html", &context).unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// `/@{username}/following`
|
/// `/@{username}/following`
|
||||||
pub async fn following_request(
|
pub async fn following_request(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
|
|
|
@ -97,6 +97,32 @@ impl DataManager {
|
||||||
Ok(res.unwrap())
|
Ok(res.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get all questions by `owner` (paginated).
|
||||||
|
pub async fn get_questions_by_owner_paginated(
|
||||||
|
&self,
|
||||||
|
owner: usize,
|
||||||
|
batch: usize,
|
||||||
|
page: usize,
|
||||||
|
) -> Result<Vec<Question>> {
|
||||||
|
let conn = match self.connect().await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = query_rows!(
|
||||||
|
&conn,
|
||||||
|
"SELECT * FROM questions WHERE owner = $1 AND NOT context LIKE '%\"is_nsfw\":true%' ORDER BY created DESC LIMIT $2 OFFSET $3",
|
||||||
|
&[&(owner as i64), &(batch as i64), &((page * batch) as i64)],
|
||||||
|
|x| { Self::get_question_from_row(x) }
|
||||||
|
);
|
||||||
|
|
||||||
|
if res.is_err() {
|
||||||
|
return Err(Error::GeneralNotFound("question".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all questions by `receiver`.
|
/// Get all questions by `receiver`.
|
||||||
pub async fn get_questions_by_receiver(&self, receiver: usize) -> Result<Vec<Question>> {
|
pub async fn get_questions_by_receiver(&self, receiver: usize) -> Result<Vec<Question>> {
|
||||||
let conn = match self.connect().await {
|
let conn = match self.connect().await {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue