@sendbird/uikit-utils 1.0.0-rc.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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/lib/commonjs/hooks/index.js +88 -0
  4. package/lib/commonjs/hooks/index.js.map +1 -0
  5. package/lib/commonjs/index.js +223 -0
  6. package/lib/commonjs/index.js.map +1 -0
  7. package/lib/commonjs/sendbird/attrs.js +57 -0
  8. package/lib/commonjs/sendbird/attrs.js.map +1 -0
  9. package/lib/commonjs/sendbird/channel.js +53 -0
  10. package/lib/commonjs/sendbird/channel.js.map +1 -0
  11. package/lib/commonjs/sendbird/message.js +111 -0
  12. package/lib/commonjs/sendbird/message.js.map +1 -0
  13. package/lib/commonjs/shared/arrayToMap.js +54 -0
  14. package/lib/commonjs/shared/arrayToMap.js.map +1 -0
  15. package/lib/commonjs/shared/conditionChaining.js +15 -0
  16. package/lib/commonjs/shared/conditionChaining.js.map +1 -0
  17. package/lib/commonjs/shared/logger.js +98 -0
  18. package/lib/commonjs/shared/logger.js.map +1 -0
  19. package/lib/commonjs/shared/pubsub.js +54 -0
  20. package/lib/commonjs/shared/pubsub.js.map +1 -0
  21. package/lib/commonjs/shared/regex.js +102 -0
  22. package/lib/commonjs/shared/regex.js.map +1 -0
  23. package/lib/commonjs/types.js +6 -0
  24. package/lib/commonjs/types.js.map +1 -0
  25. package/lib/commonjs/ui-format/common.js +106 -0
  26. package/lib/commonjs/ui-format/common.js.map +1 -0
  27. package/lib/commonjs/ui-format/groupChannel.js +80 -0
  28. package/lib/commonjs/ui-format/groupChannel.js.map +1 -0
  29. package/lib/module/hooks/index.js +60 -0
  30. package/lib/module/hooks/index.js.map +1 -0
  31. package/lib/module/index.js +15 -0
  32. package/lib/module/index.js.map +1 -0
  33. package/lib/module/sendbird/attrs.js +49 -0
  34. package/lib/module/sendbird/attrs.js.map +1 -0
  35. package/lib/module/sendbird/channel.js +36 -0
  36. package/lib/module/sendbird/channel.js.map +1 -0
  37. package/lib/module/sendbird/message.js +85 -0
  38. package/lib/module/sendbird/message.js.map +1 -0
  39. package/lib/module/shared/arrayToMap.js +45 -0
  40. package/lib/module/shared/arrayToMap.js.map +1 -0
  41. package/lib/module/shared/conditionChaining.js +7 -0
  42. package/lib/module/shared/conditionChaining.js.map +1 -0
  43. package/lib/module/shared/logger.js +90 -0
  44. package/lib/module/shared/logger.js.map +1 -0
  45. package/lib/module/shared/pubsub.js +45 -0
  46. package/lib/module/shared/pubsub.js.map +1 -0
  47. package/lib/module/shared/regex.js +78 -0
  48. package/lib/module/shared/regex.js.map +1 -0
  49. package/lib/module/types.js +2 -0
  50. package/lib/module/types.js.map +1 -0
  51. package/lib/module/ui-format/common.js +85 -0
  52. package/lib/module/ui-format/common.js.map +1 -0
  53. package/lib/module/ui-format/groupChannel.js +59 -0
  54. package/lib/module/ui-format/groupChannel.js.map +1 -0
  55. package/lib/typescript/src/hooks/index.d.ts +11 -0
  56. package/lib/typescript/src/index.d.ts +15 -0
  57. package/lib/typescript/src/sendbird/attrs.d.ts +43 -0
  58. package/lib/typescript/src/sendbird/channel.d.ts +18 -0
  59. package/lib/typescript/src/sendbird/message.d.ts +20 -0
  60. package/lib/typescript/src/shared/arrayToMap.d.ts +5 -0
  61. package/lib/typescript/src/shared/conditionChaining.d.ts +2 -0
  62. package/lib/typescript/src/shared/logger.d.ts +38 -0
  63. package/lib/typescript/src/shared/pubsub.d.ts +8 -0
  64. package/lib/typescript/src/shared/regex.d.ts +11 -0
  65. package/lib/typescript/src/types.d.ts +79 -0
  66. package/lib/typescript/src/ui-format/common.d.ts +43 -0
  67. package/lib/typescript/src/ui-format/groupChannel.d.ts +5 -0
  68. package/package.json +75 -0
  69. package/src/hooks/index.ts +65 -0
  70. package/src/index.ts +57 -0
  71. package/src/sendbird/attrs.ts +44 -0
  72. package/src/sendbird/channel.ts +37 -0
  73. package/src/sendbird/message.ts +101 -0
  74. package/src/shared/arrayToMap.ts +70 -0
  75. package/src/shared/conditionChaining.ts +6 -0
  76. package/src/shared/logger.ts +59 -0
  77. package/src/shared/pubsub.ts +42 -0
  78. package/src/shared/regex.ts +74 -0
  79. package/src/types.ts +101 -0
  80. package/src/ui-format/common.ts +73 -0
  81. package/src/ui-format/groupChannel.ts +47 -0
