import React from 'react';
import Navigation from '../lib/navigation.js';
const navigate = view => Navigation.navigateTo(view);
export const MESSAGE_COMPOSER_VIEWS = [
'compose-keyboard', 'mentions-select', 'mentions-complete', 'mentions-message',
'emoji-panel', 'emoji-message', 'voice-ready', 'voice-hold', 'voice-recording',
'media-uploading', 'media-gallery', 'media-retry', 'upload-file', 'file-message',
'message-multiselect', 'favorite-success', 'forward-select', 'forward-multiple',
'forward-individual', 'forward-bundle'
];
const MEMBERS = [
{id: 'all', name: '所有人', note: '提醒群内所有成员', avatar: 'campaign'},
{id: 'annie', name: '安妮', note: '产品设计组', image: 'img/bkg/p09.jpg'},
{id: 'lizi', name: '栗子来了', note: '前端开发组', image: 'img/bkg/p15.jpg'},
{id: 'wang', name: '王珊珊', note: '市场运营组', image: 'img/bkg/p14.jpg'},
{id: 'chen', name: '陈晨', note: '项目管理组', image: 'img/bkg/p05.jpg'}
];
const TARGETS = [
{id: 'file', name: '文件助手', note: '仅自己可见', icon: 'folder_special'},
{id: 'team', name: '产品研发群', note: '18人', image: 'img/bkg/p22.jpg'},
{id: 'annie', name: '安妮', note: '在线', image: 'img/bkg/p09.jpg'},
{id: 'lizi', name: '栗子来了', note: '刚刚活跃', image: 'img/bkg/p15.jpg'},
{id: 'wang', name: '王珊珊', note: '市场运营组', image: 'img/bkg/p14.jpg'}
];
const EMOJIS = ['😀', '😄', '😂', '🥰', '😎', '🤔', '👍', '👏', '🎉', '❤️', '🔥', '💡', '✅', '🙏', '💪', '🌟', '📌', '🚀'];
function Icon({children}) {
return {children};
}
function Avatar({item}) {
return
{item.image ?
: {item.icon || item.avatar || 'person'}}
;
}
function Header({title = '产品研发群', subtitle = '18人', onMore}) {
return ;
}
function Toast({message, onDone}) {
React.useEffect(() => {
if (!message) return undefined;
const timer = window.setTimeout(onDone, 2000);
return () => window.clearTimeout(timer);
}, [message, onDone]);
return message ?
{message}
: null;
}
function VoiceBubble({mine = false, duration = 8, unread = false}) {
return
{!mine ?
: null}
{mine ?
: null}
;
}
function TextBubble({mine = false, children, selected, onSelect}) {
return
{selected !== undefined ?
: null}
{!mine ?
: null}
{children}
{mine ?
: null}
;
}
function ImageBubble({progress, failed = false, selected, onSelect}) {
return
{selected !== undefined ?
: null}

{progress < 100 || failed ?
{failed ? 'refresh' : 'cloud_upload'}{failed ? '上传失败,点击重试' : `${progress}%`}{!failed ?
: null}
: null}
;
}
function VideoBubble() {
return ;
}
function FileBubble({progress = 100, name = '产品需求说明书.pdf', selected, onSelect}) {
return
{selected !== undefined ?
: null}
PDF{name}2.4 MB · {progress < 100 ? `上传中 ${progress}%` : '已发送'}{progress < 100 ?
: null}
{progress < 100 ? 'close' : 'download'}
;
}
function BaseTimeline({mode, uploadProgress, selected, toggleSelected}) {
const selectable = mode === 'message-multiselect' || mode === 'favorite-success';
return
今天 10:24
上午的评审结论已经整理好了,大家有空看一下。
toggleSelected('text')}>好的,我补充一下交互细节。
{mode === 'mentions-message' ? @安妮 @栗子来了 下午三点一起确认新版流程。 : null}
{['media-uploading', 'media-gallery', 'media-retry'].includes(mode) ? <>
{mode === 'media-gallery' ? <>> : null}
> : null}
{['file-message', 'message-multiselect', 'favorite-success'].includes(mode) ? toggleSelected('file')} /> : null}
{selectable ? toggleSelected('image')} /> : null}
;
}
function EmojiPanel({onEmoji, onDelete}) {
return
{EMOJIS.map(emoji => )}
;
}
function Composer({mode, draft, setDraft, onSend, setMode}) {
const voice = ['voice-ready', 'voice-hold', 'voice-recording'].includes(mode);
const emoji = ['emoji-panel', 'emoji-message'].includes(mode);
const keyDown = event => {
if (event.key === '@' && mode !== 'mentions-message') window.setTimeout(() => navigate('mentions-select'), 0);
};
return <>
{emoji ? setDraft(draft + value)} onDelete={() => setDraft(draft.slice(0, -1))} /> : null}
>;
}
function VoiceOverlay({cancel = false}) {
return
{cancel ? 'delete_outline' : 'mic'}
{[2, 5, 8, 12, 9, 6, 11, 4].map((height, index) => )}
{cancel ? '松开手指,取消发送' : '手指上滑,取消发送'}00:08
;
}
function MemberPicker({complete}) {
const [query, setQuery] = React.useState('');
const [selected, setSelected] = React.useState(complete ? ['annie', 'lizi'] : []);
const visible = MEMBERS.filter(item => item.name.includes(query) || item.note.includes(query));
const toggle = id => setSelected(values => values.includes(id) ? values.filter(value => value !== id) : values.concat(id));
return
选择提醒人
群成员
{visible.map(item =>
)}
;
}
function UploadFilePage({onToast}) {
const [files, setFiles] = React.useState([{name: '产品需求说明书.pdf', size: '2.4 MB', progress: 64}]);
React.useEffect(() => {
if (files.every(file => file.progress >= 100)) return undefined;
const timer = window.setTimeout(() => setFiles(items => items.map(file => ({...file, progress: Math.min(100, file.progress + 6)}))), 300);
return () => window.clearTimeout(timer);
}, [files]);
return ;
}
function ForwardPage({mode, onToast}) {
const multi = mode !== 'forward-select';
const confirm = mode === 'forward-individual' || mode === 'forward-bundle';
const [selected, setSelected] = React.useState(() => {
if (!confirm) return [];
try {
return JSON.parse(sessionStorage.getItem('im-forward-targets')) || ['team', 'annie'];
} catch (_) {
return ['team', 'annie'];
}
});
const [query, setQuery] = React.useState('');
const [comment, setComment] = React.useState('');
const toggle = id => setSelected(values => values.includes(id) ? values.filter(value => value !== id) : multi ? values.concat(id).slice(0, 9) : [id]);
const visible = TARGETS.filter(item => item.name.includes(query) || item.note.includes(query));
return
{confirm ? (mode === 'forward-bundle' ? '合并转发' : '逐条转发') : '选择转发对象'}
{!confirm ? : }
{!confirm ? <>
最近会话
{visible.map(item =>
)}
{multi ?
: null}> :
发送给
{TARGETS.filter(item => selected.includes(item.id)).map(item =>
{item.name})}
{mode === 'forward-bundle' ? '产品研发群的聊天记录' : '将逐条发送 3 条消息'}
栗子来了:上午的评审结论已经整理好了
我:好的,我补充一下交互细节。
我:[图片]
}
;
}
export default function MessageComposerView({view}) {
const [mode, setMode] = React.useState(view);
const [draft, setDraftState] = React.useState(() => sessionStorage.getItem('im-message-composer-draft') || (view === 'emoji-message' ? '收到啦 ' : ''));
const [uploadProgress, setUploadProgress] = React.useState(view === 'media-uploading' ? 22 : 100);
const [selected, setSelected] = React.useState(view === 'favorite-success' ? ['text', 'file', 'image'] : ['text', 'image']);
const [toast, setToast] = React.useState(view === 'favorite-success' ? '已收藏 3 条消息' : '');
const setDraft = value => {setDraftState(value); sessionStorage.setItem('im-message-composer-draft', value);};
React.useEffect(() => {
if (mode !== 'media-uploading' || uploadProgress >= 100) return undefined;
const timer = window.setTimeout(() => setUploadProgress(value => Math.min(100, value + 5)), 350);
return () => window.clearTimeout(timer);
}, [mode, uploadProgress]);
if (view === 'mentions-select' || view === 'mentions-complete') return ;
if (view === 'upload-file') return <> setToast('')} />>;
if (['forward-select', 'forward-multiple', 'forward-individual', 'forward-bundle'].includes(view)) return <> setToast('')} />>;
const toggleSelected = id => setSelected(values => values.includes(id) ? values.filter(value => value !== id) : values.concat(id));
const recording = mode === 'voice-hold' || mode === 'voice-recording';
const multiselect = mode === 'message-multiselect' || mode === 'favorite-success';
return
setToast('会话设置')} />
{multiselect ? 已选择 {selected.length} 条
: null}
{multiselect ? : {if (!draft.trim()) return; setToast('消息已发送'); setDraft('');}} />}
{recording ? : null}
setToast('')} />
;
}