@webitel/ui-chats 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/ui/media-viewer/media-viewer.vue +69 -0
- package/src/ui/messaging/modules/message/components/details/chat-message-player.vue +25 -6
- package/src/ui/messaging/modules/message/composables/useChatMessageFile.ts +17 -9
- package/src/ui/the-chat-container.vue +40 -24
- package/types/ui/media-viewer/media-viewer.vue.d.ts +11 -0
- package/types/ui/messaging/modules/message/composables/useChatMessageFile.d.ts +3 -10
package/package.json
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<aside
|
|
3
|
+
v-if="media"
|
|
4
|
+
class="media-viewer"
|
|
5
|
+
@click="close"
|
|
6
|
+
>
|
|
7
|
+
<div class="media-viewer__shadow" />
|
|
8
|
+
<div class="media-viewer__content-wrapper">
|
|
9
|
+
<img
|
|
10
|
+
class="media-viewer__content-img"
|
|
11
|
+
:src="media.url"
|
|
12
|
+
:alt="media.name"
|
|
13
|
+
>
|
|
14
|
+
</div>
|
|
15
|
+
</aside>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import type { ChatMessageFile } from '../messaging/types/ChatMessage.types';
|
|
20
|
+
|
|
21
|
+
defineProps<{
|
|
22
|
+
media: ChatMessageFile | null;
|
|
23
|
+
}>();
|
|
24
|
+
|
|
25
|
+
const emit = defineEmits<{
|
|
26
|
+
close: [];
|
|
27
|
+
}>();
|
|
28
|
+
|
|
29
|
+
function close() {
|
|
30
|
+
emit('close');
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
.media-viewer {
|
|
36
|
+
position: fixed;
|
|
37
|
+
top: 0;
|
|
38
|
+
right: 0;
|
|
39
|
+
bottom: 0;
|
|
40
|
+
left: 0;
|
|
41
|
+
z-index: var(--popup-wrapper-z-index);
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.media-viewer__shadow {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: 0;
|
|
50
|
+
right: 0;
|
|
51
|
+
bottom: 0;
|
|
52
|
+
left: 0;
|
|
53
|
+
background: var(--wt-popup-shadow-color);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.media-viewer__content-wrapper {
|
|
57
|
+
position: relative;
|
|
58
|
+
z-index: 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.media-viewer__content-img {
|
|
62
|
+
/* like on web telegram preview */
|
|
63
|
+
display: block;
|
|
64
|
+
max-width: 100vw;
|
|
65
|
+
max-height: 90vh;
|
|
66
|
+
object-fit: contain;
|
|
67
|
+
margin: auto;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
class="chat-message-player"
|
|
4
4
|
@click="emit('open', props.file)"
|
|
5
5
|
>
|
|
6
|
+
<wt-vidstack-player
|
|
7
|
+
v-if="isVideo"
|
|
8
|
+
static
|
|
9
|
+
hide-expand
|
|
10
|
+
stretch
|
|
11
|
+
:size="ComponentSize.SM"
|
|
12
|
+
:src="mediaSrc"
|
|
13
|
+
/>
|
|
6
14
|
<wt-player
|
|
7
|
-
|
|
8
|
-
:
|
|
15
|
+
v-else
|
|
16
|
+
:src="mediaSrc"
|
|
9
17
|
:autoplay="false"
|
|
10
|
-
:
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
:closable="false"
|
|
19
|
+
hide-volume-slider
|
|
20
|
+
class="chat-message-player__player"
|
|
13
21
|
@initialized="handlePlayerInitialize"
|
|
14
22
|
/>
|
|
15
23
|
</div>
|
|
@@ -17,6 +25,8 @@
|
|
|
17
25
|
|
|
18
26
|
<script setup lang="ts">
|
|
19
27
|
import { computed, defineEmits, defineProps } from 'vue';
|
|
28
|
+
import { WtVidstackPlayer, WtPlayer } from '@webitel/ui-sdk/components';
|
|
29
|
+
import { ComponentSize } from '@webitel/ui-sdk/enums';
|
|
20
30
|
|
|
21
31
|
import type { ChatMessageFile } from '../../../../types/ChatMessage.types';
|
|
22
32
|
|
|
@@ -24,6 +34,7 @@ const props = defineProps<{
|
|
|
24
34
|
file: ChatMessageFile;
|
|
25
35
|
type: string;
|
|
26
36
|
}>();
|
|
37
|
+
|
|
27
38
|
const emit = defineEmits<{
|
|
28
39
|
open: [
|
|
29
40
|
ChatMessageFile,
|
|
@@ -33,7 +44,15 @@ const emit = defineEmits<{
|
|
|
33
44
|
];
|
|
34
45
|
}>();
|
|
35
46
|
|
|
36
|
-
const
|
|
47
|
+
const isVideo = computed(() => {
|
|
48
|
+
return mediaSrc.value?.type?.includes('video');
|
|
49
|
+
});
|
|
50
|
+
const mediaSrc = computed(() => {
|
|
51
|
+
return {
|
|
52
|
+
src: props.file.streamUrl || props.file.url,
|
|
53
|
+
type: props.type,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
37
56
|
|
|
38
57
|
function handlePlayerInitialize(player) {
|
|
39
58
|
emit('initialized', player);
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
import { computed, type Ref, toRef } from
|
|
1
|
+
import { computed, type Ref, toRef } from 'vue';
|
|
2
2
|
|
|
3
|
-
import type { ChatMessageFile } from
|
|
3
|
+
import type { ChatMessageFile } from '../../../types/ChatMessage.types';
|
|
4
4
|
|
|
5
5
|
export function useChatMessageFile(
|
|
6
6
|
file: ChatMessageFile | Ref<ChatMessageFile>,
|
|
7
7
|
) {
|
|
8
8
|
const fileRef = toRef(file);
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const fileType = computed(() => {
|
|
11
11
|
return fileRef.value?.mime;
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
+
const fileSrc = computed(() => {
|
|
15
|
+
return fileRef.value?.streamUrl || fileRef.value?.url;
|
|
16
|
+
});
|
|
17
|
+
|
|
14
18
|
const image = computed(() => {
|
|
15
|
-
const isImage =
|
|
16
|
-
const isHEIC =
|
|
19
|
+
const isImage = fileType.value?.includes('image');
|
|
20
|
+
const isHEIC = fileType.value?.includes('heic');
|
|
17
21
|
|
|
18
22
|
if (isHEIC) return null;
|
|
19
23
|
|
|
20
24
|
return isImage && fileRef.value; //https://webitel.atlassian.net/browse/WTEL-6268
|
|
21
25
|
});
|
|
22
|
-
|
|
23
26
|
const media = computed(() => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
return (
|
|
28
|
+
fileRef.value &&
|
|
29
|
+
(isMediaType(fileType.value) || isMediaType(fileSrc.value))
|
|
30
|
+
);
|
|
27
31
|
});
|
|
28
32
|
|
|
33
|
+
const isMediaType = (value: string) => {
|
|
34
|
+
return !!(value?.includes('audio') || value?.includes('video'));
|
|
35
|
+
};
|
|
36
|
+
|
|
29
37
|
const document = computed(() => {
|
|
30
38
|
return !media.value && !image.value && fileRef.value;
|
|
31
39
|
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<section class="the-chat-container">
|
|
3
|
+
<media-viewer
|
|
4
|
+
:media="mediaView"
|
|
5
|
+
@close="closeMedia"
|
|
6
|
+
/>
|
|
3
7
|
<slot name="main">
|
|
4
8
|
<dropzone
|
|
5
9
|
v-if="!isDropzoneDisabled && isDropzoneVisible"
|
|
@@ -45,23 +49,26 @@
|
|
|
45
49
|
</template>
|
|
46
50
|
|
|
47
51
|
<script setup lang="ts">
|
|
48
|
-
import { ComponentSize } from
|
|
49
|
-
import { computed, provide, ref } from
|
|
52
|
+
import { ComponentSize } from '@webitel/ui-sdk/enums';
|
|
53
|
+
import { computed, provide, ref } from 'vue';
|
|
50
54
|
|
|
51
|
-
import ChatFooterWrapper from
|
|
52
|
-
import ChatInputActionsBar from
|
|
53
|
-
import ChatTextField from
|
|
55
|
+
import ChatFooterWrapper from './chat-footer/components/chat-footer-wrapper.vue';
|
|
56
|
+
import ChatInputActionsBar from './chat-footer/modules/user-input/components/chat-input-actions-bar.vue';
|
|
57
|
+
import ChatTextField from './chat-footer/modules/user-input/components/chat-text-field.vue';
|
|
54
58
|
import {
|
|
55
59
|
ChatAction,
|
|
56
60
|
type SharedActionSlots,
|
|
57
|
-
} from
|
|
58
|
-
import
|
|
59
|
-
import
|
|
60
|
-
import
|
|
61
|
-
import {
|
|
62
|
-
import type {
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
} from './chat-footer/modules/user-input/enums/ChatAction.enum';
|
|
62
|
+
import MediaViewer from './media-viewer/media-viewer.vue';
|
|
63
|
+
import Dropzone from './messaging/components/dropzone.vue';
|
|
64
|
+
import MessagesContainer from './messaging/components/the-messages-container.vue';
|
|
65
|
+
import { useDropzoneHandlers } from './messaging/composables/useDropzoneHandlers';
|
|
66
|
+
import type {
|
|
67
|
+
ChatMessageFile,
|
|
68
|
+
ChatMessageType,
|
|
69
|
+
} from './messaging/types/ChatMessage.types';
|
|
70
|
+
import { createUiChatsEmitter } from './utils/emitter';
|
|
71
|
+
import type { ResultCallbacks } from './utils/ResultCallbacks.types';
|
|
65
72
|
|
|
66
73
|
const props = withDefaults(
|
|
67
74
|
defineProps<{
|
|
@@ -71,7 +78,7 @@ const props = withDefaults(
|
|
|
71
78
|
canLoadNextMessages?: boolean; // 'next'
|
|
72
79
|
isNextMessagesLoading?: boolean;
|
|
73
80
|
withoutAvatars?: boolean;
|
|
74
|
-
|
|
81
|
+
readonly?: boolean; // hide chat footer with textarea and action-buttons
|
|
75
82
|
}>(),
|
|
76
83
|
{
|
|
77
84
|
size: ComponentSize.MD,
|
|
@@ -79,7 +86,7 @@ const props = withDefaults(
|
|
|
79
86
|
chatActions: () => [],
|
|
80
87
|
canLoadNextMessages: false,
|
|
81
88
|
isNextMessagesLoading: false,
|
|
82
|
-
|
|
89
|
+
readonly: false,
|
|
83
90
|
},
|
|
84
91
|
);
|
|
85
92
|
|
|
@@ -95,7 +102,6 @@ const emit = defineEmits<{
|
|
|
95
102
|
options: ResultCallbacks,
|
|
96
103
|
): void;
|
|
97
104
|
(e: typeof ChatAction.LoadNextMessages): void;
|
|
98
|
-
(e: typeof MessageAction.ClickOnImage, message: ChatMessageType): void;
|
|
99
105
|
}>();
|
|
100
106
|
|
|
101
107
|
const slots = defineSlots<
|
|
@@ -107,20 +113,30 @@ const slots = defineSlots<
|
|
|
107
113
|
|
|
108
114
|
const uiChatsEmitter = createUiChatsEmitter();
|
|
109
115
|
|
|
110
|
-
provide(
|
|
111
|
-
provide(
|
|
116
|
+
provide('size', props.size);
|
|
117
|
+
provide('uiChatsEmitter', uiChatsEmitter);
|
|
112
118
|
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
const mediaView = ref<ChatMessageFile | null>(null);
|
|
120
|
+
|
|
121
|
+
uiChatsEmitter?.on('clickChatMessageImage', (message) => {
|
|
122
|
+
openMedia(message);
|
|
115
123
|
});
|
|
116
124
|
const { isDropzoneVisible, handleDragLeave } = useDropzoneHandlers();
|
|
117
125
|
|
|
118
|
-
|
|
126
|
+
function openMedia(message: ChatMessageType) {
|
|
127
|
+
mediaView.value = message.file ?? null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function closeMedia() {
|
|
131
|
+
mediaView.value = null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const draft = ref<string>('');
|
|
119
135
|
|
|
120
136
|
const slottedChatActions = computed(() => {
|
|
121
137
|
return Object.keys(slots)
|
|
122
|
-
.filter((key) => key.startsWith(
|
|
123
|
-
.map((key) => key.replace(
|
|
138
|
+
.filter((key) => key.startsWith('action:'))
|
|
139
|
+
.map((key) => key.replace('action:', ''));
|
|
124
140
|
});
|
|
125
141
|
const isDropzoneDisabled = computed(
|
|
126
142
|
() => !props.chatActions.includes(ChatAction.AttachFiles),
|
|
@@ -128,7 +144,7 @@ const isDropzoneDisabled = computed(
|
|
|
128
144
|
|
|
129
145
|
function sendMessage() {
|
|
130
146
|
emit(`action:${ChatAction.SendMessage}`, draft.value, {
|
|
131
|
-
onSuccess: () => (draft.value =
|
|
147
|
+
onSuccess: () => (draft.value = ''),
|
|
132
148
|
});
|
|
133
149
|
}
|
|
134
150
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ChatMessageFile } from '../messaging/types/ChatMessage.types';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
media: ChatMessageFile | null;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
+
close: () => any;
|
|
7
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
8
|
+
onClose?: () => any;
|
|
9
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
|
+
declare const _default: typeof __VLS_export;
|
|
11
|
+
export default _default;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import { type Ref } from
|
|
2
|
-
import type { ChatMessageFile } from
|
|
1
|
+
import { type Ref } from 'vue';
|
|
2
|
+
import type { ChatMessageFile } from '../../../types/ChatMessage.types';
|
|
3
3
|
export declare function useChatMessageFile(file: ChatMessageFile | Ref<ChatMessageFile>): {
|
|
4
4
|
image: import("vue").ComputedRef<ChatMessageFile>;
|
|
5
|
-
media: import("vue").ComputedRef<
|
|
6
|
-
id?: string;
|
|
7
|
-
name?: string;
|
|
8
|
-
size?: string;
|
|
9
|
-
mime?: string;
|
|
10
|
-
url?: string;
|
|
11
|
-
streamUrl?: string;
|
|
12
|
-
}>;
|
|
5
|
+
media: import("vue").ComputedRef<boolean>;
|
|
13
6
|
document: import("vue").ComputedRef<ChatMessageFile | {
|
|
14
7
|
id?: string;
|
|
15
8
|
name?: string;
|