package/src/index.ts ADDED
@@ -0,0 +1,57 @@
1
+ export { Logger } from './shared/logger';
2
+ export { default as arrayToMap, arrayToMapWithGetter } from './shared/arrayToMap';
3
+ export { createPubSub } from './shared/pubsub';
4
+ export { default as conditionChaining } from './shared/conditionChaining';
5
+ export {
6
+ urlRegexStrict,
7
+ urlRegexRough,
8
+ newLineRegex,
9
+ audioExtRegex,
10
+ videoExtRegex,
11
+ imageExtRegex,
12
+ getFileType,
13
+ getFileExtension,
14
+ replaceUrlAsComponents,
15
+ } from './shared/regex';
16
+
17
+ export * from './hooks';
18
+ export * from './ui-format/groupChannel';
19
+ export * from './ui-format/common';
20
+ export * from './sendbird/channel';
21
+ export * from './sendbird/message';
22
+ export * from './sendbird/attrs';
23
+
24
+ export const NOOP: () => void = () => void 0;
25
+ export const ASYNC_NOOP = async () => void 0;
26
+ export const PASS = <T>(val: T) => val;
27
+ export type {
28
+ FilterByValueType,
29
+ UnionToIntersection,
30
+ OmittedValues,
31
+ PartialDeep,
32
+ Optional,
33
+ ContextValue,
34
+ SendbirdMessage,
35
+ SendbirdChatSDK,
36
+ SendbirdChannel,
37
+ SendbirdBaseChannel,
38
+ SendbirdGroupChannel,
39
+ SendbirdOpenChannel,
40
+ SendbirdUser,
41
+ SendbirdMember,
42
+ SendbirdBaseMessage,
43
+ SendbirdUserMessage,
44
+ SendbirdFileMessage,
45
+ SendbirdAdminMessage,
46
+ SendbirdGroupChannelParams,
47
+ SendbirdFileMessageParams,
48
+ SendbirdUserMessageParams,
49
+ SendbirdError,
50
+ SendbirdMessageCollection,
51
+ SendbirdGroupChannelCollection,
52
+ NotificationFiles,
53
+ NotificationMentionedUsers,
54
+ NotificationTranslations,
55
+ PartialNullable,
56
+ SendbirdDataPayload,
57
+ } from './types';
@@ -0,0 +1,44 @@
1
+ export enum ApplicationAttributes {
2
+ reactions = 'reactions',
3
+ message_search_v3 = 'message_search_v3',
4
+ enable_og_tag = 'enable_og_tag',
5
+ use_last_seen_at = 'use_last_seen_at',
6
+ enable_message_threading = 'enable_message_threading',
7
+ allow_message_update_from_sdk = 'allow_message_update_from_sdk',
8
+ allow_group_channel_create_from_sdk = 'allow_group_channel_create_from_sdk',
9
+ allow_migration_api = 'allow_migration_api',
10
+ allow_only_operator_sdk_to_update_group_channel_image = 'allow_only_operator_sdk_to_update_group_channel_image',
11
+ allow_user_update_from_sdk = 'allow_user_update_from_sdk',
12
+ allow_broadcast_channel = 'allow_broadcast_channel',
13
+ allow_sdk_file_message_urls = 'allow_sdk_file_message_urls',
14
+ allow_group_channel_leave_from_sdk = 'allow_group_channel_leave_from_sdk',
15
+ allow_user_list_from_sdk = 'allow_user_list_from_sdk',
16
+ allow_group_channel_invite_from_sdk = 'allow_group_channel_invite_from_sdk',
17
+ allow_only_operator_sdk_to_update_group_channel_name = 'allow_only_operator_sdk_to_update_group_channel_name',
18
+ allow_group_channel_update_from_sdk = 'allow_group_channel_update_from_sdk',
19
+ allow_super_group_channel = 'allow_super_group_channel',
20
+ allow_non_operators_to_mention_channel = 'allow_non_operators_to_mention_channel',
21
+ allow_file_messages_from_sdk = 'allow_file_messages_from_sdk',
22
+ allow_open_channel_create_from_sdk = 'allow_open_channel_create_from_sdk',
23
+ allow_anonymous_message_update = 'allow_anonymous_message_update',
24
+ allow_group_channel_delete_from_sdk = 'allow_group_channel_delete_from_sdk',
25
+ allow_operators_to_ban_operators = 'allow_operators_to_ban_operators',
26
+ allow_operators_to_edit_operators = 'allow_operators_to_edit_operators',
27
+ }
28
+
29
+ export enum PremiumFeatures {
30
+ reactions = 'reactions',
31
+ moderation_group = 'moderation_group',
32
+ message_search_v3 = 'message_search_v3',
33
+ data_export = 'data_export',
34
+ migration = 'migration',
35
+ delivery_receipt = 'delivery_receipt',
36
+ auto_thumbnail = 'auto_thumbnail',
37
+ image_moderation = 'image_moderation',
38
+ super_group_channel = 'super_group_channel',
39
+ announcement = 'announcement',
40
+ moderation_open = 'moderation_open',
41
+ desk = 'desk',
42
+ bot_interface = 'bot_interface',
43
+ auto_trans = 'auto_trans',
44
+ }
@@ -0,0 +1,37 @@
1
+ import type { SendbirdBaseChannel, SendbirdGroupChannel } from '../types';
2
+
3
+ /**
4
+ * Default sort comparator for channel
5
+ * @param {SendbirdBaseChannel} a
6
+ * @param {SendbirdBaseChannel} b
7
+ * @returns {number}
8
+ * */
9
+ export function channelComparator<T extends SendbirdBaseChannel>(a: T, b: T): number {
10
+ if (a.isGroupChannel() && b.isGroupChannel()) {
11
+ return (b.lastMessage?.createdAt ?? b.createdAt) - (a.lastMessage?.createdAt ?? a.createdAt);
12
+ }
13
+ return b.createdAt - a.createdAt;
14
+ }
15
+
16
+ /**
17
+ * Diff utils for channel
18
+ * @param {SendbirdBaseChannel} [a]
19
+ * @param {SendbirdBaseChannel} [b]
20
+ * @returns {boolean}
21
+ * */
22
+ export function isDifferentChannel<T extends SendbirdBaseChannel>(a?: T, b?: T): boolean {
23
+ if (!a || !b) return true;
24
+ return a.url !== b.url;
25
+ }
26
+
27
+ export const groupChannelChatUnavailable = (channel: SendbirdGroupChannel) => {
28
+ return channel.myMutedState === 'muted' || (channel.isFrozen && channel.myRole !== 'operator');
29
+ };
30
+
31
+ export function isDefaultCoverImage(coverUrl: string) {
32
+ return coverUrl === '' || coverUrl.startsWith('https://static.sendbird.com/sample/cover');
33
+ }
34
+
35
+ export function getMembersExcludeMe(channel: SendbirdGroupChannel, currentUserId?: string) {
36
+ return channel.members.filter((m) => m.userId !== currentUserId);
37
+ }
@@ -0,0 +1,101 @@
1
+ import type { SendbirdDataPayload, SendbirdFileMessage, SendbirdMessage } from '../types';
2
+ import { messageTime } from '../ui-format/common';
3
+
4
+ export function isNewMessage(msg: SendbirdMessage, currentUserId?: string) {
5
+ const myMessage = isMyMessage(msg, currentUserId);
6
+ if (myMessage) return false;
7
+ if (msg.isAdminMessage()) return false;
8
+ return msg.updatedAt === 0;
9
+ }
10
+
11
+ export function isMyMessage(msg: SendbirdMessage, currentUserId = '##__USER_ID_IS_NOT_PROVIDED__##') {
12
+ return (
13
+ ('sender' in msg && msg.sender?.userId === currentUserId) ||
14
+ msg.sendingStatus === 'pending' ||
15
+ msg.sendingStatus === 'failed' ||
16
+ msg.sendingStatus === 'canceled'
17
+ );
18
+ }
19
+
20
+ export function messageKeyExtractor(message: SendbirdMessage): string {
21
+ return getMessageUniqId(message);
22
+ }
23
+
24
+ // |-------------------|-------------------|-----------------|-------------------|
25
+ // | sending status | reqId | messageId | createdAt |
26
+ // |-------------------|-------------------|-----------------|-------------------|
27
+ // | pending | timestamp(A) | 0 | timestamp(B) |
28
+ // | canceled | timestamp(A) | 0 | ? |
29
+ // | failed | timestamp(A) | 0 | ? |
30
+ // | succeeded | timestamp(A) / '' | id from DB | timestamp(C) |
31
+ // |-------------------|-------------------|-----------------|-------------------|
32
+ export function messageComparator<T extends SendbirdMessage>(a: T, b: T) {
33
+ let aStatusOffset = 0;
34
+ let bStatusOffset = 0;
35
+
36
+ if (a.sendingStatus !== 'succeeded') aStatusOffset = 999999;
37
+ if (b.sendingStatus !== 'succeeded') bStatusOffset = 999999;
38
+
39
+ return b.createdAt + bStatusOffset - (a.createdAt + aStatusOffset);
40
+ }
41
+
42
+ export function hasSameSender(a?: SendbirdMessage, b?: SendbirdMessage) {
43
+ if (!a || !b) return false;
44
+ if ('sender' in a && 'sender' in b) return a.sender?.userId === b.sender?.userId;
45
+ return false;
46
+ }
47
+
48
+ export function calcMessageGrouping(
49
+ groupEnabled: boolean,
50
+ curr: SendbirdMessage,
51
+ prev?: SendbirdMessage,
52
+ next?: SendbirdMessage,
53
+ ) {
54
+ const getPrev = () => {
55
+ if (!groupEnabled) return false;
56
+ if (!prev) return false;
57
+ if (curr.isAdminMessage()) return false;
58
+ if (!hasSameSender(curr, prev)) return false;
59
+ if (messageTime(new Date(curr.createdAt)) !== messageTime(new Date(prev.createdAt))) return false;
60
+ return true;
61
+ };
62
+
63
+ const getNext = () => {
64
+ if (!groupEnabled) return false;
65
+ if (!next) return false;
66
+ if (curr.isAdminMessage()) return false;
67
+ if (!hasSameSender(curr, next)) return false;
68
+ if (messageTime(new Date(curr.createdAt)) !== messageTime(new Date(next.createdAt))) return false;
69
+ return true;
70
+ };
71
+
72
+ return { groupWithPrev: getPrev(), groupWithNext: getNext() };
73
+ }
74
+
75
+ export function getMessageUniqId(msg: SendbirdMessage) {
76
+ if (msg.isUserMessage() || msg.isFileMessage()) {
77
+ if (msg.sendingStatus === 'succeeded') return String(msg.messageId);
78
+ return msg.reqId;
79
+ }
80
+
81
+ return String(msg.messageId);
82
+ }
83
+
84
+ export function getAvailableUriFromFileMessage(message: SendbirdFileMessage) {
85
+ if (!message.url && message.messageParams && 'uri' in message.messageParams.file) {
86
+ return message.messageParams.file.uri;
87
+ }
88
+ return message.url;
89
+ }
90
+
91
+ type RawSendbirdDataPayload = { sendbird: string | object };
92
+ export function isSendbirdNotification(dataPayload?: {
93
+ [key: string]: string | object;
94
+ }): dataPayload is RawSendbirdDataPayload {
95
+ if (!dataPayload) return false;
96
+ return Boolean(dataPayload['sendbird']);
97
+ }
98
+
99
+ export function parseSendbirdNotification(dataPayload: RawSendbirdDataPayload): SendbirdDataPayload {
100
+ return typeof dataPayload.sendbird === 'string' ? JSON.parse(dataPayload.sendbird) : dataPayload.sendbird;
101
+ }
@@ -0,0 +1,70 @@
1
+ import type { FilterByValueType } from '../types';
2
+
3
+ export default function arrayToMap<T extends Record<K, unknown>, K extends keyof T = keyof T>(
4
+ arr: T[],
5
+ selector: keyof FilterByValueType<T, string | number>,
6
+ ): Record<string, T>;
7
+
8
+ export default function arrayToMap<T extends Record<K, unknown>, K extends keyof T = keyof T>(
9
+ arr: T[],
10
+ selector: string,
11
+ fallbackSelector: K,
12
+ ): Record<string, T>;
13
+
14
+ export default function arrayToMap<T extends Record<K, unknown>, K extends keyof T = keyof T>(
15
+ arr: T[],
16
+ selector: string,
17
+ selector2: string,
18
+ combineKey: true,
19
+ ): Record<string, T>;
20
+
21
+ /**
22
+ * Convert Array of the object to Object
23
+ * @param {object[]} arr
24
+ * @param {keyof object} selector
25
+ * @param {keyof object} [fallbackSelector]
26
+ * @param {boolean} [combine]
27
+ * @returns {Record<keyof object, object>}
28
+ * @example
29
+ * ```
30
+ * const arr = [{name: 'john'}, {name: 'chris'}, {name: 'sravan'}, {name: 'hoon'}];
31
+ * const obj = arrayToMap(arr, 'name');
32
+ *
33
+ * console.log(obj);
34
+ * // {
35
+ * // 'john': {name: 'john'},
36
+ * // 'chris': {name: 'chris'},
37
+ * // 'sravan': {name: 'sravan'},
38
+ * // 'hoon': {name: 'hoon'}
39
+ * // }
40
+ * ```
41
+ * */
42
+ export default function arrayToMap<T extends Record<K, unknown>, K extends keyof T = keyof T>(
43
+ arr: T[],
44
+ selector: K,
45
+ fallbackSelector?: K,
46
+ combine?: boolean,
47
+ ) {
48
+ return arr.reduce((accum, curr) => {
49
+ if (combine && fallbackSelector) {
50
+ const _key = curr[selector] + (curr[fallbackSelector] as string);
51
+ accum[_key] = curr;
52
+ } else {
53
+ const _key = (curr[selector] || curr[fallbackSelector as K]) as string;
54
+ accum[_key] = curr;
55
+ }
56
+
57
+ return accum;
58
+ }, {} as Record<string, T>);
59
+ }
60
+
61
+ export function arrayToMapWithGetter<T extends Record<K, unknown>, K extends keyof T = keyof T>(
62
+ arr: T[],
63
+ getSelector: (item: T) => string,
64
+ ) {
65
+ return arr.reduce((accum, curr) => {
66
+ const _key = getSelector(curr);
67
+ accum[_key] = curr;
68
+ return accum;
69
+ }, {} as Record<string, T>);
70
+ }
@@ -0,0 +1,6 @@
1
+ const conditionChaining = <T, V>(conditions: T[], values: V[]) => {
2
+ const idx = conditions.findIndex((state) => Boolean(state));
3
+ return idx > -1 ? values[idx] : values[values.length - 1];
4
+ };
5
+
6
+ export default conditionChaining;
@@ -0,0 +1,59 @@
1
+ /* eslint-disable no-console */
2
+
3
+ const LogLevelEnum = {
4
+ 'none': 0,
5
+ 'log': 1,
6
+ 'error': 2,
7
+ 'warn': 3,
8
+ 'info': 4,
9
+ 'debug': 5,
10
+ };
11
+ type LogLevel = keyof typeof LogLevelEnum;
12
+
13
+ const createLogger = (level: LogLevel = 'warn') => {
14
+ let _logLevel = level;
15
+ let _title = '[UIKit]';
16
+
17
+ return {
18
+ setTitle(title: string) {
19
+ _title = title;
20
+ },
21
+ setLogLevel(lv: LogLevel) {
22
+ _logLevel = lv;
23
+ },
24
+ getLogLevel() {
25
+ return _logLevel;
26
+ },
27
+ log(...args: unknown[]) {
28
+ if (LogLevelEnum[_logLevel] < LogLevelEnum.log) return LogLevelEnum.none;
29
+ console.log(_title, ...args);
30
+ return LogLevelEnum[_logLevel];
31
+ },
32
+ error(...args: unknown[]) {
33
+ if (LogLevelEnum[_logLevel] < LogLevelEnum.error) return LogLevelEnum.none;
34
+ console.error(_title, ...args);
35
+ return LogLevelEnum[_logLevel];
36
+ },
37
+ warn(...args: unknown[]) {
38
+ if (LogLevelEnum[_logLevel] < LogLevelEnum.warn) return LogLevelEnum.none;
39
+ console.warn(_title, ...args);
40
+ return LogLevelEnum[_logLevel];
41
+ },
42
+ info(...args: unknown[]) {
43
+ if (LogLevelEnum[_logLevel] < LogLevelEnum.info) return LogLevelEnum.none;
44
+ console.info(_title, ...args);
45
+ return LogLevelEnum[_logLevel];
46
+ },
47
+ debug(...args: unknown[]) {
48
+ if (LogLevelEnum[_logLevel] < LogLevelEnum.debug) return LogLevelEnum.none;
49
+ console.log(_title, ...args);
50
+ return LogLevelEnum[_logLevel];
51
+ },
52
+ };
53
+ };
54
+
55
+ export const Logger = {
56
+ ...createLogger(),
57
+ LogLevelEnum,
58
+ create: createLogger,
59
+ };
@@ -0,0 +1,42 @@
1
+ export type SubscribeListener = ((...args: unknown[]) => void) & { onError?: (error: unknown) => void };
2
+ export type SubscribeListenerPool = Record<symbol, SubscribeListener>;
3
+
4
+ export const createPubSub = (label = 'pubsub') => {
5
+ const eventPool: Record<string, SubscribeListenerPool> = {};
6
+
7
+ const getSubscribeListenerPool = (event: string) => {
8
+ if (!eventPool[event]) eventPool[event] = {};
9
+ return eventPool[event];
10
+ };
11
+
12
+ return {
13
+ subscribe: (event: string, listener: SubscribeListener, subscriber = '') => {
14
+ const name = `${label}_${event}_${subscriber}`;
15
+ const id = Symbol(name);
16
+
17
+ const subscribeListenerPool = getSubscribeListenerPool(event);
18
+ subscribeListenerPool[id] = listener;
19
+
20
+ return () => {
21
+ delete getSubscribeListenerPool(event)[id];
22
+ };
23
+ },
24
+ publish: (event: string, ...args: unknown[]) => {
25
+ const subscribeListenerPool = getSubscribeListenerPool(event);
26
+ const ids = Object.getOwnPropertySymbols(subscribeListenerPool);
27
+ setTimeout(() => {
28
+ ids.forEach((id) => {
29
+ const listener = subscribeListenerPool[id];
30
+ try {
31
+ listener(...args);
32
+ } catch (err: unknown) {
33
+ if (listener.onError) {
34
+ (err as Error).stack += `\npubsub error: ${id.description}`;
35
+ listener.onError(err);
36
+ }
37
+ }
38
+ });
39
+ }, 0);
40
+ },
41
+ };
42
+ };
@@ -0,0 +1,74 @@
1
+ // For iOS 12 backwards compatibility
2
+ require('string.prototype.matchall').shim();
3
+
4
+ export const urlRegexStrict =
5
+ /(https?:\/\/|www\.)[-a-zA-Z0-9@:%._+~#=]{1,256}\.(xn--)?[a-z0-9-]{2,20}\b([-a-zA-Z0-9@:%_+[\],.~#?&/=]*[-a-zA-Z0-9@:%_+\]~#?&/=])*/g;
6
+ export const urlRegexRough =
7
+ /(https?:\/\/|www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
8
+ export const phoneRegex = /[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,7}/;
9
+ export const emailRegex = /\S+@\S+\.\S+/;
10
+ export const newLineRegex = /\r\n|\r|\n/g;
11
+
12
+ // const cases = [
13
+ // {
14
+ // type: 'urlStrict',
15
+ // regex: urlRegexStrict,
16
+ // },
17
+ // {
18
+ // type: 'urlRough',
19
+ // regex: urlRegexStrict,
20
+ // },
21
+ // {
22
+ // type: 'email',
23
+ // regex: emailRegex,
24
+ // },
25
+ // {
26
+ // type: 'phone',
27
+ // regex: urlRegexStrict,
28
+ // },
29
+ // ];
30
+
31
+ export const replaceUrlAsComponents = <T>(originText: string, replacer: (url: string) => T, strict?: boolean) => {
32
+ const matches = [...originText.matchAll(strict ? urlRegexStrict : urlRegexRough)];
33
+ const founds = matches.map((value) => {
34
+ const text = value[0];
35
+ const start = value.index ?? 0;
36
+ const end = start + text.length;
37
+ return { text, start, end };
38
+ });
39
+
40
+ const items: Array<string | T> = [originText];
41
+ let cursor = 0;
42
+ founds.forEach(({ text, start, end }) => {
43
+ const restText = items.pop() as string;
44
+ const head = restText.slice(0, start - cursor);
45
+ const mid = replacer(text);
46
+ const tail = restText.slice(end - cursor);
47
+ items.push(head, mid, tail);
48
+ cursor = end;
49
+ });
50
+ return items;
51
+ };
52
+
53
+ export const imageExtRegex = /jpeg|jpg|png|webp|gif/i;
54
+ export const audioExtRegex = /3gp|aac|aax|act|aiff|flac|gsm|m4a|m4b|m4p|tta|wma|mp3|webm|wav/i;
55
+ export const videoExtRegex = /mov|vod|mp4|avi/i;
56
+ export const getFileType = (extOrType: string) => {
57
+ if (extOrType.indexOf('/') > -1) {
58
+ const type = extOrType.split('/')[0];
59
+ if (type === 'video') return 'video';
60
+ if (type === 'audio') return 'audio';
61
+ if (type === 'image') return 'image';
62
+ return 'file';
63
+ }
64
+
65
+ if (extOrType.match(imageExtRegex)) return 'image';
66
+ if (extOrType.match(audioExtRegex)) return 'audio';
67
+ if (extOrType.match(videoExtRegex)) return 'video';
68
+ return 'file';
69
+ };
70
+ export function getFileExtension(filePath: string) {
71
+ const idx = filePath.lastIndexOf('.');
72
+ if (idx === -1) return '';
73
+ return filePath.slice(idx - filePath.length).toLowerCase();
74
+ }
package/src/types.ts ADDED
@@ -0,0 +1,101 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ /* eslint-disable @typescript-eslint/no-empty-interface */
4
+ import type React from 'react';
5
+ import type Sendbird from 'sendbird';
6
+
7
+ export type FilterByValueType<T extends object, Type> = {
8
+ [K in keyof T as T[K] extends Type ? K : never]: T[K];
9
+ };
10
+
11
+ export type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void
12
+ ? I
13
+ : never;
14
+
15
+ export type OmittedValues<T, K extends keyof T> = Omit<T, K>[keyof Omit<T, K>];
16
+
17
+ export type PartialDeep<T> = T extends object
18
+ ? T extends Function
19
+ ? T
20
+ : {
21
+ [P in keyof T]?: PartialDeep<T[P]>;
22
+ }
23
+ : T;
24
+
25
+ export type PartialNullable<T> = {
26
+ [P in keyof T]?: T[P] | null;
27
+ };
28
+
29
+ export type Optional<T> = T | undefined;
30
+
31
+ export type ContextValue<T extends React.Context<any>> = T extends React.Context<infer V> ? V : never;
32
+
33
+ // for v4 migration
34
+ export type SendbirdChatSDK = Sendbird.SendBirdInstance;
35
+ export type SendbirdMessage =
36
+ | Sendbird.BaseMessageInstance
37
+ | Sendbird.FileMessage
38
+ | Sendbird.UserMessage
39
+ | Sendbird.AdminMessage;
40
+ export type SendbirdChannel = Sendbird.BaseChannel | Sendbird.GroupChannel | Sendbird.OpenChannel;
41
+ export type SendbirdUserMessage = Sendbird.UserMessage;
42
+ export type SendbirdFileMessage = Sendbird.FileMessage;
43
+ export type SendbirdAdminMessage = Sendbird.AdminMessage;
44
+ export type SendbirdBaseMessage = Sendbird.BaseMessageInstance;
45
+ export type SendbirdFileMessageParams = Sendbird.FileMessageParams;
46
+ export type SendbirdUserMessageParams = Sendbird.UserMessageParams;
47
+ export type SendbirdError = Sendbird.SendBirdError;
48
+ export type SendbirdGroupChannelParams = Sendbird.GroupChannelParams;
49
+ export type SendbirdUser = Sendbird.User;
50
+ export type SendbirdMember = Sendbird.Member;
51
+ export type SendbirdGroupChannel = Sendbird.GroupChannel;
52
+ export type SendbirdBaseChannel = Sendbird.BaseChannel;
53
+ export type SendbirdOpenChannel = Sendbird.OpenChannel;
54
+ export type SendbirdGroupChannelCollection = Sendbird.GroupChannelCollection;
55
+ export type SendbirdMessageCollection = Sendbird.MessageCollection;
56
+
57
+ export interface SendbirdDataPayload {
58
+ app_id: string;
59
+ audience_type: string;
60
+ category: string;
61
+ channel: {
62
+ channel_unread_message_count: number;
63
+ channel_url: string;
64
+ custom_type: string;
65
+ name: string;
66
+ };
67
+ channel_type: string;
68
+ created_at: number;
69
+ custom_type: string;
70
+ files: NotificationFiles[];
71
+ mentioned_users: NotificationMentionedUsers[];
72
+ message: string;
73
+ message_id: number;
74
+ push_sound: string;
75
+ push_title: null;
76
+ recipient: {
77
+ id: string;
78
+ name: string;
79
+ push_template: string;
80
+ };
81
+ sender?: {
82
+ id: string;
83
+ name: string;
84
+ profile_url: string;
85
+ require_auth_for_profile_image: boolean;
86
+ };
87
+ sqs_ts: number;
88
+ translations: NotificationTranslations;
89
+ type: 'ADMM' | 'MESG' | 'FILE' | string;
90
+ unread_message_count: number;
91
+ }
92
+ export interface NotificationFiles {
93
+ type: string;
94
+ url: string;
95
+ name: string;
96
+ size: number;
97
+ require_auth: boolean;
98
+ }
99
+
100
+ export interface NotificationTranslations {}
101
+ export interface NotificationMentionedUsers {}
@@ -0,0 +1,73 @@
1
+ import type { Locale } from 'date-fns';
2
+ import format from 'date-fns/format';
3
+
4
+ type TruncateMode = 'head' | 'mid' | 'tail';
5
+ type TruncateOption = { mode: TruncateMode; maxLen: number; separator: string };
6
+ const defaultOpts: TruncateOption = { mode: 'mid', maxLen: 40, separator: '...' };
7
+
8
+ /**
9
+ * String truncate util
10
+ * @param {string} str
11
+ * @param {Object} opts Options for truncate
12
+ * @param {'head' | 'mid' | 'tail'} opts.mode default "mid"
13
+ * @param {number} opts.maxLen default 40
14
+ * @param {string} opts.separator default "..."
15
+ * @returns {string}
16
+ * */
17
+ export const truncate = (str: string, opts: Partial<TruncateOption> = defaultOpts): string => {
18
+ const options = { ...defaultOpts, ...opts };
19
+ const { maxLen, mode, separator } = options;
20
+
21
+ if (str.length <= maxLen) return str;
22
+
23
+ if (mode === 'head') {
24
+ return separator + str.slice(-maxLen);
25
+ }
26
+
27
+ if (mode === 'mid') {
28
+ const lead = Math.ceil(maxLen / 2);
29
+ const trail = Math.floor(maxLen / 2);
30
+ return str.slice(0, lead) + separator + str.slice(-trail);
31
+ }
32
+
33
+ if (mode === 'tail') {
34
+ return str.slice(0, maxLen) + separator;
35
+ }
36
+
37
+ throw new Error('Invalid truncate mode: ' + mode);
38
+ };
39
+
40
+ /**
41
+ * Badge count truncate util
42
+ * If count exceed the limit, it comes in the form of "MAX+"
43
+ *
44
+ * @param {number} count
45
+ * @param {number} MAX default 99
46
+ * @returns {string}
47
+ * */
48
+ export const truncatedBadgeCount = (count: number, MAX = 99) => {
49
+ if (count >= MAX) return `${MAX}+`;
50
+ return `${count}`;
51
+ };
52
+
53
+ /**
54
+ * Messages date separator format
55
+ *
56
+ * @param {Date} date
57
+ * @param {Locale} [locale]
58
+ * @returns {string}
59
+ * */
60
+ export const dateSeparator = (date: Date, locale?: Locale): string => {
61
+ return format(date, 'E, LLL dd', { locale });
62
+ };
63
+
64
+ /**
65
+ * Message time format
66
+ *
67
+ * @param {Date} date
68
+ * @param {Locale} [locale]
69
+ * @returns {string}
70
+ * */
71
+ export const messageTime = (date: Date, locale?: Locale): string => {
72
+ return format(date, 'p', { locale });
73
+ };