Commit 84b48c2e by panjiangyi

format file

parent 31d2e1e4
import type { UniplatSdk } from "uniplat-sdk"
import type { UniplatSdk } from "uniplat-sdk";
export interface Chat {
chat_id: number
title: string
service_id: number
create_time: number
update_time: number
start_time: number
end_time: number
is_finish: boolean
msg_id: number
last_cs_eid: string
first_cs_eid: string
last_msg_eid: string
last_msg_name: string
last_msg_ts: number
last_online_time: number
msg_type: string
msg: string
customer_name: string
customer_mobile: string
customer_avatar_url: string
customer_online: boolean
customer_eid: string
origin: string
unread_msg_count: number
chat_id: number;
title: string;
service_id: number;
create_time: number;
update_time: number;
start_time: number;
end_time: number;
is_finish: boolean;
msg_id: number;
last_cs_eid: string;
first_cs_eid: string;
last_msg_eid: string;
last_msg_name: string;
last_msg_ts: number;
last_online_time: number;
msg_type: string;
msg: string;
customer_name: string;
customer_mobile: string;
customer_avatar_url: string;
customer_online: boolean;
customer_eid: string;
origin: string;
unread_msg_count: number;
}
export type TokenStringGetter = () => Promise<string>
......@@ -33,174 +33,174 @@ export interface ChatOption {
/**
* 企业token [商户端使用,用户端和移动端不需要]
*/
enterpriseTokenString?: TokenStringGetter
enterpriseTokenString?: TokenStringGetter;
/**
* 个人token
*/
userTokenString: TokenStringGetter
userTokenString: TokenStringGetter;
/**
* 长链接chat sdk地址
*/
webSocketUri: string
webSocketUri: string;
sdk: () => UniplatSdk
sdk: () => UniplatSdk;
orgId: () => string | number
orgId: () => string | number;
logger?: ChatServiceLogger
logger?: ChatServiceLogger;
}
export interface ChatServiceLogger {
enabled: boolean
debug(message?: any, ...optionalParams: any[]): void
info(message?: any, ...optionalParams: any[]): void
error(message?: any, ...optionalParams: any[]): void
enabled: boolean;
debug(message?: any, ...optionalParams: any[]): void;
info(message?: any, ...optionalParams: any[]): void;
error(message?: any, ...optionalParams: any[]): void;
}
export type ChatListRequestList = {
list: Chat[]
total: number
list: Chat[];
total: number;
}
export interface Message {
at_id: string
chat_id: number
create_time: number
eid: string
id: number
is_open: boolean
is_read: boolean
like: boolean
like_count: number
msg: string
oid: string
read_count: number
ref_id: number
status: number
total_read_count: number
ts: number
type: "text" | "image" | "file" | "video" | "voice"
update_time: number
url: string
at_id: string;
chat_id: number;
create_time: number;
eid: string;
id: number;
is_open: boolean;
is_read: boolean;
like: boolean;
like_count: number;
msg: string;
oid: string;
read_count: number;
ref_id: number;
status: number;
total_read_count: number;
ts: number;
type: "text" | "image" | "file" | "video" | "voice";
update_time: number;
url: string;
}
export type MessageRequestResult = readonly Message[]
export interface CreateChatByServicemanRequestResult {
id: number
org_id: string
uid: string
oid: string
eid: string
type: string
title: string
app_id: string
tag: string
msg_id: number
ext: string
exit_msg_id: number
is_exited: boolean
dnd: number
is_top: boolean
label: string
join_msg_id: number
last_read_msg_id: number
biz_id: string
business_data: string
is_finish: boolean
is_deleted: boolean
is_remove: boolean
member_type: number
ref_id: number
unread_msg_count: number
at_me: boolean
at_all: boolean
last_login_oid: string
owner_oid: string
owner_eid: string
is_act: boolean
create_time: number
update_time: number
last_msg_ts: number
members_updated: number
user_updated: number
id: number;
org_id: string;
uid: string;
oid: string;
eid: string;
type: string;
title: string;
app_id: string;
tag: string;
msg_id: number;
ext: string;
exit_msg_id: number;
is_exited: boolean;
dnd: number;
is_top: boolean;
label: string;
join_msg_id: number;
last_read_msg_id: number;
biz_id: string;
business_data: string;
is_finish: boolean;
is_deleted: boolean;
is_remove: boolean;
member_type: number;
ref_id: number;
unread_msg_count: number;
at_me: boolean;
at_all: boolean;
last_login_oid: string;
owner_oid: string;
owner_eid: string;
is_act: boolean;
create_time: number;
update_time: number;
last_msg_ts: number;
members_updated: number;
user_updated: number;
}
export type ChatMemberExtraInfo = {
name?: string
phone?: string
name?: string;
phone?: string;
}
export interface ChatMember {
at_all: boolean
at_me: boolean
chat_id: number
create_time: number
dnd: number
eid: string
exit_msg_id: number
id: number
is_act: boolean
is_exited: boolean
is_remove: boolean
is_top: boolean
join_msg_id: number
label: string
nickname: string
oid: string
org_id: string
type: number
uid: string
unread_msg_count: number
update_time: number
at_all: boolean;
at_me: boolean;
chat_id: number;
create_time: number;
dnd: number;
eid: string;
exit_msg_id: number;
id: number;
is_act: boolean;
is_exited: boolean;
is_remove: boolean;
is_top: boolean;
join_msg_id: number;
label: string;
nickname: string;
oid: string;
org_id: string;
type: number;
uid: string;
unread_msg_count: number;
update_time: number;
}
export type ChatMembers = readonly ChatMember[]
export interface ServiceMan {
id: number
oid: string
eid: string
is_deleted: boolean
delete_time: number
delete_eid: string
service_id: number
type: number
name: string
mobile: string
email: string
description: string
avatar_url: string
create_time: number
update_time: number
chat_count: number
status: number
id: number;
oid: string;
eid: string;
is_deleted: boolean;
delete_time: number;
delete_eid: string;
service_id: number;
type: number;
name: string;
mobile: string;
email: string;
description: string;
avatar_url: string;
create_time: number;
update_time: number;
chat_count: number;
status: number;
}
export type AllServiceMan = ServiceMan[]
export interface OneWhoReadMessage {
create_time: number
eid: string
id: number
is_read: boolean
like: boolean
like_time: number
oid: string
owner_id: number
read_time: number
target_id: number
type: number
uid: string
create_time: number;
eid: string;
id: number;
is_read: boolean;
like: boolean;
like_time: number;
oid: string;
owner_id: number;
read_time: number;
target_id: number;
type: number;
uid: string;
}
export interface GetAllChatListParams {
service_id: number
user_info?: string
origin?: string
user_id?: string
is_finish?: boolean | -1 | number
chat_update_time_start?: string
chat_update_time_end?: string
page?: number
page_size?: number
last_cs_eid?: string | number
service_id: number;
user_info?: string;
origin?: string;
user_id?: string;
is_finish?: boolean | -1 | number;
chat_update_time_start?: string;
chat_update_time_end?: string;
page?: number;
page_size?: number;
last_cs_eid?: string | number;
}
import { TokenStringGetter } from "../model"
import { invokeGet } from "./request"
import { TokenStringGetter } from "../model";
import { invokeGet } from "./request";
export class EmojiService {
private static ready = false
......@@ -11,39 +12,39 @@ export class EmojiService {
this.url =
process.env.NODE_ENV === "production"
? "https://file.teammix.com"
: ""
: "";
}
public async getEmoji() {
const token = await EmojiService.token()
const token = await EmojiService.token();
return invokeGet<{
type: string
type: string;
list: {
code: string
name: string
emoji_chars: string
}[]
}>(`${this.url}/v1/emoji/list?type=chat`, token)
code: string;
name: string;
emoji_chars: string;
}[];
}>(`${this.url}/v1/emoji/list?type=chat`, token);
}
public static onReady(callback: () => void) {
if (EmojiService.ready) {
callback()
callback();
} else {
EmojiService.beforeReadyCacheAction.push(callback)
EmojiService.beforeReadyCacheAction.push(callback);
}
}
private static fireBeforeReadyAction() {
for (const item of EmojiService.beforeReadyCacheAction) {
item()
item();
}
EmojiService.beforeReadyCacheAction = []
EmojiService.beforeReadyCacheAction = [];
}
public static raiseOnReady(token: TokenStringGetter) {
EmojiService.ready = true
EmojiService.token = token
EmojiService.fireBeforeReadyAction()
EmojiService.ready = true;
EmojiService.token = token;
EmojiService.fireBeforeReadyAction();
}
}
import Axios from "axios"
import qs from "qs"
import Axios from "axios";
import qs from "qs";
export function buildConfig(token: string, url: string) {
if (url && url.includes("/general")) {
return { headers: { Authorization: token, CurrentOrg: this.station } }
return { headers: { Authorization: token, CurrentOrg: this.station } };
}
return { headers: { Authorization: token } }
return { headers: { Authorization: token } };
}
export function invokeGet<T>(url: string, token: string) {
return new Promise<T>((resolve, reject) => {
Axios.get(url, buildConfig(token, url))
.then((r) => {
if (r?.data?.data !== undefined) {
return resolve(r.data.data as T)
return resolve(r.data.data as T);
}
reject(r)
reject(r);
})
.catch(reject)
})
.catch(reject);
});
}
export const enum DataType {
......@@ -32,21 +32,21 @@ export function invokePost<T>(
data: any,
dataType = DataType.Json
): Promise<T> {
let postData: any = null
let postData: any = null;
if (dataType === DataType.Qs) {
postData = qs.stringify(data)
postData = qs.stringify(data);
} else {
postData = data
postData = data;
}
return new Promise<T>((resolve, reject) => {
Axios.post(url, postData, buildConfig(token, url))
.then((r) => {
if (r?.data?.data !== undefined) {
return resolve(r.data.data as T)
return resolve(r.data.data as T);
}
reject(r)
reject(r);
})
.catch(reject)
})
.catch(reject);
});
}
export function isAccessibleUrl(url: string) {
return url && (url.startsWith("blob") || url.startsWith("http"))
return url && (url.startsWith("blob") || url.startsWith("http"));
}
import Vue from "vue"
import { invokePost } from "./request"
import Axios from "axios"
import tokenManager from "../xim/token"
const orgId = () => Vue.prototype.global.org?.id ?? "0"
import Axios from "axios";
import Vue from "vue";
import tokenManager from "../xim/token";
import { invokePost } from "./request";
const orgId = () => Vue.prototype.global.org?.id ?? "0";
export async function uploadFile(
file: File,
chatId: number,
width?: number,
height?: number
) {
const splits = file.name.split(".")
const splits = file.name.split(".");
const parameter = {
file_name: file.name,
size: file.size,
......@@ -18,30 +20,30 @@ export async function uploadFile(
owner_id: chatId,
org_id: orgId(),
ext_type: splits[splits.length - 1],
}
};
if (width && height) {
Object.assign(parameter, { width, height })
Object.assign(parameter, { width, height });
}
const token = await invokePost<{
url_id: string
sign: string
domain: string
file_name: string
id: number
}>("/xchat/personal/upload_token", await tokenManager.getToken(), parameter)
url_id: string;
sign: string;
domain: string;
file_name: string;
id: number;
}>("/xchat/personal/upload_token", await tokenManager.getToken(), parameter);
const uri = `${token.domain}/gw/file/upload`
const form = new FormData()
form.append("file", file)
form.append("fileId", token.url_id)
form.append("fileSize", file.size.toString())
form.append("orgId", orgId())
form.append("sign", token.sign)
const uri = `${token.domain}/gw/file/upload`;
const form = new FormData();
form.append("file", file);
form.append("fileId", token.url_id);
form.append("fileSize", file.size.toString());
form.append("orgId", orgId());
form.append("sign", token.sign);
const result = await Axios.post(uri, form, {
headers: { "Content-Type": "multipart/form-data" },
})
});
if (result && result.data && result.data.errcode === 0) {
return token.url_id
return token.url_id;
}
return ""
return "";
}
export class ImageCompresser {
public static dataURL2File(data: string, name: string) {
const arr = data.split(",")
const arr = data.split(",");
if (arr) {
if (arr[0]) {
const match = arr[0].match(/:(.*?);/)
const match = arr[0].match(/:(.*?);/);
if (match) {
const mime = match[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
const mime = match[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
// eslint-disable-next-line no-plusplus
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], name, { type: mime })
return new File([u8arr], name, { type: mime });
}
}
}
return null
return null;
}
public static readFile2Image(
......@@ -26,38 +26,38 @@ export class ImageCompresser {
maxHeight: number
): Promise<File | null> {
return new Promise((resolve, reject) => {
const img = new Image()
const reader = new FileReader()
const img = new Image();
const reader = new FileReader();
reader.onload = (e) => {
if (e && e.target && e.target.result) {
img.src = e.target.result as string
img.src = e.target.result as string;
} else {
reject()
reject();
}
}
reader.readAsDataURL(file)
};
reader.readAsDataURL(file);
img.onload = () => {
const originWidth = img.naturalWidth
const originHeight = img.naturalHeight
const originWidth = img.naturalWidth;
const originHeight = img.naturalHeight;
if (originWidth > maxWidth || originHeight > maxHeight) {
ImageCompresser.compressImg(img, maxWidth, maxHeight)
.then((b) => {
if (b) {
const newFile = new File([b], file.name)
const newFile = new File([b], file.name);
// 如果压缩完还不如原始图片size小,直接返回原始图片
resolve(
newFile.size <= file.size ? newFile : file
)
);
} else {
reject()
reject();
}
})
.catch(reject)
.catch(reject);
} else {
resolve(file)
resolve(file);
}
}
})
};
});
}
/**
......@@ -74,40 +74,40 @@ export class ImageCompresser {
type?: string
): Promise<Blob | null> {
return new Promise((resolve, reject) => {
const canvas = document.createElement("canvas")
const context = canvas.getContext("2d")
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
if (!context) {
reject()
return
reject();
return;
}
const { width: originWidth, height: originHeight } = img // 最大尺寸限制
const maxWidth = mx
const maxHeight = mh // 目标尺寸
let targetWidth = originWidth
let targetHeight = originHeight
const { width: originWidth, height: originHeight } = img; // 最大尺寸限制
const maxWidth = mx;
const maxHeight = mh; // 目标尺寸
let targetWidth = originWidth;
let targetHeight = originHeight;
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > 1) {
// 宽图片
targetWidth = maxWidth
targetWidth = maxWidth;
targetHeight = Math.round(
maxWidth * (originHeight / originWidth)
)
);
} else {
// 高图片
targetHeight = maxHeight
targetHeight = maxHeight;
targetWidth = Math.round(
maxHeight * (originWidth / originHeight)
)
);
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
context.drawImage(img, 0, 0, targetWidth, targetHeight)
canvas.width = targetWidth;
canvas.height = targetHeight;
context.clearRect(0, 0, targetWidth, targetHeight);
context.drawImage(img, 0, 0, targetWidth, targetHeight);
canvas.toBlob((blob) => {
resolve(blob)
canvas.remove()
}, type || "image/png")
})
resolve(blob);
canvas.remove();
}, type || "image/png");
});
}
}
import { UserAgentHelper } from "../user-agent"
import { UserAgentHelper } from "../user-agent";
export enum TransformTarget {
Mobile,
......@@ -36,12 +36,12 @@ export interface PcMobileTransformerOption {
/**
* 手机端根host,如 https://www.teammix.com/ms
*/
mobileHost?: string
mobileHost?: string;
/**
* 桌面端根host,如 https://www.teammix.com/s
*/
desktopHost?: string
desktopHost?: string;
}
/**
......@@ -79,84 +79,84 @@ export class PcMobileTransformer {
public static setup(option: PcMobileTransformerOption) {
if (option.desktopHost) {
PcMobileTransformer.target = TransformTarget.Desktop
PcMobileTransformer.target = TransformTarget.Desktop;
PcMobileTransformer.host = PcMobileTransformer.trimEnd(
option.desktopHost
)
);
}
if (option.mobileHost) {
PcMobileTransformer.target = TransformTarget.Mobile
PcMobileTransformer.target = TransformTarget.Mobile;
PcMobileTransformer.host = PcMobileTransformer.trimEnd(
option.mobileHost
)
);
}
}
public static isNeed2Redirect() {
if (PcMobileTransformer.target === TransformTarget.Mobile) {
return PcMobileTransformer.isMobileDevice()
return PcMobileTransformer.isMobileDevice();
}
return false
return false;
}
private static trimEnd(input: string) {
if (input && input.endsWith("/")) {
return input.substring(0, input.length - 1)
return input.substring(0, input.length - 1);
}
return input
return input;
}
private static isMobileDevice() {
const ua = window.navigator.userAgent
return UserAgentHelper.isMobile(ua)
const ua = window.navigator.userAgent;
return UserAgentHelper.isMobile(ua);
}
public static transform(
parameters: (string | number)[],
target: TransformDirection
) {
let v = ""
let v = "";
if (PcMobileTransformer.target === TransformTarget.Desktop) {
v = PcMobileTransformer.transform2Desktop(parameters, target)
v = PcMobileTransformer.transform2Desktop(parameters, target);
}
if (PcMobileTransformer.target === TransformTarget.Mobile) {
v = PcMobileTransformer.transform2Mobile(parameters, target)
v = PcMobileTransformer.transform2Mobile(parameters, target);
}
return v.replace(/\.html/gi, "")
return v.replace(/\.html/gi, "");
}
private static transform2Mobile(
parameters: (string | number)[],
target: TransformDirection
) {
const url = PcMobileTransformer.desktop2MobileMapping.get(target)
const url = PcMobileTransformer.desktop2MobileMapping.get(target);
if (url) {
let targetUrl = `${PcMobileTransformer.host}${url}`
let targetUrl = `${PcMobileTransformer.host}${url}`;
for (let i = 0; i < parameters.length; i++) {
targetUrl = targetUrl.replace(`{${i}}`, parameters[i] + "")
targetUrl = targetUrl.replace(`{${i}}`, parameters[i] + "");
}
return targetUrl
return targetUrl;
}
const base = PcMobileTransformer.rebuildMapping.get(target)
const base = PcMobileTransformer.rebuildMapping.get(target);
if (base) {
let rebuildUrl = base
let rebuildUrl = base;
for (let i = 0; i < parameters.length; i++) {
rebuildUrl = rebuildUrl.replace(`{${i}}`, parameters[i] + "")
rebuildUrl = rebuildUrl.replace(`{${i}}`, parameters[i] + "");
}
return `${PcMobileTransformer.host}${rebuildUrl}`
return `${PcMobileTransformer.host}${rebuildUrl}`;
}
return PcMobileTransformer.host
return PcMobileTransformer.host;
}
private static transform2Desktop(
parameters: (string | number)[],
target: TransformDirection
) {
return target + ""
return target + "";
}
}
......@@ -4,24 +4,24 @@ export class SeoHelper {
title &&
(title.indexOf("TeamMix") > -1 || title.indexOf("亲亲小站") > -1)
) {
return title
return title;
}
return `${title || ""}-亲亲小站`
return `${title || ""}-亲亲小站`;
}
public static updateFavicon(path: string) {
const link = document.querySelector(
"link[rel*='icon']"
) as HTMLLinkElement
) as HTMLLinkElement;
if (link) {
link.href = path
link.href = path;
} else {
const l = document.createElement("link")
l.type = "image/x-icon"
l.rel = "shortcut icon"
l.href = path
document.getElementsByTagName("head")[0].appendChild(l)
const l = document.createElement("link");
l.type = "image/x-icon";
l.rel = "shortcut icon";
l.href = path;
document.getElementsByTagName("head")[0].appendChild(l);
}
}
}
......@@ -39,79 +39,79 @@ export const enum UserAgentType {
export class UserAgentHelper {
private static contains(source: string, match: string) {
return source.indexOf(match) > -1
return source.indexOf(match) > -1;
}
private static containsAll(source: string, matches: string[]) {
for (const item of matches) {
if (!UserAgentHelper.contains(source, item)) {
return false
return false;
}
}
return true
return true;
}
private static containsAny(source: string, matches: string[]) {
for (const item of matches) {
if (UserAgentHelper.contains(source, item)) {
return true
return true;
}
}
return false
return false;
}
public static getType(ua: string) {
const lower = ua.toLowerCase()
const lower = ua.toLowerCase();
if (UserAgentHelper.contains(lower, "ipad")) {
return UserAgentType.IPad
return UserAgentType.IPad;
}
if (UserAgentHelper.contains(lower, "android_tablet")) {
return UserAgentType.Tablet
return UserAgentType.Tablet;
}
if (UserAgentHelper.contains(lower, "iphone")) {
return UserAgentType.IPhone
return UserAgentType.IPhone;
}
if (UserAgentHelper.contains(lower, "ipod")) {
return UserAgentType.IPhone
return UserAgentType.IPhone;
}
if (UserAgentHelper.containsAny(lower, ["android", "mobile"])) {
return UserAgentType.Andriod
return UserAgentType.Andriod;
}
if (UserAgentHelper.contains(lower, "safari")) {
return UserAgentType.DesktopSafari
return UserAgentType.DesktopSafari;
}
if (UserAgentHelper.containsAny(lower, ["edge", "edg"])) {
return UserAgentType.DesktopEdge
return UserAgentType.DesktopEdge;
}
if (UserAgentHelper.contains(lower, "trident")) {
return UserAgentType.DesktopIE
return UserAgentType.DesktopIE;
}
if (UserAgentHelper.contains(lower, "firefox")) {
return UserAgentType.DesktopFirefox
return UserAgentType.DesktopFirefox;
}
if (UserAgentHelper.contains(lower, "chrome")) {
return UserAgentType.DesktopChrome
return UserAgentType.DesktopChrome;
}
if (UserAgentHelper.containsAny(lower, ["360", "baidu", "qq"])) {
return UserAgentType.DesktopOthers
return UserAgentType.DesktopOthers;
}
return UserAgentType.None
return UserAgentType.None;
}
public static isMobile(ua: string) {
const type = UserAgentHelper.getType(ua)
return type === UserAgentType.Andriod || type === UserAgentType.IPhone
const type = UserAgentHelper.getType(ua);
return type === UserAgentType.Andriod || type === UserAgentType.IPhone;
}
}
function b64DecodeUnicode(str: string) {
return decodeURIComponent(
atob(str).replace(/(.)/g, function (m, p) {
let code = p.charCodeAt(0).toString(16).toUpperCase()
let code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = "0" + code
code = "0" + code;
}
return "%" + code
return "%" + code;
})
)
);
}
function base64_url_decode(str: string) {
let output = str.replace(/-/g, "+").replace(/_/g, "/")
let output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break
break;
case 2:
output += "=="
break
output += "==";
break;
case 3:
output += "="
break
output += "=";
break;
default:
throw "Illegal base64url string!"
throw "Illegal base64url string!";
}
try {
return b64DecodeUnicode(output)
return b64DecodeUnicode(output);
} catch {
return atob(output)
return atob(output);
}
}
export function decode(token: string) {
return JSON.parse(base64_url_decode(token.split(".")[1]))
return JSON.parse(base64_url_decode(token.split(".")[1]));
}
const STANDARD = Math.pow(10, 12)
const ONE_DAY_TICKS = 86400000
const STANDARD = Math.pow(10, 12);
const ONE_DAY_TICKS = 86400000;
const ONE_MINUTE_TICKS = 60 * 1000
const ONE_HOUR_TICKS = 60 * 60 * 1000
const ONE_MINUTE_TICKS = 60 * 1000;
const ONE_HOUR_TICKS = 60 * 60 * 1000;
export const enum TimeFormatRule {
/**
......@@ -17,43 +17,43 @@ export const enum TimeFormatRule {
function formatHour2Friendly(hour: number) {
if (hour <= 1) {
return "凌晨"
return "凌晨";
}
if (hour < 12) {
return "上午"
return "上午";
}
if (hour === 12) {
return "中午"
return "中午";
}
if (hour < 20) {
return "下午"
return "下午";
}
return "晚上"
return "晚上";
}
function formatHour12Unit(hour: number, rule: TimeFormatRule) {
if (rule === TimeFormatRule.Hour12) {
return hour > 12 ? hour - 12 : hour
return hour > 12 ? hour - 12 : hour;
}
return hour
return hour;
}
function format2DetailTime(hour: number, time: Date, rule: TimeFormatRule) {
let hourString = ""
const h = formatHour12Unit(hour, rule)
let hourString = "";
const h = formatHour12Unit(hour, rule);
if (h < 10) {
hourString = "0" + h
hourString = "0" + h;
} else {
hourString = h + ""
hourString = h + "";
}
let minuteString = ""
const m = time.getMinutes()
let minuteString = "";
const m = time.getMinutes();
if (m < 10) {
minuteString = "0" + m
minuteString = "0" + m;
} else {
minuteString = "" + m
minuteString = "" + m;
}
return `${formatHour2Friendly(hour)} ${hourString}:${minuteString}`
return `${formatHour2Friendly(hour)} ${hourString}:${minuteString}`;
}
function isSameDay(d1: Date, d2: Date) {
......@@ -61,43 +61,43 @@ function isSameDay(d1: Date, d2: Date) {
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
)
);
}
function isInMinute(d1: Date, d2: Date) {
return Math.abs(d1.valueOf() - d2.valueOf()) <= ONE_MINUTE_TICKS
return Math.abs(d1.valueOf() - d2.valueOf()) <= ONE_MINUTE_TICKS;
}
function isInHour(d1: Date, d2: Date) {
return Math.abs(d1.valueOf() - d2.valueOf()) < ONE_HOUR_TICKS
return Math.abs(d1.valueOf() - d2.valueOf()) < ONE_HOUR_TICKS;
}
function isYesterday(date: Date) {
const now = new Date()
now.setHours(0)
now.setMinutes(0)
now.setSeconds(0)
now.setMilliseconds(0)
const ticks = now.valueOf()
const yesterday = ticks - ONE_DAY_TICKS
const v = date.valueOf()
return v >= yesterday && v < ticks
const now = new Date();
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
now.setMilliseconds(0);
const ticks = now.valueOf();
const yesterday = ticks - ONE_DAY_TICKS;
const v = date.valueOf();
return v >= yesterday && v < ticks;
}
function isIn6Days(date: Date) {
const now = new Date()
now.setHours(0)
now.setMinutes(0)
now.setSeconds(0)
now.setMilliseconds(0)
const ticks = now.valueOf()
const sixDaysAgo = ticks - ONE_DAY_TICKS * 6
const v = date.valueOf()
return v >= sixDaysAgo && v < ticks
const now = new Date();
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
now.setMilliseconds(0);
const ticks = now.valueOf();
const sixDaysAgo = ticks - ONE_DAY_TICKS * 6;
const v = date.valueOf();
return v >= sixDaysAgo && v < ticks;
}
function isSameYear(d1: Date, d2: Date) {
return d1.getFullYear() === d2.getFullYear()
return d1.getFullYear() === d2.getFullYear();
}
const DAY_MAPPING: { [key: number]: string } = {
......@@ -108,39 +108,39 @@ const DAY_MAPPING: { [key: number]: string } = {
4: "四",
5: "五",
6: "六",
}
};
function getDayInWeek(time: Date) {
const d = time.getDay()
return DAY_MAPPING[d]
const d = time.getDay();
return DAY_MAPPING[d];
}
function formatWithTwoNumber(number: number) {
return number < 10 ? `0${number}` : `${number}`
return number < 10 ? `0${number}` : `${number}`;
}
function formatTime2MonthDate(time: Date) {
return `${formatWithTwoNumber(time.getMonth() + 1)}${formatWithTwoNumber(
time.getDate()
)}日`
)}日`;
}
function formatTime2ShortMonthDate(time: Date) {
return `${formatWithTwoNumber(time.getMonth() + 1)}/${formatWithTwoNumber(
time.getDate()
)}`
)}`;
}
function formatTime2YearMonthDate(time: Date) {
return `${time.getFullYear()}${formatWithTwoNumber(
time.getMonth() + 1
)}${formatWithTwoNumber(time.getDate())}日`
)}${formatWithTwoNumber(time.getDate())}日`;
}
function formatTime2ShortYearMonthDate(time: Date) {
return `${time.getFullYear()}/${formatWithTwoNumber(
time.getMonth() + 1
)}/${formatWithTwoNumber(time.getDate())}`
)}/${formatWithTwoNumber(time.getDate())}`;
}
/**
......@@ -160,63 +160,63 @@ export function formatTime(
option = { rule: TimeFormatRule.Hour12, short: false }
) {
if (time < STANDARD) {
time *= 1000
time *= 1000;
}
const t = new Date(time)
const now = new Date()
const t = new Date(time);
const now = new Date();
if (!option.short) {
if (isInMinute(t, now)) {
return "刚刚"
return "刚刚";
}
if (isInHour(t, now)) {
return `${Math.round(
Math.abs(t.valueOf() - now.valueOf()) / 1000 / 60
)}分钟前`
)}分钟前`;
}
}
const hour = t.getHours()
const hour = t.getHours();
if (isSameDay(t, now)) {
return format2DetailTime(hour, t, option.rule)
return format2DetailTime(hour, t, option.rule);
}
if (isYesterday(t)) {
if (option.short) {
return "昨天"
return "昨天";
}
return "昨天 " + format2DetailTime(hour, t, option.rule)
return "昨天 " + format2DetailTime(hour, t, option.rule);
}
if (isIn6Days(t)) {
if (option.short) {
return `星期${getDayInWeek(t)}`
return `星期${getDayInWeek(t)}`;
}
return (
`星期${getDayInWeek(t)} ` + format2DetailTime(hour, t, option.rule)
)
);
}
if (isSameYear(t, now)) {
if (option.short) {
return formatTime2ShortMonthDate(t)
return formatTime2ShortMonthDate(t);
}
return (
formatTime2MonthDate(t) +
" " +
format2DetailTime(hour, t, option.rule)
)
);
}
if (option.short) {
return formatTime2ShortYearMonthDate(t)
return formatTime2ShortYearMonthDate(t);
}
return (
formatTime2YearMonthDate(t) +
" " +
format2DetailTime(hour, t, option.rule)
)
);
}
export default "group"
export default "group";
import xim from "./xim"
import { ChatOption, TokenStringGetter } from "./../model"
import { ChatLoggerService } from "./logger"
import { EmojiService } from "../service/emoji"
import tokenManager from "./token"
import type { UniplatSdk } from "uniplat-sdk"
import type { UniplatSdk } from "uniplat-sdk";
import { EmojiService } from "../service/emoji";
import { ChatOption, TokenStringGetter } from "./../model";
import { ChatLoggerService } from "./logger";
import tokenManager from "./token";
import xim from "./xim";
class Chat {
private _sdk?: () => UniplatSdk
private _orgId: () => string | number = () => "0"
......@@ -11,11 +14,12 @@ class Chat {
private userMapping: { [key: string]: { name: string; avatar: string } } =
{}
private webHost = false
public async setup(option: ChatOption) {
if (!option) {
throw new Error(`You must specify a chat option for chat service`)
throw new Error(`You must specify a chat option for chat service`);
}
// if (!option.userTokenString) {
// throw new Error(`You must specify a user token for chat service`)
......@@ -23,75 +27,75 @@ class Chat {
if (!option.webSocketUri) {
throw new Error(
`You must specify a web socket address for chat service`
)
);
}
this._sdk = option.sdk
this._orgId = option.orgId
this._sdk = option.sdk;
this._orgId = option.orgId;
// if (option.enterpriseTokenString) {
// this.token = option.enterpriseTokenString
// } else {
this.token = option.userTokenString
tokenManager.save(this.token)
this.token = option.userTokenString;
tokenManager.save(this.token);
// }
EmojiService.raiseOnReady(this.token)
return this.initChatSdk(option.webSocketUri)
EmojiService.raiseOnReady(this.token);
return this.initChatSdk(option.webSocketUri);
}
public getSdk = () => {
if(this._sdk == null) return
return this._sdk()
if (this._sdk == null) return;
return this._sdk();
}
public getOrgId = () => {
return this._orgId()
return this._orgId();
}
public isWebHost() {
return this.webHost
return this.webHost;
}
private trimToken(token: string) {
return token.replace(/^Bearer\s/, "")
return token.replace(/^Bearer\s/, "");
}
public async getToken() {
return this.trimToken(await this.token())
return this.trimToken(await this.token());
}
private async initChatSdk(uri: string) {
if (xim.isConnected()) {
return uri
return uri;
}
return new Promise((resolve: (p?: unknown) => void) => {
xim.open(uri, this.token)
this.registerXimEvent(resolve)
})
xim.open(uri, this.token);
this.registerXimEvent(resolve);
});
}
public registerXimEvent(onConnected?: () => void) {
xim.off("status", (e) => this.raiseOnStatusChanged(e, onConnected))
xim.on("status", (e) => this.raiseOnStatusChanged(e, onConnected))
xim.off("status", (e) => this.raiseOnStatusChanged(e, onConnected));
xim.on("status", (e) => this.raiseOnStatusChanged(e, onConnected));
}
private raiseOnStatusChanged(e: any, onConnected?: () => void) {
if (e === "CONNECTED") {
if (onConnected) {
onConnected()
onConnected();
}
}
this.debug(`client status ${e}`)
this.debug(`client status ${e}`);
}
public getUserMapping() {
return {}
return {};
}
private debug(message: string) {
ChatLoggerService.logger?.debug(message)
ChatLoggerService.logger?.debug(message);
}
}
export default new Chat()
export default new Chat();
import { ChatServiceLogger } from "../model"
import { ChatServiceLogger } from "../model";
export class ChatLoggerService {
public static logger = console
......
export interface Chat {
type: string
chat_id: number
service_id: number
title: string
type: string;
chat_id: number;
service_id: number;
title: string;
// tag: string;
// ext: string;
create_time: number
update_time: number
create_time: number;
update_time: number;
// exit_msg_id: number;
// is_exited: boolean;
// dnd: number;
is_top: boolean
is_top: boolean;
// label: string;
// join_msg_id: number;
// last_read_msg_id: number;
......@@ -22,62 +22,62 @@ export interface Chat {
// is_remove: boolean;
// member_type: number;
// ref_id: number;
unread_msg_count: number
unread_msg_count: number;
// at_me: boolean;
// at_all: boolean;
// last_login_oid: string;
last_msg_eid: string
last_msg_name: string
last_msg_ts: number
msg_id: number
msg_type: string
msg: string
last_msg_eid: string;
last_msg_name: string;
last_msg_ts: number;
msg_id: number;
msg_type: string;
msg: string;
}
export interface Message {
chat_id: number
oid: string
eid: string
id: number
ts: number
type: string
msg: string
total_read_count: number
read_count: number
like_count: number
ref_id: number
at_id: string
is_read: boolean
like: boolean
create_time: number
update_time: number
status: number
url: string
is_open: boolean
chat_id: number;
oid: string;
eid: string;
id: number;
ts: number;
type: string;
msg: string;
total_read_count: number;
read_count: number;
like_count: number;
ref_id: number;
at_id: string;
is_read: boolean;
like: boolean;
create_time: number;
update_time: number;
status: number;
url: string;
is_open: boolean;
}
export interface NotifyMessage {
chat_type: string
chat_id: number
eid: string
ts: number
msg_type: string
msg: string
chat_type: string;
chat_id: number;
eid: string;
ts: number;
msg_type: string;
msg: string;
}
export interface Member {
chat_id: number
oid: string
eid: string
type: number
is_exited: boolean
is_remove: boolean
is_top: boolean
label: string
create_time: number
update_time: number
nick_name: string
chat_id: number;
oid: string;
eid: string;
type: number;
is_exited: boolean;
is_remove: boolean;
is_top: boolean;
label: string;
create_time: number;
update_time: number;
nick_name: string;
}
/**
......@@ -119,102 +119,102 @@ export type ChatInputBoxData = { key: string } & (
)
export type TextMessageBody = {
text: string
text: string;
}
export type FileMessageBody = {
name: string
url: string // 生消息是本地path,熟消息是文件url_id
size: number // number, 可选,文件大小:单位-字节
remark: string
isNeedUploaded?: boolean // pc客户端自定义属性(只有生消息的消息体才有该属性, 值为false时不需要上传, true和undefined时需要上传)
name: string;
url: string; // 生消息是本地path,熟消息是文件url_id
size: number; // number, 可选,文件大小:单位-字节
remark: string;
isNeedUploaded?: boolean; // pc客户端自定义属性(只有生消息的消息体才有该属性, 值为false时不需要上传, true和undefined时需要上传)
source?: {
source_type: number //来源类型(若为团队小站文件,则来源类型为团队小站文件)
source_id: number //来源id(若为团队小站文件,此处为团队工作站id)
source_name: string //来源名称(若为团队小站文件,此处传入团队小站名称)
source_icon: string //来源图标(若为团队小站文件,此处传入团队icon的url)
extra?: { [prop: string]: any } //附加信息
}
source_type: number; // 来源类型(若为团队小站文件,则来源类型为团队小站文件)
source_id: number; // 来源id(若为团队小站文件,此处为团队工作站id)
source_name: string; // 来源名称(若为团队小站文件,此处传入团队小站名称)
source_icon: string; // 来源图标(若为团队小站文件,此处传入团队icon的url)
extra?: { [prop: string]: any }; // 附加信息
};
}
export type ImageMessageBody = {
name: string
url: string
size: number
w: number
h: number
thumbnail?: string // 缩略图地址
preview?: string // 预览图地址
remark: string
name: string;
url: string;
size: number;
w: number;
h: number;
thumbnail?: string; // 缩略图地址
preview?: string; // 预览图地址
remark: string;
}
export type VoiceMessageBody = {
name: string
url: string
size: number
duration: number // 语音时间:单位-ms
name: string;
url: string;
size: number;
duration: number; // 语音时间:单位-ms
}
//第一顺序 default_text 第二顺序 operator_text、receiver_text
// 第一顺序 default_text 第二顺序 operator_text、receiver_text
export type NotifyMessageBody = {
operator_id: string
receiver_ids: string[]
default_text: string //默认文本
operator_text: string //操作人文本
receiver_text: string //处理人文本
operator_id: string;
receiver_ids: string[];
default_text: string; // 默认文本
operator_text: string; // 操作人文本
receiver_text: string; // 处理人文本
}
export type TextNoticeMessageBody = {
title: string
text: string
is_at_all: boolean //是否@全部人
title: string;
text: string;
is_at_all: boolean; // 是否@全部人
}
export type VideoMessageBody = {
name: string
url: string // 生消息是本地path,熟消息是文件url_id
size: number
cover?: string
duration: number
w: number
h: number
isNeedUploaded?: boolean // pc客户端自定义属性(只有生消息的消息体才有该属性, 值为false时不需要上传, true和undefined时需要上传)
name: string;
url: string; // 生消息是本地path,熟消息是文件url_id
size: number;
cover?: string;
duration: number;
w: number;
h: number;
isNeedUploaded?: boolean; // pc客户端自定义属性(只有生消息的消息体才有该属性, 值为false时不需要上传, true和undefined时需要上传)
}
export type UrlMessageBody = {
title: string
icon: string
url: string
desc: string
title: string;
icon: string;
url: string;
desc: string;
source?: {
source_type: number //来源类型(若为团队小站文件,则来源类型为团队小站文件)
source_id: number //来源id(若为团队小站文件,此处为团队工作站id)
source_name: string //来源名称(若为团队小站文件,此处传入团队小站名称)
source_icon: string //来源图标(若为团队小站文件,此处传入团队icon的url)
extra?: { [prop: string]: any } //附加信息
}
source_type: number; // 来源类型(若为团队小站文件,则来源类型为团队小站文件)
source_id: number; // 来源id(若为团队小站文件,此处为团队工作站id)
source_name: string; // 来源名称(若为团队小站文件,此处传入团队小站名称)
source_icon: string; // 来源图标(若为团队小站文件,此处传入团队icon的url)
extra?: { [prop: string]: any }; // 附加信息
};
}
export type ForwardMessageBody = {
snap: string
chat_type: string //会话类型
chat_id: number // 会话id
msg_ids: number[] //消息id集合
snap: string;
chat_type: string; // 会话类型
chat_id: number; // 会话id
msg_ids: number[]; // 消息id集合
}
export type QuoteMessageBody = {
text: string
quote_text: string // JSON字符串
quote_msg_type: string
quote_eid: string
quote_msg_id: number //引用id
text: string;
quote_text: string; // JSON字符串
quote_msg_type: string;
quote_eid: string;
quote_msg_id: number; // 引用id
}
export type CommentForwardMessageBody = {
snap: string
channel_id: number //团队工作站id
topic_id: number // 主题id
comment_ids: number[] //评论id集合
snap: string;
channel_id: number; // 团队工作站id
topic_id: number; // 主题id
comment_ids: number[]; // 评论id集合
}
// export type SpecifiedChatRecordMsg = SpecifiedChatRecord & {
......@@ -229,21 +229,21 @@ export type CommentForwardMessageBody = {
// 客服
export interface CsUser {
id: number
oid: string
eid: string
is_deleted: boolean
delete_time: number
delete_oid: string
delete_eid: string
service_id: number
type: number
name: string
mobile: string
email: string
description: string
create_time: number
update_time: number
chat_count: number
status: number
id: number;
oid: string;
eid: string;
is_deleted: boolean;
delete_time: number;
delete_oid: string;
delete_eid: string;
service_id: number;
type: number;
name: string;
mobile: string;
email: string;
description: string;
create_time: number;
update_time: number;
chat_count: number;
status: number;
}
......@@ -5,9 +5,9 @@ export enum UserType {
}
export default interface User {
uid: string
oid: string
eid: string
jwt: string
userType: UserType
}
uid: string;
oid: string;
eid: string;
jwt: string;
userType: UserType;
};;;;;;;;;;
import { ChatOption, TokenStringGetter } from "./../model"
import { ChatOption, TokenStringGetter } from "./../model";
function Token() {
let _token: TokenStringGetter
let _token: TokenStringGetter;
return {
save(token: TokenStringGetter) {
_token = token
_token = token;
},
getToken() {
return _token()
return _token();
},
}
};
}
export default Token()
export default Token();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment