Add server emoji picker
This commit is contained in:
		@@ -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: () =>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										84
									
								
								src/web/routes/dashboard/api/guild/emojis.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/web/routes/dashboard/api/guild/emojis.rs
									
									
									
									
									
										Normal 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")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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},
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user