Add server emoji picker

This commit is contained in:
jude 2024-03-31 12:49:52 +01:00
parent 2861cdda0b
commit b0f932445c
6 changed files with 114 additions and 4 deletions

View File

@ -59,6 +59,11 @@ type RoleInfo = {
name: string; name: string;
}; };
type EmojiInfo = {
fmt: string;
name: string;
};
type Template = { type Template = {
id: number; id: number;
name: string; name: string;
@ -125,6 +130,15 @@ export const fetchGuildRoles = (guild: string) => ({
staleTime: GUILD_INFO_STALE_TIME, staleTime: GUILD_INFO_STALE_TIME,
}); });
export const fetchGuildEmojis = (guild: string) => ({
queryKey: ["GUILD_EMOJIS", guild],
queryFn: () =>
axios.get(`/dashboard/api/guild/${guild}/emojis`).then((resp) => resp.data) as Promise<
EmojiInfo[]
>,
staleTime: GUILD_INFO_STALE_TIME,
});
export const fetchGuildReminders = (guild: string) => ({ export const fetchGuildReminders = (guild: string) => ({
queryKey: ["GUILD_REMINDERS", guild], queryKey: ["GUILD_REMINDERS", guild],
queryFn: () => queryFn: () =>

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo } from "preact/hooks"; import { useEffect, useMemo } from "preact/hooks";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import { fetchGuildChannels, fetchGuildRoles } from "../../api"; import { fetchGuildChannels, fetchGuildRoles, fetchGuildEmojis } from "../../api";
import Tribute from "tributejs"; import Tribute from "tributejs";
import { useGuild } from "./useGuild"; import { useGuild } from "./useGuild";
@ -9,6 +9,7 @@ export const Mentions = ({ input }) => {
const { data: roles } = useQuery(fetchGuildRoles(guild)); const { data: roles } = useQuery(fetchGuildRoles(guild));
const { data: channels } = useQuery(fetchGuildChannels(guild)); const { data: channels } = useQuery(fetchGuildChannels(guild));
const { data: emojis } = useQuery(fetchGuildEmojis(guild));
const tribute = useMemo(() => { const tribute = useMemo(() => {
return new Tribute({ return new Tribute({
@ -27,9 +28,16 @@ export const Mentions = ({ input }) => {
selectTemplate: (item) => `<#${item.original.value}>`, selectTemplate: (item) => `<#${item.original.value}>`,
menuItemTemplate: (item) => `#${item.original.key}`, menuItemTemplate: (item) => `#${item.original.key}`,
}, },
{
trigger: ":",
values: (emojis || []).map(({ fmt, name }) => ({ key: name, value: fmt })),
allowSpaces: true,
selectTemplate: (item) => item.original.value,
menuItemTemplate: (item) => `:${item.original.key}:`,
},
], ],
}); });
}, [roles, channels]); }, [roles, channels, emojis]);
useEffect(() => { useEffect(() => {
tribute.detach(input.current); tribute.detach(input.current);

View File

@ -8,6 +8,7 @@ export const GuildError = () => {
The bot may have just been restarted, in which case please try again in a The bot may have just been restarted, in which case please try again in a
few minutes. few minutes.
<br /> <br />
<br />
Otherwise, please check Reminder Bot is in the server, and has correct Otherwise, please check Reminder Bot is in the server, and has correct
permissions. permissions.
</p> </p>

View File

@ -138,6 +138,7 @@ pub async fn initialize(
routes::dashboard::api::guild::get_guild_info, routes::dashboard::api::guild::get_guild_info,
routes::dashboard::api::guild::get_guild_channels, routes::dashboard::api::guild::get_guild_channels,
routes::dashboard::api::guild::get_guild_roles, routes::dashboard::api::guild::get_guild_roles,
routes::dashboard::api::guild::get_guild_emojis,
routes::dashboard::api::guild::get_reminder_templates, routes::dashboard::api::guild::get_reminder_templates,
routes::dashboard::api::guild::create_reminder_template, routes::dashboard::api::guild::create_reminder_template,
routes::dashboard::api::guild::delete_reminder_template, routes::dashboard::api::guild::delete_reminder_template,

View File

@ -0,0 +1,84 @@
use std::{collections::HashMap, sync::OnceLock, time::Instant};
use log::{info, warn};
use rocket::{get, http::CookieJar, serde::json::json, State};
use serde::Serialize;
use serenity::{client::Context, model::id::GuildId};
use tokio::sync::RwLock;
use crate::web::{check_authorization, routes::JsonResult};
#[derive(Serialize, Clone)]
struct EmojiInfo {
fmt: String,
name: String,
}
#[derive(Clone)]
struct EmojiCache {
emojis: Vec<EmojiInfo>,
timestamp: Instant,
}
const CACHE_LENGTH: u64 = 120;
static EMOJI_CACHE: OnceLock<RwLock<HashMap<GuildId, EmojiCache>>> = OnceLock::new();
#[get("/api/guild/<id>/emojis")]
pub async fn get_guild_emojis(
id: u64,
cookies: &CookieJar<'_>,
ctx: &State<Context>,
) -> JsonResult {
offline!(Ok(json!(vec![] as Vec<EmojiInfo>)));
check_authorization(cookies, ctx.inner(), id).await?;
let cache_value = {
let cache = EMOJI_CACHE.get_or_init(|| RwLock::new(HashMap::new()));
let read_lock = cache.read().await;
read_lock.get(&GuildId::new(id)).cloned()
};
if let Some(emojis) = cache_value
.map(|v| {
if Instant::now().duration_since(v.timestamp).as_secs() < CACHE_LENGTH {
Some(v.emojis)
} else {
None
}
})
.flatten()
{
Ok(json!(emojis))
} else {
let emojis_res = ctx.http.get_emojis(GuildId::new(id)).await;
match emojis_res {
Ok(emojis) => {
let emojis = emojis
.iter()
.map(|emoji| EmojiInfo {
fmt: format!("{}", emoji),
name: emoji.name.to_string(),
})
.collect::<Vec<EmojiInfo>>();
{
let cache = EMOJI_CACHE.get_or_init(|| RwLock::new(HashMap::new()));
let mut write_lock = cache.write().await;
write_lock.insert(
GuildId::new(id),
EmojiCache { emojis: emojis.clone(), timestamp: Instant::now() },
);
}
Ok(json!(emojis))
}
Err(e) => {
warn!("Could not fetch emojis from {}: {:?}", id, e);
json_err!("Could not get emojis")
}
}
}
}

View File

@ -1,14 +1,16 @@
mod channels; mod channels;
mod emojis;
mod reminders; mod reminders;
mod roles; mod roles;
mod templates; mod templates;
use std::env; use std::env;
pub use channels::*; pub use channels::get_guild_channels;
pub use emojis::get_guild_emojis;
pub use reminders::*; pub use reminders::*;
use rocket::{get, http::CookieJar, serde::json::json, State}; use rocket::{get, http::CookieJar, serde::json::json, State};
pub use roles::*; pub use roles::get_guild_roles;
use serenity::{ use serenity::{
client::Context, client::Context,
model::id::{GuildId, RoleId}, model::id::{GuildId, RoleId},