diff --git a/backend/open_webui/models/channels.py b/backend/open_webui/models/channels.py index 03ffd57c2..0e31d5e8e 100644 --- a/backend/open_webui/models/channels.py +++ b/backend/open_webui/models/channels.py @@ -64,9 +64,9 @@ class ChannelTable: self, form_data: ChannelForm, user_id: str ) -> Optional[ChannelModel]: with get_db() as db: - new_channel = Channel( + channel = ChannelModel( **{ - **form_data.model_dump(), + **form_data.dict(), "id": str(uuid.uuid4()), "user_id": user_id, "created_at": int(time.time()), @@ -74,9 +74,11 @@ class ChannelTable: } ) + new_channel = Channel(**channel.model_dump()) + db.add(new_channel) db.commit() - return new_channel + return channel def get_channels(self) -> list[ChannelModel]: with get_db() as db: diff --git a/src/lib/apis/channels/index.ts b/src/lib/apis/channels/index.ts new file mode 100644 index 000000000..8fd6f24f1 --- /dev/null +++ b/src/lib/apis/channels/index.ts @@ -0,0 +1,71 @@ +import { WEBUI_API_BASE_URL } from '$lib/constants'; + +type ChannelForm = { + name: string; + data?: object; + meta?: object; + access_control?: object; +} + +export const createNewChannel = async (token: string = '', channel: ChannelForm) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/channels/create`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + }, + body: JSON.stringify({ ...channel }) + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err.detail; + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + +export const getChannels = async (token: string = '') => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/channels/`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + authorization: `Bearer ${token}` + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .then((json) => { + return json; + }) + .catch((err) => { + error = err.detail; + console.log(err); + return null; + }); + + if (error) { + throw error; + } + + return res; +}; diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index e51551ca5..e7a07f2f1 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -16,7 +16,8 @@ pinnedChats, scrollPaginationEnabled, currentChatPage, - temporaryChatEnabled + temporaryChatEnabled, + channels } from '$lib/stores'; import { onMount, getContext, tick, onDestroy } from 'svelte'; @@ -49,6 +50,9 @@ import Plus from '../icons/Plus.svelte'; import Tooltip from '../common/Tooltip.svelte'; import Folders from './Sidebar/Folders.svelte'; + import { getChannels, createNewChannel } from '$lib/apis/channels'; + import CreateChannelModal from './Sidebar/CreateChannelModal.svelte'; + import ChannelItem from './Sidebar/ChannelItem.svelte'; const BREAKPOINT = 768; @@ -61,14 +65,14 @@ let showDropdown = false; let showPinnedChat = true; + let showCreateChannel = false; + // Pagination variables let chatListLoading = false; let allChatsLoaded = false; let folders = {}; - const createChannel = async () => {}; - const initFolders = async () => { const folderList = await getFolders(localStorage.token).catch((error) => { toast.error(error); @@ -145,6 +149,10 @@ } }; + const initChannels = async () => { + channels.set(await getChannels(localStorage.token)); + }; + const initChatList = async () => { // Reset pagination variables tags.set(await getAllTags(localStorage.token)); @@ -348,6 +356,7 @@ localStorage.sidebar = value; }); + await initChannels(); await initChatList(); window.addEventListener('keydown', onKeyDown); @@ -391,6 +400,13 @@ }} /> + { + await initChannels(); + }} +/> + {#if $showSidebar} @@ -533,10 +549,14 @@ className="px-2 mt-0.5" name={$i18n.t('Channels')} dragAndDrop={false} - onAdd={createChannel} - onAddLabel={$i18n.t('New Channel')} + onAdd={() => { + showCreateChannel = true; + }} + onAddLabel={$i18n.t('Create Channel')} > - channels + {#each $channels as channel} + + {/each} + import { toast } from 'svelte-sonner'; + import { onMount, getContext, createEventDispatcher, tick, onDestroy } from 'svelte'; + const i18n = getContext('i18n'); + + const dispatch = createEventDispatcher(); + + import { mobile, showSidebar } from '$lib/stores'; + import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte'; + + export let className = ''; + + export let id; + export let name; + + let itemElement; + + + diff --git a/src/lib/components/layout/Sidebar/CreateChannelModal.svelte b/src/lib/components/layout/Sidebar/CreateChannelModal.svelte new file mode 100644 index 000000000..ca629efa7 --- /dev/null +++ b/src/lib/components/layout/Sidebar/CreateChannelModal.svelte @@ -0,0 +1,138 @@ + + + +
+
+
{$i18n.t('Create Channel')}
+ +
+ +
+
+
{ + submitHandler(); + }} + > +
+
{$i18n.t('Channel Name')}
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts index 0319a5b25..63d3ee29f 100644 --- a/src/lib/stores/index.ts +++ b/src/lib/stores/index.ts @@ -23,6 +23,8 @@ export const theme = writable('system'); export const chatId = writable(''); export const chatTitle = writable(''); + +export const channels = writable([]); export const chats = writable([]); export const pinnedChats = writable([]); export const tags = writable([]);