tuikit-atomicx-vue3 3.3.1 → 3.3.2-beta.1
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/dist/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.js +1 -4
- package/dist/components/ChatSetting/GroupChatSetting/GroupChatSetting.js +1 -2
- package/dist/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.js +1 -2
- package/dist/components/ContactList/ContactInfo/GroupInfo/GroupInfo.js +1 -2
- package/dist/components/ConversationList/ConversationCreate/ConversationCreate.js +1 -2
- package/dist/components/ConversationList/ConversationSearch/ConversationSearch.js +0 -1
- package/dist/components/LiveCoreView/PlayerControl/AudioControl.js +251 -0
- package/dist/components/LiveCoreView/PlayerControl/AudioControl.vue.d.ts +38 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControl.js +271 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControl.vue.d.ts +2 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.d.ts +27 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.js +407 -0
- package/dist/components/LiveCoreView/PlayerControl/index.d.ts +3 -0
- package/dist/components/LiveCoreView/PlayerControl/index.js +8 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.d.ts +85 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.js +129 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.d.ts +75 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.js +120 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.d.ts +120 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.js +311 -0
- package/dist/components/LiveCoreView/i18n/en-US/index.d.ts +9 -0
- package/dist/components/LiveCoreView/i18n/en-US/index.js +10 -1
- package/dist/components/LiveCoreView/i18n/zh-CN/index.d.ts +9 -0
- package/dist/components/LiveCoreView/i18n/zh-CN/index.js +10 -1
- package/dist/components/LiveCoreView/index.js +23 -3
- package/dist/styles/index.css +302 -30
- package/package.json +2 -2
- package/src/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.vue +0 -3
- package/src/components/ChatSetting/GroupChatSetting/GroupChatSetting.vue +0 -1
- package/src/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.vue +0 -1
- package/src/components/ContactList/ContactInfo/GroupInfo/GroupInfo.vue +0 -1
- package/src/components/ConversationList/ConversationCreate/ConversationCreate.vue +0 -1
- package/src/components/ConversationList/ConversationSearch/ConversationSearch.vue +0 -1
- package/src/components/LiveCoreView/PlayerControl/AudioControl.vue +456 -0
- package/src/components/LiveCoreView/PlayerControl/PlayerControl.module.scss +52 -0
- package/src/components/LiveCoreView/PlayerControl/PlayerControl.vue +429 -0
- package/src/components/LiveCoreView/PlayerControl/PlayerControlState.ts +599 -0
- package/src/components/LiveCoreView/PlayerControl/index.ts +3 -0
- package/src/components/LiveCoreView/PlayerControl/utils/deviceDetection.ts +234 -0
- package/src/components/LiveCoreView/PlayerControl/utils/domHelpers.ts +145 -0
- package/src/components/LiveCoreView/PlayerControl/utils/fullscreenManager.ts +417 -0
- package/src/components/LiveCoreView/i18n/en-US/index.ts +9 -0
- package/src/components/LiveCoreView/i18n/zh-CN/index.ts +9 -0
- package/src/components/LiveCoreView/index.vue +13 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device detection utility module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Device type enum
|
|
6
|
+
export enum DeviceType {
|
|
7
|
+
DESKTOP = 'desktop',
|
|
8
|
+
MOBILE = 'mobile',
|
|
9
|
+
IOS = 'ios',
|
|
10
|
+
ANDROID = 'android',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Device capabilities interface
|
|
14
|
+
export interface DeviceCapabilities {
|
|
15
|
+
supportsFullscreen: boolean;
|
|
16
|
+
supportsOrientation: boolean;
|
|
17
|
+
supportsPictureInPicture: boolean;
|
|
18
|
+
deviceType: DeviceType;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Detect if it's a mobile device
|
|
23
|
+
*/
|
|
24
|
+
export const isMobileDevice = (): boolean => {
|
|
25
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Detect if it's an iOS device
|
|
30
|
+
*/
|
|
31
|
+
export const isIOSDevice = (): boolean => {
|
|
32
|
+
return /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Detect if it's an Android device
|
|
37
|
+
*/
|
|
38
|
+
export const isAndroidDevice = (): boolean => {
|
|
39
|
+
return /Android/i.test(navigator.userAgent);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Detect if it's a Safari browser
|
|
44
|
+
*/
|
|
45
|
+
export const isSafariBrowser = (): boolean => {
|
|
46
|
+
// Safari has several unique features
|
|
47
|
+
const isSafari =
|
|
48
|
+
// Check the vendor feature
|
|
49
|
+
navigator.vendor && navigator.vendor.includes('Apple') &&
|
|
50
|
+
// Check specific apis or behaviors unique to Safari
|
|
51
|
+
!navigator.userAgent.includes('CriOS') && // Exclude Chrome iOS
|
|
52
|
+
!navigator.userAgent.includes('FxiOS') && // Exclude Firefox iOS
|
|
53
|
+
// Additional feature checks
|
|
54
|
+
(typeof safari !== 'undefined' ||
|
|
55
|
+
!('netscape' in window) || // Firefox has netscape objects
|
|
56
|
+
document.documentMode === undefined); // Exclude IE
|
|
57
|
+
|
|
58
|
+
return !!isSafari;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Detect if it's a Firefox browser
|
|
63
|
+
*/
|
|
64
|
+
export const isFirefoxBrowser = (): boolean => {
|
|
65
|
+
// The unique features of Firefox
|
|
66
|
+
return typeof InstallTrigger !== 'undefined' ||
|
|
67
|
+
navigator.userAgent.includes('Firefox') ||
|
|
68
|
+
navigator.userAgent.includes('Gecko/');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get device type
|
|
73
|
+
*/
|
|
74
|
+
export const getDeviceType = (): DeviceType => {
|
|
75
|
+
if (isIOSDevice()) return DeviceType.IOS;
|
|
76
|
+
if (isAndroidDevice()) return DeviceType.ANDROID;
|
|
77
|
+
if (isMobileDevice()) return DeviceType.MOBILE;
|
|
78
|
+
return DeviceType.DESKTOP;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Detect if device supports screen orientation control
|
|
83
|
+
*/
|
|
84
|
+
export const isOrientationSupported = (): boolean => {
|
|
85
|
+
return !!(screen.orientation && (screen.orientation as any).lock);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get current screen orientation
|
|
90
|
+
*/
|
|
91
|
+
export const getCurrentOrientation = (): 'portrait' | 'landscape' | 'unknown' => {
|
|
92
|
+
// First try the standard Screen Orientation API
|
|
93
|
+
if (screen.orientation) {
|
|
94
|
+
const angle = screen.orientation.angle;
|
|
95
|
+
const type = screen.orientation.type;
|
|
96
|
+
|
|
97
|
+
// Check orientation type first (more reliable)
|
|
98
|
+
if (type.includes('portrait')) {
|
|
99
|
+
return 'portrait';
|
|
100
|
+
} else if (type.includes('landscape')) {
|
|
101
|
+
return 'landscape';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Fallback to angle-based detection
|
|
105
|
+
if (angle === 0 || angle === 180) {
|
|
106
|
+
return 'portrait';
|
|
107
|
+
} else if (angle === 90 || angle === 270) {
|
|
108
|
+
return 'landscape';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fallback to window dimensions
|
|
113
|
+
if (window.innerHeight > window.innerWidth) {
|
|
114
|
+
return 'portrait';
|
|
115
|
+
} else if (window.innerWidth > window.innerHeight) {
|
|
116
|
+
return 'landscape';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return 'unknown';
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if device is currently in portrait orientation
|
|
124
|
+
*/
|
|
125
|
+
export const isCurrentlyPortrait = (): boolean => {
|
|
126
|
+
return getCurrentOrientation() === 'portrait';
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if device is currently in landscape orientation
|
|
131
|
+
*/
|
|
132
|
+
export const isCurrentlyLandscape = (): boolean => {
|
|
133
|
+
return getCurrentOrientation() === 'landscape';
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Determine if device should rotate to landscape for fullscreen
|
|
138
|
+
* @param deviceType - The type of device
|
|
139
|
+
* @param isLandscapeStream - Whether the stream is landscape oriented
|
|
140
|
+
* @returns Whether the device should rotate to landscape
|
|
141
|
+
*/
|
|
142
|
+
export const shouldRotateToLandscapeForFullscreen = (
|
|
143
|
+
deviceType: DeviceType,
|
|
144
|
+
isLandscapeStream: boolean
|
|
145
|
+
): boolean => {
|
|
146
|
+
// Only mobile devices can/should rotate
|
|
147
|
+
if (deviceType === DeviceType.DESKTOP) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Only rotate for landscape streams
|
|
152
|
+
if (!isLandscapeStream) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Only rotate if currently in portrait
|
|
157
|
+
if (!isCurrentlyPortrait()) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Determine if device had landscape rotation that should be undone
|
|
166
|
+
* @param deviceType - The type of device
|
|
167
|
+
* @param isLandscapeStream - Whether the stream is landscape oriented
|
|
168
|
+
* @returns Whether the device had landscape rotation that should be undone
|
|
169
|
+
*/
|
|
170
|
+
export const hadLandscapeRotationToUndo = (
|
|
171
|
+
deviceType: DeviceType,
|
|
172
|
+
isLandscapeStream: boolean
|
|
173
|
+
): boolean => {
|
|
174
|
+
// Only mobile devices can have had rotation
|
|
175
|
+
if (deviceType === DeviceType.DESKTOP) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Only relevant for landscape streams
|
|
180
|
+
if (!isLandscapeStream) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Only if currently in landscape (meaning we probably rotated from portrait)
|
|
185
|
+
if (!isCurrentlyLandscape()) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return true;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Detect if fullscreen API is supported
|
|
194
|
+
*/
|
|
195
|
+
export const isFullscreenSupported = (element: HTMLElement): boolean => {
|
|
196
|
+
return !!(
|
|
197
|
+
element.requestFullscreen ||
|
|
198
|
+
(element as any).webkitRequestFullscreen ||
|
|
199
|
+
(element as any).mozRequestFullScreen ||
|
|
200
|
+
(element as any).msRequestFullscreen
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Detect if exit fullscreen API is supported
|
|
206
|
+
*/
|
|
207
|
+
export const isExitFullscreenSupported = (): boolean => {
|
|
208
|
+
return !!(
|
|
209
|
+
document.exitFullscreen ||
|
|
210
|
+
(document as any).webkitExitFullscreen ||
|
|
211
|
+
(document as any).mozCancelFullScreen ||
|
|
212
|
+
(document as any).msExitFullscreen
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Detect if picture-in-picture is supported
|
|
218
|
+
*/
|
|
219
|
+
export const isPictureInPictureSupported = (video?: HTMLVideoElement): boolean => {
|
|
220
|
+
if (!video) return false;
|
|
221
|
+
return typeof video.requestPictureInPicture === 'function' && typeof document.exitPictureInPicture === 'function';
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get complete device capabilities information
|
|
226
|
+
*/
|
|
227
|
+
export const getDeviceCapabilities = (element?: HTMLElement): DeviceCapabilities => {
|
|
228
|
+
return {
|
|
229
|
+
supportsFullscreen: element ? isFullscreenSupported(element) : false,
|
|
230
|
+
supportsOrientation: isOrientationSupported(),
|
|
231
|
+
supportsPictureInPicture: isPictureInPictureSupported(),
|
|
232
|
+
deviceType: getDeviceType(),
|
|
233
|
+
};
|
|
234
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM operation helper utility module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// DOM selector constants
|
|
6
|
+
export const DOM_SELECTORS = {
|
|
7
|
+
LIVE_CORE_VIEW_CONTAINER: '.live-core-view-container',
|
|
8
|
+
LIVE_CORE_VIEW: '.live-core-view',
|
|
9
|
+
VIDEO_ELEMENT: '#atomicx-live-stream-content video',
|
|
10
|
+
TCPLAYER_ELEMENT: '.tcplayer',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* DOM element getter
|
|
15
|
+
*/
|
|
16
|
+
export class DOMElementGetter {
|
|
17
|
+
/**
|
|
18
|
+
* Get live-core-view-container element
|
|
19
|
+
*/
|
|
20
|
+
static getLiveCoreViewContainer(): HTMLElement | null {
|
|
21
|
+
return document.querySelector(DOM_SELECTORS.LIVE_CORE_VIEW_CONTAINER);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get live-core-view element
|
|
26
|
+
*/
|
|
27
|
+
static getLiveCoreView(): HTMLElement | null {
|
|
28
|
+
return document.querySelector(DOM_SELECTORS.LIVE_CORE_VIEW);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get video element
|
|
33
|
+
*/
|
|
34
|
+
static getVideoElement(): HTMLVideoElement | null {
|
|
35
|
+
return document.querySelector(DOM_SELECTORS.VIDEO_ELEMENT);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get all required DOM elements
|
|
40
|
+
*/
|
|
41
|
+
static getAllElements(): {
|
|
42
|
+
container: HTMLElement | null;
|
|
43
|
+
view: HTMLElement | null;
|
|
44
|
+
video: HTMLVideoElement | null;
|
|
45
|
+
} {
|
|
46
|
+
return {
|
|
47
|
+
container: DOMElementGetter.getLiveCoreViewContainer(),
|
|
48
|
+
view: DOMElementGetter.getLiveCoreView(),
|
|
49
|
+
video: DOMElementGetter.getVideoElement(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if tcplayer element exists in live-core-view-container
|
|
55
|
+
*/
|
|
56
|
+
static hasTcPlayerElement(): boolean {
|
|
57
|
+
const container = DOMElementGetter.getLiveCoreViewContainer();
|
|
58
|
+
if (!container) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return container.querySelector(DOM_SELECTORS.TCPLAYER_ELEMENT) !== null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get tcplayer element from live-core-view-container
|
|
66
|
+
*/
|
|
67
|
+
static getTcPlayerElement(): HTMLElement | null {
|
|
68
|
+
const container = DOMElementGetter.getLiveCoreViewContainer();
|
|
69
|
+
if (!container) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return container.querySelector(DOM_SELECTORS.TCPLAYER_ELEMENT);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validate if elements exist
|
|
77
|
+
*/
|
|
78
|
+
static validateElements(elements: { container?: HTMLElement | null; view?: HTMLElement | null; video?: HTMLVideoElement | null }): {
|
|
79
|
+
isValid: boolean;
|
|
80
|
+
missingElements: string[];
|
|
81
|
+
} {
|
|
82
|
+
const missingElements: string[] = [];
|
|
83
|
+
|
|
84
|
+
if (elements.container === null) {
|
|
85
|
+
missingElements.push('live-core-view-container');
|
|
86
|
+
}
|
|
87
|
+
if (elements.view === null) {
|
|
88
|
+
missingElements.push('live-core-view');
|
|
89
|
+
}
|
|
90
|
+
if (elements.video === null) {
|
|
91
|
+
missingElements.push('video');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
isValid: missingElements.length === 0,
|
|
96
|
+
missingElements,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Event listener manager
|
|
103
|
+
*/
|
|
104
|
+
export class EventListenerManager {
|
|
105
|
+
private listeners: Map<string, { element: EventTarget; event: string; handler: EventListener }> = new Map();
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Add event listener
|
|
109
|
+
*/
|
|
110
|
+
addListener(id: string, element: EventTarget, event: string, handler: EventListener): void {
|
|
111
|
+
// Remove possible existing old listener
|
|
112
|
+
this.removeListener(id);
|
|
113
|
+
|
|
114
|
+
// Add new listener
|
|
115
|
+
element.addEventListener(event, handler);
|
|
116
|
+
this.listeners.set(id, { element, event, handler });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Remove specified event listener
|
|
121
|
+
*/
|
|
122
|
+
removeListener(id: string): void {
|
|
123
|
+
const listener = this.listeners.get(id);
|
|
124
|
+
if (listener) {
|
|
125
|
+
listener.element.removeEventListener(listener.event, listener.handler);
|
|
126
|
+
this.listeners.delete(id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Remove all event listeners
|
|
132
|
+
*/
|
|
133
|
+
removeAllListeners(): void {
|
|
134
|
+
for (const [id] of this.listeners) {
|
|
135
|
+
this.removeListener(id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get current listener count
|
|
141
|
+
*/
|
|
142
|
+
getListenerCount(): number {
|
|
143
|
+
return this.listeners.size;
|
|
144
|
+
}
|
|
145
|
+
}
|