Skip to content

Commit

Permalink
Adding and removing bots to groups and communities (#7069)
Browse files Browse the repository at this point in the history
  • Loading branch information
julianjelfs authored Dec 16, 2024
1 parent 2fb2b43 commit d3f9d43
Show file tree
Hide file tree
Showing 57 changed files with 1,212 additions and 184 deletions.
1 change: 1 addition & 0 deletions backend/canisters/community/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ fn main() {
generate_ts_method!(community, undelete_messages);
generate_ts_method!(community, unfollow_thread);
generate_ts_method!(community, unpin_message);
generate_ts_method!(community, update_bot);
generate_ts_method!(community, update_channel);
generate_ts_method!(community, update_community);
generate_ts_method!(community, update_user_group);
Expand Down
4 changes: 2 additions & 2 deletions backend/canisters/community/api/src/updates/update_bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ use serde::{Deserialize, Serialize};
use ts_export::ts_export;
use types::{SlashCommandPermissions, UserId};

#[ts_export(community, add_bot)]
#[ts_export(community, update_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct Args {
pub bot_id: UserId,
pub granted_permissions: SlashCommandPermissions,
}

#[ts_export(community, add_bot)]
#[ts_export(community, update_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success,
Expand Down
1 change: 1 addition & 0 deletions backend/canisters/group/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ fn main() {
generate_ts_method!(group, undelete_messages);
generate_ts_method!(group, unfollow_thread);
generate_ts_method!(group, unpin_message);
generate_ts_method!(group, update_bot);
generate_ts_method!(group, update_group_v2);

candid::export_service!();
Expand Down
4 changes: 2 additions & 2 deletions backend/canisters/group/api/src/updates/add_bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ use serde::{Deserialize, Serialize};
use ts_export::ts_export;
use types::{SlashCommandPermissions, UserId};

#[ts_export(community, add_bot)]
#[ts_export(group, add_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct Args {
pub bot_id: UserId,
pub granted_permissions: SlashCommandPermissions,
}

#[ts_export(community, add_bot)]
#[ts_export(group, add_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success,
Expand Down
4 changes: 2 additions & 2 deletions backend/canisters/group/api/src/updates/remove_bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use serde::{Deserialize, Serialize};
use ts_export::ts_export;
use types::UserId;

#[ts_export(community, remove_bot)]
#[ts_export(group, remove_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct Args {
pub bot_id: UserId,
}

#[ts_export(community, remove_bot)]
#[ts_export(group, remove_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success,
Expand Down
4 changes: 2 additions & 2 deletions backend/canisters/group/api/src/updates/update_bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ use serde::{Deserialize, Serialize};
use ts_export::ts_export;
use types::{SlashCommandPermissions, UserId};

#[ts_export(community, add_bot)]
#[ts_export(group, update_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub struct Args {
pub bot_id: UserId,
pub granted_permissions: SlashCommandPermissions,
}

