feat: 增加功能,在新窗口打开会话

pull/13/head
moonrailgun 3 years ago
parent 11d0411537
commit ce1f9af030

@ -126,7 +126,7 @@ export {
useGroupTextPanelUnread,
} from './redux/hooks/useGroup';
export { useUserInfo, useUserId } from './redux/hooks/useUserInfo';
export { userActions, groupActions } from './redux/slices';
export { userActions, groupActions, uiActions } from './redux/slices';
export type { ChatConverseState } from './redux/slices/chat';
export { setupRedux } from './redux/setup';
export { createStore } from './redux/store';

@ -2,11 +2,13 @@ import { combineReducers } from '@reduxjs/toolkit';
import { userReducer } from './user';
import { chatReducer } from './chat';
import { groupReducer } from './group';
import { uiReducer } from './ui';
export const appReducer = combineReducers({
user: userReducer,
chat: chatReducer,
group: groupReducer,
ui: uiReducer,
});
export type AppState = ReturnType<typeof appReducer>;
@ -14,3 +16,4 @@ export type AppState = ReturnType<typeof appReducer>;
export { userActions } from './user';
export { chatActions } from './chat';
export { groupActions } from './group';
export { uiActions } from './ui';

@ -0,0 +1,28 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface UIState {
panelWinUrls: string[];
}
const initialState: UIState = {
panelWinUrls: [],
};
const uiSlice = createSlice({
name: 'ui',
initialState,
reducers: {
addPanelWindowUrl(state, action: PayloadAction<{ url: string }>) {
const panelUrl = action.payload.url;
state.panelWinUrls.push(panelUrl);
},
deletePanelWindowUrl(state, action: PayloadAction<{ url: string }>) {
const panelUrl = action.payload.url;
const index = state.panelWinUrls.indexOf(panelUrl);
state.panelWinUrls.splice(index, 1);
},
},
});
export const uiActions = uiSlice.actions;
export const uiReducer = uiSlice.reducer;

@ -2,7 +2,7 @@ import { ChatBox } from '@/components/ChatBox';
import { UserListItem } from '@/components/UserListItem';
import { Icon } from '@iconify/react';
import { Button, Tooltip } from 'antd';
import React from 'react';
import React, { useState } from 'react';
import {
ChatConverseState,
t,
@ -13,6 +13,8 @@ import { CommonPanelWrapper } from '../common/Wrapper';
import _compact from 'lodash/compact';
import { openModal } from '@/components/Modal';
import { AppendDMConverseMembers } from '@/components/modals/AppendDMConverseMembers';
import { openInNewWindow, panelWindowManager } from '@/utils/window-helper';
import { usePanelWindow } from '@/hooks/usePanelWindow';
const ConversePanelTitle: React.FC<{ converse: ChatConverseState }> =
React.memo(({ converse }) => {
@ -44,6 +46,17 @@ export const ConversePanel: React.FC<ConversePanelProps> = React.memo(
(state) => state.chat.converses[converseId]
);
const { hasOpenedPanel, openPanelWindow, closePanelWindow } =
usePanelWindow(`/panel/personal/converse/${converseId}`);
if (hasOpenedPanel) {
return (
<div>
<div>{t('面板已在独立窗口打开')}</div>
<Button onClick={closePanelWindow}>{t('关闭独立窗口')}</Button>
</div>
);
}
return (
<CommonPanelWrapper
header={converse && <ConversePanelTitle converse={converse} />}
@ -53,6 +66,14 @@ export const ConversePanel: React.FC<ConversePanelProps> = React.memo(
}
return _compact([
<Tooltip key="open" title={t('在新窗口打开')}>
<Button
icon={
<Icon className="anticon text-2xl" icon="mdi:dock-window" />
}
onClick={openPanelWindow}
/>
</Tooltip>,
<Tooltip key="add" title={t('邀请成员')}>
<Button
icon={

@ -0,0 +1,39 @@
import { panelWindowManager } from '@/utils/window-helper';
import { useCallback } from 'react';
import { uiActions, useAppDispatch, useAppSelector } from 'tailchat-shared';
/**
*
* 使hooks redux panelWindowManager
*/
export function usePanelWindow(panelUrl: string) {
const hasOpenedPanel = useAppSelector(
(state) =>
/**
*
*/
state.ui.panelWinUrls.includes(panelUrl) &&
panelWindowManager.has(panelUrl)
);
const dispatch = useAppDispatch();
const openPanelWindow = useCallback(() => {
panelWindowManager.open(panelUrl, {
onClose() {
dispatch(uiActions.deletePanelWindowUrl({ url: panelUrl }));
},
});
dispatch(uiActions.addPanelWindowUrl({ url: panelUrl }));
}, [panelUrl]);
const closePanelWindow = useCallback(() => {
panelWindowManager.close(panelUrl);
dispatch(uiActions.deletePanelWindowUrl({ url: panelUrl }));
}, [panelUrl]);
return {
hasOpenedPanel,
openPanelWindow,
closePanelWindow,
};
}

@ -0,0 +1,14 @@
import { buildWindowFeatures } from '../window-helper';
describe('window-helper', () => {
describe('buildWindowFeatures', () => {
test.each([
[{ foo: 'bar' }, 'foo=bar'],
[{ foo: 'bar', baz: 'qux' }, 'foo=bar,baz=qux'],
[{ foo: 'bar', baz: 1 }, 'foo=bar,baz=1'],
[{ foo: 'bar', baz: true }, 'foo=bar,baz=true'],
])('%p => %s', (input, output) => {
expect(buildWindowFeatures(input)).toBe(output);
});
});
});

@ -0,0 +1,107 @@
/**
*
*/
/**
*
*
*/
let windowIndex = 0;
export function buildWindowFeatures(
options: Record<string, string | number | boolean>
): string {
return Object.entries(options)
.map(([key, val]) => `${key}=${val}`)
.join(',');
}
/**
*
* @param url
*/
export function openInNewWindow(url: string) {
const width = 414;
const height = 736;
const top = (window.screen.height - height) / 2;
const left = (window.screen.width - width) / 2;
// 打开窗口
const win = window.open(
url,
`tailwindow#${windowIndex++}`,
buildWindowFeatures({
top,
left,
width,
height,
menubar: false,
toolbar: false,
location: false,
status: false,
resizable: true,
})
);
return win;
}
class PanelWindowManager {
openedPanelWindows: Record<string, Window> = {};
/**
*
* @param url Url
*/
open(url: string, options?: { onClose: () => void }): void {
if (this.openedPanelWindows[url]) {
this.openedPanelWindows[url].focus();
return;
}
const win = openInNewWindow(url);
if (!win) {
return;
}
const timer = setInterval(() => {
// 目前没有好的办法可以检测打开的窗口是否被直接关闭
// 轮询检测是否被关闭
if (win.closed) {
// 窗口已经被用户关闭
delete this.openedPanelWindows[url];
clearInterval(timer);
if (typeof options?.onClose === 'function') {
// 触发回调
options?.onClose();
}
}
}, 1000);
this.openedPanelWindows[url] = win;
}
/**
*
* @param url url
*/
close(url: string): void {
if (!this.openedPanelWindows[url]) {
return;
}
this.openedPanelWindows[url].close();
delete this.openedPanelWindows[url];
}
/**
*
* @param url Url
*/
has(url: string): boolean {
return !!this.openedPanelWindows[url];
}
}
export const panelWindowManager = new PanelWindowManager();
Loading…
Cancel
Save