Skip to content

Commit c71eed8

Browse files
authored
Merge pull request #309 from iceljc/features/refine-chat-window
rename
2 parents b047d96 + e41bc17 commit c71eed8

File tree

11 files changed

+176
-80
lines changed

11 files changed

+176
-80
lines changed

src/lib/common/FileGallery.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
<i class="bx bx-trash" />
104104
</div>
105105
{/if}
106-
{#if needDownload}
106+
{#if needDownload && !!file.file_download_url}
107107
<div
108108
class="gallery-item-icon download-icon clickable"
109109
tabindex="0"

src/lib/common/MessageFileGallery.svelte

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
/** @type {string} */
1414
export let galleryStyles = '';
1515
16-
/** @type {string} */
17-
export let messageId;
16+
/** @type {any} */
17+
export let message;
18+
19+
/** @type {boolean} */
20+
export let appendImage = false;
1821
1922
/** @type {() => Promise<any>} */
2023
export let fetchFiles = () => Promise.resolve([]);
@@ -25,31 +28,38 @@
2528
/** @type {import('$fileTypes').AudioFileModel[]} */
2629
let audioFiles = [];
2730
28-
onMount(() => {
31+
onMount(async () => {
2932
if (fetchFiles != null && fetchFiles != undefined) {
30-
fetchFiles().then(data => {
31-
// @ts-ignore
32-
const validFiles = data?.filter(item => !!item.file_url) || [];
33-
// @ts-ignore
34-
textFiles = validFiles.filter(item => !isAudio(item.file_extension)).map(item => {
35-
return {
36-
file_name: item.file_name,
37-
file_extension: item.file_extension,
38-
file_data: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`,
39-
file_download_url: isExternalUrl(item.file_download_url) ? item.file_download_url : `${PUBLIC_SERVICE_URL}${item.file_download_url}?access_token=${$userStore?.token}`
40-
};
41-
});
42-
// @ts-ignore
43-
audioFiles = validFiles.filter(item => isAudio(item.file_extension)).map(item => {
44-
return {
45-
name: item.file_name,
46-
cover: AUDIO_ICON,
47-
artist: '',
48-
url: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`
49-
};
50-
});
33+
const res = await fetchFiles();
34+
// @ts-ignore
35+
const validFiles = res?.filter(item => !!item.file_url) || [];
36+
// @ts-ignore
37+
textFiles = validFiles.filter(item => !isAudio(item.file_extension)).map(item => {
38+
return {
39+
file_name: item.file_name,
40+
file_extension: item.file_extension,
41+
file_data: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`,
42+
file_download_url: isExternalUrl(item.file_download_url) ? item.file_download_url : `${PUBLIC_SERVICE_URL}${item.file_download_url}?access_token=${$userStore?.token}`
43+
};
44+
});
45+
// @ts-ignore
46+
audioFiles = validFiles.filter(item => isAudio(item.file_extension)).map(item => {
47+
return {
48+
name: item.file_name,
49+
cover: AUDIO_ICON,
50+
artist: '',
51+
url: isExternalUrl(item.file_url) ? item.file_url : `${PUBLIC_SERVICE_URL}${item.file_url}?access_token=${$userStore?.token}`
52+
};
5153
});
5254
}
55+
56+
if (appendImage && message?.data) {
57+
textFiles = [...textFiles, {
58+
file_name: 'data',
59+
file_extension: '',
60+
file_data: message?.data
61+
}];
62+
}
5363
});
5464
5565
/** @param {number} idx */
@@ -71,7 +81,7 @@
7181
onDownload={idx => handleDownloadFile(idx)}
7282
/>
7383
<AudioGallery
74-
id={messageId}
84+
id={message?.message_id}
7585
containerClasses={galleryClasses}
7686
containerStyles={galleryStyles}
7787
audios={audioFiles}

src/lib/helpers/constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ export const LEARNER_ID = "01acc3e5-0af7-49e6-ad7a-a760bd12dc40";
3535
export const EVALUATOR_ID = "2cd4b805-7078-4405-87e9-2ec9aadf8a11";
3636
export const TRAINING_MODE = "training";
3737

38-
export const DEFAULT_KNOWLEDGE_COLLECTION = "BotSharp";
38+
export const DEFAULT_KNOWLEDGE_COLLECTION = "BotSharp";
39+
export const IMAGE_DATA_PREFIX = 'data:image';

src/lib/helpers/http.js

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import axios from 'axios';
2-
import { getUserStore, loaderStore } from '$lib/helpers/store.js';
2+
import { getUserStore, globalErrorStore, loaderStore } from '$lib/helpers/store.js';
33

44
// Add a request interceptor to attach authentication tokens or headers
55
axios.interceptors.request.use(
66
(config) => {
77
// Add your authentication logic here
8-
let user = getUserStore();
8+
const user = getUserStore();
99
if (!skipLoader(config)) {
1010
loaderStore.set(true);
1111
}
@@ -25,35 +25,48 @@ axios.interceptors.response.use(
2525
(response) => {
2626
// If the request was successful, return the response
2727
loaderStore.set(false);
28+
const user = getUserStore();
29+
30+
const isExpired = Date.now() / 1000 > user.expires;
31+
if (isExpired) {
32+
redirectToLogin();
33+
return Promise.reject('user token expired!');
34+
}
2835
return response;
2936
},
3037
(error) => {
3138
loaderStore.set(false);
32-
let user = getUserStore();
33-
34-
if (Date.now() / 1000 > user.expires) {
35-
error.response = {status: 401};
36-
}
37-
38-
// If the error status is 401, handle it here
39-
if (error.response && error.response.status === 401) {
40-
// Perform actions like redirecting to the login page or refreshing tokens
41-
// Example: redirect to the login page
42-
const curUrl = window.location.pathname + window.location.search;
43-
let loginUrl = 'login';
44-
if (curUrl) {
45-
loginUrl += `?redirect=${encodeURIComponent(curUrl)}`;
46-
}
47-
window.location.href = loginUrl;
39+
const user = getUserStore();
40+
console.log('error', error);
41+
const isExpired = Date.now() / 1000 > user.expires;
42+
if (isExpired || (error.response && error.response.status === 401)) {
43+
redirectToLogin();
44+
return Promise.reject(error);
45+
} else if (!skipGlobalError(error.config)) {
46+
globalErrorStore.set(true);
47+
setTimeout(() => {
48+
globalErrorStore.set(false);
49+
}, 2500);
4850
}
4951

5052
// Return the error to the calling function
5153
return Promise.reject(error);
5254
}
5355
);
5456

57+
58+
function redirectToLogin() {
59+
const curUrl = window.location.pathname + window.location.search;
60+
let loginUrl = 'login';
61+
if (curUrl) {
62+
loginUrl += `?redirect=${encodeURIComponent(curUrl)}`;
63+
}
64+
window.location.href = loginUrl;
65+
}
66+
5567
/** @param {import('axios').InternalAxiosRequestConfig<any>} config */
5668
function skipLoader(config) {
69+
/** @type {RegExp[]} */
5770
const postRegexes = [
5871
new RegExp('http(s*)://(.*?)/conversation/(.*?)/(.*?)', 'g'),
5972
new RegExp('http(s*)://(.*?)/agent', 'g'),
@@ -64,19 +77,22 @@ function skipLoader(config) {
6477
new RegExp('http(s*)://(.*?)/users', 'g')
6578
];
6679

80+
/** @type {RegExp[]} */
6781
const putRegexes = [
6882
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
6983
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
7084
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-tags', 'g'),
7185
new RegExp('http(s*)://(.*?)/users', 'g'),
7286
];
7387

88+
/** @type {RegExp[]} */
7489
const deleteRegexes = [
7590
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/delete-collection', 'g'),
7691
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data/(.*?)', 'g'),
7792
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data', 'g'),
7893
];
7994

95+
/** @type {RegExp[]} */
8096
const getRegexes = [
8197
new RegExp('http(s*)://(.*?)/setting/(.*?)', 'g'),
8298
new RegExp('http(s*)://(.*?)/user/me', 'g'),
@@ -112,6 +128,56 @@ function skipLoader(config) {
112128
return false;
113129
}
114130

131+
/** @param {import('axios').InternalAxiosRequestConfig<any>} config */
132+
function skipGlobalError(config) {
133+
/** @type {RegExp[]} */
134+
const postRegexes = [
135+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/page', 'g'),
136+
new RegExp('http(s*)://(.*?)/knowledge/(.*?)/search', 'g'),
137+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/create', 'g'),
138+
new RegExp('http(s*)://(.*?)/knowledge/document/(.*?)/page', 'g'),
139+
new RegExp('http(s*)://(.*?)/knowledge/vector/create-collection', 'g'),
140+
new RegExp('http(s*)://(.*?)/refresh-agents', 'g')
141+
];
142+
143+
/** @type {RegExp[]} */
144+
const putRegexes = [
145+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/update', 'g'),
146+
new RegExp('http(s*)://(.*?)/role', 'g'),
147+
new RegExp('http(s*)://(.*?)/user', 'g'),
148+
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-message', 'g'),
149+
new RegExp('http(s*)://(.*?)/conversation/(.*?)/update-tags', 'g')
150+
];
151+
152+
/** @type {RegExp[]} */
153+
const deleteRegexes = [
154+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/delete-collection', 'g'),
155+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data/(.*?)', 'g'),
156+
new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/data', 'g'),
157+
];
158+
159+
/** @type {RegExp[]} */
160+
const getRegexes = [];
161+
162+
if (config.method === 'post' && postRegexes.some(regex => regex.test(config.url || ''))) {
163+
return true;
164+
}
165+
166+
if (config.method === 'put' && putRegexes.some(regex => regex.test(config.url || ''))) {
167+
return true;
168+
}
169+
170+
if (config.method === 'delete' && deleteRegexes.some(regex => regex.test(config.url || ''))) {
171+
return true;
172+
}
173+
174+
if (config.method === 'get' && getRegexes.some(regex => regex.test(config.url || ''))) {
175+
return true;
176+
}
177+
178+
return false;
179+
}
180+
115181
/**
116182
* @param {String} url
117183
* @param {Object} args
@@ -120,6 +186,7 @@ function skipLoader(config) {
120186
export function replaceUrl(url, args) {
121187
const keys = Object.keys(args);
122188
keys.forEach(key => {
189+
// @ts-ignore
123190
url = url.replace("{" + key + "}", args[key]);
124191
});
125192
return url;

src/lib/helpers/store.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ const createLoaderStore = () => {
9090
export const loaderStore = createLoaderStore();
9191

9292

93+
const createGlobalErrorStore = () => {
94+
const { subscribe, set } = writable(false);
95+
return {
96+
subscribe,
97+
set
98+
};
99+
}
100+
101+
export const globalErrorStore = createGlobalErrorStore();
102+
103+
93104
const createConversationUserStateStore = () => {
94105
return {
95106
resetAll: () => {

src/lib/helpers/types/conversationTypes.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
* @typedef {Object} StateSearchQuery
4242
* @property {string} query - The search query.
4343
* @property {number?} [keyLimit] - The key limit.
44-
* @property {boolean?} [preLoad] - Whether it is preloading or not.
44+
* @property {number?} [convLimit] - The conversation limit.
45+
* @property {boolean?} [preload] - Whether it is preloading or not.
4546
*/
4647

4748

src/routes/+layout.svelte

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,49 @@
22
import { addMessages, init, getLocaleFromNavigator } from 'svelte-i18n';
33
import en from '$lib/langs/en.json';
44
import '$lib/helpers/http';
5-
import { onMount } from 'svelte';
6-
import { loaderStore } from '$lib/helpers/store';
5+
import { onDestroy, onMount } from 'svelte';
6+
import { globalErrorStore, loaderStore } from '$lib/helpers/store';
77
import Loader from '$lib/common/Loader.svelte';
8+
import LoadingToComplete from '$lib/common/LoadingToComplete.svelte';
89
910
addMessages('en', en);
1011
1112
init({
1213
fallbackLocale: 'en',
1314
initialLocale: getLocaleFromNavigator()
1415
});
15-
/**
16-
* @type {boolean}
17-
*/
18-
let isLoading;
16+
17+
/** @type {boolean} */
18+
let isLoading = false;
19+
let hasError = false;
20+
21+
/** @type {any} */
22+
let loaderUnsubscriber;
23+
/** @type {any} */
24+
let errorUnsubscriber;
25+
1926
onMount(() => {
2027
window?.speechSynthesis?.cancel();
21-
const subscribe = loaderStore.subscribe(value => {
28+
loaderUnsubscriber = loaderStore.subscribe(value => {
2229
isLoading = value;
2330
});
31+
32+
errorUnsubscriber = globalErrorStore.subscribe(value => {
33+
hasError = value;
34+
});
2435
})
36+
37+
onDestroy(() => {
38+
loaderUnsubscriber?.();
39+
errorUnsubscriber?.();
40+
});
2541
</script>
2642
{#if isLoading}
2743
<Loader size={50}/>
2844
{/if}
45+
46+
<LoadingToComplete isError={hasError} />
47+
2948
<slot />
3049
3150
<style lang="scss">

src/routes/chat/[agentId]/[conversationId]/chat-box.svelte

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
PUBLIC_LIVECHAT_ENABLE_TRAINING,
4545
PUBLIC_DEBUG_MODE
4646
} from '$env/static/public';
47-
import { BOT_SENDERS, LEARNER_ID, TRAINING_MODE, USER_SENDERS, ADMIN_ROLES } from '$lib/helpers/constants';
47+
import { BOT_SENDERS, LEARNER_ID, TRAINING_MODE, USER_SENDERS, ADMIN_ROLES, IMAGE_DATA_PREFIX } from '$lib/helpers/constants';
4848
import { signalr } from '$lib/services/signalr-service.js';
4949
import { llmRealtime } from '$lib/services/llm-realtime-service.js';
5050
import { newConversation } from '$lib/services/conversation-service';
@@ -1658,9 +1658,10 @@
16581658
{#if !!message.post_action_disclaimer}
16591659
<RcDisclaimer content={message.post_action_disclaimer} />
16601660
{/if}
1661-
{#if !!message.is_chat_message || !!message.has_message_files}
1661+
{#if !!message.is_chat_message || !!message.has_message_files || message?.data?.startsWith(IMAGE_DATA_PREFIX)}
16621662
<MessageFileGallery
1663-
messageId={message?.message_id}
1663+
message={message}
1664+
appendImage
16641665
galleryStyles={'justify-content: flex-end;'}
16651666
fetchFiles={() => getConversationFiles(params.conversationId, message.message_id, FileSourceType.User)}
16661667
/>
@@ -1756,9 +1757,10 @@
17561757
</div>
17571758
</div>
17581759
{/if}
1759-
{#if !!message.is_chat_message || !!message.has_message_files}
1760+
{#if !!message.is_chat_message || !!message.has_message_files || message?.data?.startsWith(IMAGE_DATA_PREFIX)}
17601761
<MessageFileGallery
1761-
messageId={message?.message_id}
1762+
message={message}
1763+
appendImage
17621764
galleryStyles={'justify-content: flex-start;'}
17631765
fetchFiles={() => getConversationFiles(params.conversationId, message.message_id, FileSourceType.Bot)}
17641766
/>

0 commit comments

Comments
 (0)