#[ts_export(community, add_bot)]
#[ts_export(group, update_bot)]
#[derive(CandidType, Serialize, Deserialize, Debug)]
pub enum Response {
Success,
Expand Down
13 changes: 9 additions & 4 deletions frontend/app/src/components/bots/BotExplorer.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<script lang="ts">
import { i18nKey, type CommunitySummary, type OpenChat } from "openchat-client";
import {
i18nKey,
type CommunityIdentifier,
type GroupChatIdentifier,
type OpenChat,
} from "openchat-client";
import Search from "../Search.svelte";
import { getContext } from "svelte";
import { botSearchState } from "../../stores/search.svelte";
Expand All @@ -11,10 +16,10 @@
const PAGE_SIZE = 50;
interface Props {
community: CommunitySummary;
id: CommunityIdentifier | GroupChatIdentifier;
}
let { community }: Props = $props();
let { id }: Props = $props();
let initialised = $state(false);
let searching = $state(false);
Expand Down Expand Up @@ -70,7 +75,7 @@
{/if}

{#each botSearchState.results as match}
<BotMatch {community} {match} />
<BotMatch {id} {match} />
{/each}

<style lang="scss"></style>
13 changes: 9 additions & 4 deletions frontend/app/src/components/bots/BotMatch.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
<script lang="ts">
import { AvatarSize, type BotMatch, type CommunitySummary } from "openchat-client";
import {
AvatarSize,
type BotMatch,
type CommunityIdentifier,
type GroupChatIdentifier,
} from "openchat-client";
import Avatar from "../Avatar.svelte";
import BotSummary from "./BotSummary.svelte";
import TooltipWrapper from "../TooltipWrapper.svelte";
import TooltipPopup from "../TooltipPopup.svelte";
interface Props {
match: BotMatch;
community: CommunitySummary;
id: CommunityIdentifier | GroupChatIdentifier;
}
let { match, community }: Props = $props();
let { match, id }: Props = $props();
let showing = $state(false);
</script>

{#if showing}
<BotSummary {community} onClose={() => (showing = false)} bot={match} />
<BotSummary mode={"adding"} {id} onClose={() => (showing = false)} bot={match} />
{/if}

<!-- svelte-ignore a11y_click_events_have_key_events -->
Expand Down
179 changes: 179 additions & 0 deletions frontend/app/src/components/bots/BotMember.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<script lang="ts">
import ChevronDown from "svelte-material-icons/ChevronDown.svelte";
import DeleteOutline from "svelte-material-icons/DeleteOutline.svelte";
import TextBoxOutline from "svelte-material-icons/TextBoxOutline.svelte";
import PencilOutline from "svelte-material-icons/PencilOutline.svelte";
import MenuIcon from "../MenuIcon.svelte";
import HoverIcon from "../HoverIcon.svelte";
import Menu from "../Menu.svelte";
import MenuItem from "../MenuItem.svelte";
import { _ } from "svelte-i18n";
import { iconSize } from "../../stores/iconSize";
import { AvatarSize, type ExternalBot } from "openchat-shared";
import Translatable from "../Translatable.svelte";
import { i18nKey } from "../../i18n/i18n";
import Avatar from "../Avatar.svelte";
import FilteredUsername from "../FilteredUsername.svelte";
import type {
CommunityIdentifier,
GroupChatIdentifier,
OpenChat,
SlashCommandPermissions,
} from "openchat-client";
import { getContext } from "svelte";
import { toastStore } from "../../stores/toast";
import BotSummary from "./BotSummary.svelte";
const client = getContext<OpenChat>("client");
interface Props {
id: CommunityIdentifier | GroupChatIdentifier;
bot: ExternalBot;
canManage: boolean;
searchTerm: string;
grantedPermissions: SlashCommandPermissions;
}
let { id, bot, canManage, searchTerm, grantedPermissions }: Props = $props();
let reviewMode: "editing" | "viewing" | undefined = $state(undefined);
function removeBot() {
client.removeBot(id, bot.id).then((success) => {
if (!success) {
toastStore.showFailureToast(i18nKey("bots.manage.removeFailed"));
}
});
}
function reviewPermissions() {
reviewMode = "editing";
}
function viewBotDetails() {
reviewMode = "viewing";
}
function closeModal() {
reviewMode = undefined;
}
</script>

{#if reviewMode !== undefined}
<BotSummary
currentPermissions={grantedPermissions}
mode={reviewMode}
{id}
onClose={closeModal}
{bot} />
{/if}

<div class="bot_member" role="button">
<span class="avatar">
<Avatar userId={bot.id} url={bot.avatarUrl} size={AvatarSize.Default} />
</span>
<div class="details">
<div class="bot_name">
<h4>
<FilteredUsername {searchTerm} username={bot.name} />
</h4>
</div>
<div class="bot_description">
<FilteredUsername {searchTerm} username={bot.description} />
</div>
</div>
<MenuIcon position={"bottom"} align={"end"}>
<span slot="icon">
<HoverIcon>
<ChevronDown size={$iconSize} color={"var(--icon-txt)"} />
</HoverIcon>
</span>
<span slot="menu">
<Menu>
{#if canManage}
<MenuItem on:click={() => removeBot()}>
<DeleteOutline
size={$iconSize}
color={"var(--icon-inverted-txt)"}
slot="icon" />
<div slot="text">
<Translatable resourceKey={i18nKey("bots.manage.remove")} />
</div>
</MenuItem>
<MenuItem on:click={() => reviewPermissions()}>
<PencilOutline
size={$iconSize}
color={"var(--icon-inverted-txt)"}
slot="icon" />
<div slot="text">
<Translatable resourceKey={i18nKey("bots.manage.review")} />
</div>
</MenuItem>
{/if}
<MenuItem on:click={() => viewBotDetails()}>
<TextBoxOutline
size={$iconSize}
color={"var(--icon-inverted-txt)"}
slot="icon" />
<div slot="text">
<Translatable resourceKey={i18nKey("bots.manage.view")} />
</div>
</MenuItem>
</Menu>
</span>
</MenuIcon>
</div>

<style lang="scss">
.bot_member {
display: flex;
justify-content: center;
align-items: center;
padding: $sp4;
transition:
background-color ease-in-out 100ms,
border-color ease-in-out 100ms;
gap: 12px;
cursor: pointer;
@media (hover: hover) {
&:hover {
background-color: var(--members-hv);
}
}
@include mobile() {
padding: $sp3 toRem(10);
}
}
.avatar {
flex: 0 0 50px;
position: relative;
}
.details {
display: flex;
flex: 1;
flex-direction: column;
@include font(medium, normal, fs-100);
.bot_name {
display: flex;
flex: 1;
align-items: center;
@include ellipsis();
h4 {
display: flex;
align-items: center;
gap: $sp2;
}
}
.bot_description {
font-weight: 200;
color: var(--txt-light);
@include clamp(2);
}
}
</style>
Loading

0 comments on commit d3f9d43

Please sign in to comment.