fiora/packages/web/src/socket.ts
2021-07-28 16:52:53 +08:00

273 lines
7.5 KiB
TypeScript

import IO from 'socket.io-client';
import platform from 'platform';
import convertMessage from '@fiora/utils/convertMessage';
import getFriendId from '@fiora/utils/getFriendId';
import config from '@fiora/config/client';
import notification from './utils/notification';
import voice from './utils/voice';
import { initOSS } from './utils/uploadFile';
import playSound from './utils/playSound';
import { Message, Linkman } from './state/reducer';
import {
ActionTypes,
SetLinkmanPropertyPayload,
AddLinkmanHistoryMessagesPayload,
AddLinkmanMessagePayload,
DeleteMessagePayload,
} from './state/action';
import {
guest,
loginByToken,
getLinkmanHistoryMessages,
getLinkmansLastMessagesV2,
} from './service';
import store from './state/store';
const { dispatch } = store;
const options = {
// reconnectionDelay: 1000,
};
const socket = IO(config.server, options);
async function loginFailback() {
const defaultGroup = await guest(
platform.os?.family,
platform.name,
platform.description,
);
if (defaultGroup) {
const { messages } = defaultGroup;
dispatch({
type: ActionTypes.SetGuest,
payload: defaultGroup,
});
messages.forEach(convertMessage);
dispatch({
type: ActionTypes.AddLinkmanHistoryMessages,
payload: {
linkmanId: defaultGroup._id,
messages,
},
});
}
}
socket.on('connect', async () => {
dispatch({ type: ActionTypes.Connect, payload: '' });
await initOSS();
dispatch({ type: ActionTypes.Ready, payload: '' });
const token = window.localStorage.getItem('token');
if (token) {
const user = await loginByToken(
token,
platform.os?.family,
platform.name,
platform.description,
);
if (user) {
dispatch({
type: ActionTypes.SetUser,
payload: user,
});
const linkmanIds = [
...user.groups.map((group: any) => group._id),
...user.friends.map((friend: any) =>
getFriendId(friend.from, friend.to._id),
),
];
const linkmanMessages = await getLinkmansLastMessagesV2(linkmanIds);
Object.values(linkmanMessages).forEach(
// @ts-ignore
({ messages }: { messages: Message[] }) => {
messages.forEach(convertMessage);
},
);
dispatch({
type: ActionTypes.SetLinkmansLastMessages,
payload: linkmanMessages,
});
return;
}
}
loginFailback();
});
socket.on('disconnect', () => {
// @ts-ignore
dispatch({ type: ActionTypes.Disconnect, payload: null });
});
let windowStatus = 'focus';
window.onfocus = () => {
windowStatus = 'focus';
};
window.onblur = () => {
windowStatus = 'blur';
};
let prevFrom: string | null = '';
let prevName = '';
socket.on('message', async (message: any) => {
convertMessage(message);
const state = store.getState();
const isSelfMessage = message.from._id === state.user?._id;
if (isSelfMessage && message.from.tag !== state.user?.tag) {
dispatch({
type: ActionTypes.UpdateUserInfo,
payload: {
tag: message.from.tag,
},
});
}
const linkman = state.linkmans[message.to];
let title = '';
if (linkman) {
dispatch({
type: ActionTypes.AddLinkmanMessage,
payload: {
linkmanId: message.to,
message,
} as AddLinkmanMessagePayload,
});
if (linkman.type === 'group') {
title = `${message.from.username}${linkman.name} 对大家说:`;
} else {
title = `${message.from.username} 对你说:`;
}
} else {
// 联系人不存在并且是自己发的消息, 不创建新联系人
if (isSelfMessage) {
return;
}
const newLinkman = {
_id: getFriendId(state.user?._id as string, message.from._id),
type: 'temporary',
createTime: Date.now(),
avatar: message.from.avatar,
name: message.from.username,
messages: [],
unread: 1,
};
dispatch({
type: ActionTypes.AddLinkman,
payload: {
linkman: (newLinkman as unknown) as Linkman,
focus: false,
},
});
title = `${message.from.username} 对你说:`;
const messages = await getLinkmanHistoryMessages(newLinkman._id, 0);
if (messages) {
dispatch({
type: ActionTypes.AddLinkmanHistoryMessages,
payload: {
linkmanId: newLinkman._id,
messages,
} as AddLinkmanHistoryMessagesPayload,
});
}
}
if (windowStatus === 'blur' && state.status.notificationSwitch) {
notification(
title,
message.from.avatar,
message.type === 'text'
? message.content.replace(/&lt;/g, '<').replace(/&gt;/g, '>')
: `[${message.type}]`,
Math.random().toString(),
);
}
if (state.status.soundSwitch) {
const soundType = state.status.sound;
playSound(soundType);
}
if (state.status.voiceSwitch) {
if (message.type === 'text') {
const text = message.content
.replace(
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g,
'',
)
.replace(/#/g, '');
if (text.length > 100) {
return;
}
const from =
linkman && linkman.type === 'group'
? `${message.from.username}${
linkman.name === prevName ? '' : `${linkman.name}`
}`
: `${message.from.username}对你说`;
if (text) {
voice.push(
from !== prevFrom ? from + text : text,
message.from.username,
);
}
prevFrom = from;
prevName = message.from.username;
} else if (message.type === 'system') {
voice.push(message.from.originUsername + message.content, '');
prevFrom = null;
}
}
});
socket.on(
'changeGroupName',
({ groupId, name }: { groupId: string; name: string }) => {
dispatch({
type: ActionTypes.SetLinkmanProperty,
payload: {
linkmanId: groupId,
key: 'name',
value: name,
} as SetLinkmanPropertyPayload,
});
},
);
socket.on('deleteGroup', ({ groupId }: { groupId: string }) => {
dispatch({
type: ActionTypes.RemoveLinkman,
payload: groupId,
});
});
socket.on('changeTag', (tag: string) => {
dispatch({
type: ActionTypes.UpdateUserInfo,
payload: {
tag,
},
});
});
socket.on(
'deleteMessage',
({ linkmanId, messageId }: { linkmanId: string; messageId: string }) => {
dispatch({
type: ActionTypes.DeleteMessage,
payload: {
linkmanId,
messageId,
} as DeleteMessagePayload,
});
},
);
export default socket;