Regression: React + Blaze reconciliation (#21567)

pull/21597/head^2
Guilherme Gazzo 5 years ago committed by GitHub
parent d6ff6bddb8
commit b226da3849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/e2e/client/tabbar.ts
  2. 5
      app/ui-message/client/message.js
  3. 72
      app/ui-utils/client/lib/RoomManager.js
  4. 3
      app/ui-utils/client/lib/messageContext.js
  5. 44
      app/ui-utils/client/lib/openRoom.js
  6. 23
      app/ui/client/views/app/room.js
  7. 4
      app/utils/lib/RoomTypesCommon.js
  8. 6
      app/utils/lib/roomExit.js
  9. 32
      client/components/Message/Attachments/components/Image.tsx
  10. 173
      client/lib/RoomManager.ts
  11. 2
      client/sidebar/search/Row.js
  12. 13
      client/templates.ts
  13. 2
      client/views/room/Room.stories.js
  14. 2
      client/views/room/Room/Room.js
  15. 14
      client/views/room/Room/RoomWithData.js
  16. 55
      client/views/room/Room/Skeleton.tsx
  17. 2
      client/views/room/components/BlazeTemplate.js
  18. 43
      client/views/room/components/RoomTemplate.js
  19. 44
      client/views/room/components/RoomTemplate/RoomTemplate.tsx
  20. 5
      client/views/room/components/RoomTemplate/slots/Aside.tsx
  21. 5
      client/views/room/components/RoomTemplate/slots/Body.tsx
  22. 5
      client/views/room/components/RoomTemplate/slots/Footer.tsx
  23. 5
      client/views/room/components/RoomTemplate/slots/Header.tsx
  24. 21
      client/views/room/providers/RoomProvider.tsx
  25. 14
      client/views/room/providers/ToolboxProvider.tsx
  26. 4
      client/views/teams/contextualBar/channels/TeamsChannels.js

@ -10,7 +10,7 @@ import { e2e } from './rocketchat.e2e';
addAction('e2e', ({ room }) => {
const e2eEnabled = useSetting('E2E_Enable');
const e2eReady = e2e.isReady() || room.encrypted;
const e2ePermission = room.t === 'd' || usePermission('toggle-room-e2e-encryption', room._id);
const e2ePermission = usePermission('toggle-room-e2e-encryption', room._id) || room.t === 'd';
const hasPermission = usePermission('edit-room', room._id) && e2ePermission && e2eReady;
const toggleE2E = useMethod('saveRoomSettings');

@ -551,6 +551,11 @@ const processSequentials = ({ index, currentNode, settings, forceDate, showDateS
const previousNode = (index === undefined || index > 0) && getPreviousSentMessage(currentNode);
const nextNode = currentNode.nextElementSibling;
if (!previousNode) {
setTimeout(() => {
currentNode.dispatchEvent(new CustomEvent('MessageGroup', { bubbles: true }));
}, 100);
}
if (isSequential(currentNode, previousNode, forceDate, settings.Message_GroupingPeriod, showDateSeparator, shouldCollapseReplies)) {
currentNode.classList.add('sequential');
} else {

@ -4,7 +4,6 @@ import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
import { Blaze } from 'meteor/blaze';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Template } from 'meteor/templating';
import _ from 'underscore';
import { fireGlobalEvent } from './fireGlobalEvent';
@ -19,7 +18,7 @@ import { CachedCollectionManager } from '../../../ui-cached-collection';
import { getConfig } from '../config';
import { ROOM_DATA_STREAM } from '../../../utils/stream/constants';
import { call } from '..';
import { RoomManager as NewRoomManager } from '../../../../client/lib/RoomManager';
const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen')) || 5;
@ -143,23 +142,6 @@ export const RoomManager = new function() {
return Object.keys(openedRooms).map((typeName) => openedRooms[typeName]).find((openedRoom) => openedRoom.rid === rid);
}
getDomOfRoom(typeName, rid, templateName) {
const room = openedRooms[typeName];
if (room == null) {
return;
}
if ((room.dom == null) && (rid != null)) {
room.dom = document.createElement('div');
room.dom.classList.add('room-container');
const contentAsFunc = (content) => () => content;
room.template = Blaze._TemplateWith({ _id: rid }, contentAsFunc(Template[templateName || 'room']));
Blaze.render(room.template, room.dom); // , nextNode, parentView
}
return room.dom;
}
close(typeName) {
if (openedRooms[typeName]) {
@ -185,6 +167,7 @@ export const RoomManager = new function() {
delete openedRooms[typeName];
if (rid != null) {
NewRoomManager.close(rid);
return RoomHistoryManager.clear(rid);
}
}
@ -265,34 +248,29 @@ export const RoomManager = new function() {
return onlineUsers.set(onlineUsersValue);
}
updateMentionsMarksOfRoom(typeName) {
const dom = this.getDomOfRoom(typeName);
if (!dom) {
return;
}
const [ticksBar] = dom.getElementsByClassName('ticks-bar');
const [messagesBox] = dom.getElementsByClassName('messages-box');
const scrollTop = $('> .wrapper', messagesBox).scrollTop() - 50;
const totalHeight = $(' > .wrapper > ul', messagesBox).height() + 40;
if (!ticksBar) {
return;
}
// TODO: thread quotes should NOT have mention links at all
const mentionsSelector = '.message .body .mention-link--me, .message .body .mention-link--group';
ticksBar.innerHTML = Array.from(messagesBox?.querySelectorAll(mentionsSelector) || [])
.map((mentionLink) => {
const topOffset = $(mentionLink).offset().top + scrollTop;
const percent = (100 / totalHeight) * topOffset;
const className = [
'tick',
mentionLink.classList.contains('mention-link--me') && 'tick--me',
mentionLink.classList.contains('mention-link--group') && 'tick--group',
].filter(Boolean).join(' ');
return `<div class="${ className }" style="top: ${ percent }%;"></div>`;
})
.join('');
updateMentionsMarksOfRoom(/* typeName */) {
// const [ticksBar] = dom.getElementsByClassName('ticks-bar');
// const [messagesBox] = dom.getElementsByClassName('messages-box');
// const scrollTop = $('> .wrapper', messagesBox).scrollTop() - 50;
// const totalHeight = $(' > .wrapper > ul', messagesBox).height() + 40;
// if (!ticksBar) {
// return;
// }
// // TODO: thread quotes should NOT have mention links at all
// const mentionsSelector = '.message .body .mention-link--me, .message .body .mention-link--group';
// ticksBar.innerHTML = Array.from(messagesBox?.querySelectorAll(mentionsSelector) || [])
// .map((mentionLink) => {
// const topOffset = $(mentionLink).offset().top + scrollTop;
// const percent = (100 / totalHeight) * topOffset;
// const className = [
// 'tick',
// mentionLink.classList.contains('mention-link--me') && 'tick--me',
// mentionLink.classList.contains('mention-link--group') && 'tick--group',
// ].filter(Boolean).join(' ');
// return `<div class="${ className }" style="top: ${ percent }%;"></div>`;
// })
// .join('');
}
};
Cls.initClass();

@ -31,6 +31,8 @@ export function messageContext({ rid } = Template.instance()) {
}, {
jump: tmid && tmid !== mid && mid && mid,
});
e.preventDefault();
e.stopPropagation();
};
const runAction = Layout.isEmbedded() ? (msg, e) => {
@ -51,6 +53,7 @@ export function messageContext({ rid } = Template.instance()) {
const openDiscussion = (e) => {
e.preventDefault();
e.stopPropagation();
const { drid } = e.currentTarget.dataset;
goToRoomById(drid);
};

@ -3,35 +3,22 @@ import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Session } from 'meteor/session';
import _ from 'underscore';
import { Random } from 'meteor/random';
import { appLayout } from '../../../../client/lib/appLayout';
import { Messages, ChatSubscription, Rooms } from '../../../models';
import { Messages, ChatSubscription } from '../../../models';
import { settings } from '../../../settings';
import { callbacks } from '../../../callbacks';
import { roomTypes } from '../../../utils';
import { call, callMethod } from './callMethod';
import { RoomManager, fireGlobalEvent, RoomHistoryManager } from '..';
import { waitUntilWrapperExists } from './RoomHistoryManager';
import { createTemplateForComponent } from '../../../../client/lib/portals/createTemplateForComponent';
import { RoomManager as NewRoomManager } from '../../../../client/lib/RoomManager';
import { Rooms } from '../../../models/client';
window.currentTracker = undefined;
// cleanup session when hot reloading
Session.set('openedRoom', null);
const replaceCenterDomBy = (dom) => {
const roomNode = dom();
const center = createTemplateForComponent(Random.id(), () => import('../../../../client/views/root/DomNode'), {
attachment: 'at-parent',
props: () => ({ node: roomNode }),
});
appLayout.render('main', { center });
return roomNode;
};
const waitUntilRoomBeInserted = async (type, rid) => new Promise((resolve) => {
Tracker.autorun((c) => {
@ -43,6 +30,12 @@ const waitUntilRoomBeInserted = async (type, rid) => new Promise((resolve) => {
});
});
NewRoomManager.on('changed', (rid) => {
Session.set('openedRoom', rid);
RoomManager.openedRoom = rid;
});
export const openRoom = async function(type, name) {
window.currentTracker && window.currentTracker.stop();
window.currentTracker = Tracker.autorun(async function(c) {
@ -61,20 +54,15 @@ export const openRoom = async function(type, name) {
return FlowRouter.go('direct', { rid: room._id }, FlowRouter.current().queryParams);
}
if (room._id === Session.get('openedRoom') && !FlowRouter.getQueryParam('msg')) {
return;
}
if (RoomManager.open(type + name).ready() !== true) {
if (settings.get('Accounts_AllowAnonymousRead')) {
appLayout.render('main');
}
RoomManager.open(type + name);
appLayout.render('main', { center: 'loading' });
return;
}
appLayout.render('main', { center: 'room' });
appLayout.render('main', { center: 'loading' });
c.stop();
@ -82,13 +70,7 @@ export const openRoom = async function(type, name) {
window.currentTracker = undefined;
}
const roomDom = replaceCenterDomBy(() => RoomManager.getDomOfRoom(type + name, room._id, roomTypes.getConfig(type).mainTemplate));
const selector = await waitUntilWrapperExists('.messages-box .wrapper');
selector.scrollTop = roomDom.oldScrollTop;
Session.set('openedRoom', room._id);
RoomManager.openedRoom = room._id;
NewRoomManager.open(room._id);
fireGlobalEvent('room-opened', _.omit(room, 'usernames'));

@ -29,6 +29,7 @@ import { ChatMessages } from '../../lib/chatMessages';
import { fileUpload } from '../../lib/fileUpload';
import './room.html';
import { getCommonRoomEvents } from './lib/getCommonRoomEvents';
import { RoomManager as NewRoomManager } from '../../../../../client/lib/RoomManager';
export const chatMessages = {};
@ -787,10 +788,30 @@ Meteor.startup(() => {
if (!chatMessages[rid]) {
chatMessages[rid] = new ChatMessages();
}
const wrapper = this.find('.wrapper');
const store = NewRoomManager.getStore(rid);
const afterMessageGroup = () => {
if (store.scroll) {
wrapper.scrollTop = store.scroll;
} else {
this.sendToBottom();
}
wrapper.removeEventListener('MessageGroup', afterMessageGroup);
wrapper.addEventListener('scroll', _.throttle(() => {
store.update({ scroll: wrapper.scrollTop });
}, 100));
};
wrapper.addEventListener('MessageGroup', afterMessageGroup);
chatMessages[rid].initializeWrapper(this.find('.wrapper'));
chatMessages[rid].initializeInput(this.find('.js-input-message'), { rid });
const wrapper = this.find('.wrapper');
const wrapperUl = this.find('.wrapper > ul');
const newMessage = this.find('.new-message');

@ -1,5 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
// import { Session } from 'meteor/session';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { RoomTypeConfig } from './RoomTypeConfig';
@ -47,7 +47,7 @@ export class RoomTypesCommon {
const routeConfig = {
name: roomConfig.route.name,
action: roomConfig.route.action,
triggersExit: [() => Session.set('openedRoom', '')],
// triggersExit: [() => Session.set('openedRoom', '')],
};
if (Meteor.isClient) {

@ -1,5 +1,5 @@
import { Blaze } from 'meteor/blaze';
import { Session } from 'meteor/session';
// import { Session } from 'meteor/session';
import { Tracker } from 'meteor/tracker';
import { FlowRouter } from 'meteor/kadira:flow-router';
@ -25,8 +25,8 @@ export const roomExit = function() {
}
callbacks.run('roomExit');
Session.set('lastOpenedRoom', Session.get('openedRoom'));
Session.set('openedRoom', null);
// Session.set('lastOpenedRoom', Session.get('openedRoom'));
// Session.set('openedRoom', null);
RoomManager.openedRoom = null;
const mainNode = document.querySelector('.main-content');

@ -15,17 +15,20 @@ type ImageProps = {
({ loadImage: true } | { loadImage: false; setLoadImage: () => void });
const getDimensions = (
width: Dimensions['width'],
height: Dimensions['height'],
originalWidth: Dimensions['width'],
originalHeight: Dimensions['height'],
limits: { width: number; height: number },
): { width: 'auto' | number; height: 'auto' | number } => {
const ratio = height / width;
): { width: number; height: number } => {
const widthRatio = originalWidth / (limits.width - 4);
const heightRatio = originalHeight / limits.height;
if (height >= width || Math.min(width, limits.width) * ratio > limits.height) {
return { width: (width * Math.min(height, limits.height)) / height, height: 'auto' };
if (widthRatio > heightRatio) {
const width = Math.min(originalWidth, limits.width - 4);
return { width, height: (width / originalWidth) * originalHeight };
}
return { width: Math.min(width, limits.width), height: 'auto' };
const height = Math.min(originalHeight, limits.height);
return { width: (height / originalHeight) * originalWidth, height };
};
const Image: FC<ImageProps> = ({ previewUrl, loadImage = true, setLoadImage, src, ...size }) => {
@ -46,22 +49,23 @@ const Image: FC<ImageProps> = ({ previewUrl, loadImage = true, setLoadImage, src
const background = previewUrl && `url(${previewUrl}) center center / cover no-repeat fixed`;
if (!loadImage) {
return <Load {...limits} load={setLoadImage} />;
return <Load {...dimensions} {...limits} load={setLoadImage} />;
}
if (error) {
return <Retry retry={setHasNoError} />;
return <Retry {...dimensions} retry={setHasNoError} />;
}
return (
<ImageBox
className='gallery-item'
onError={setHasError}
{...(previewUrl && ({ style: { background } } as any))}
box
{...(previewUrl && ({ style: { background, boxSizing: 'content-box' } } as any))}
{...dimensions}
src={src}
is='img'
/>
is='picture'
>
<img className='gallery-item' src={src} {...dimensions} />
</ImageBox>
);
};

@ -0,0 +1,173 @@
import { Emitter } from '@rocket.chat/emitter';
import { useEffect, useMemo } from 'react';
import { useSubscription, Subscription, Unsubscribe } from 'use-subscription';
import { getConfig } from '../../app/ui-utils/client/config';
import { IRoom } from '../../definition/IRoom';
import { useUserId, useUserRoom, useUserSubscription } from '../contexts/UserContext';
import { useAsyncState } from '../hooks/useAsyncState';
import { AsyncState } from './asyncState';
const debug = !!(getConfig('debug') || getConfig('debug-RoomStore'));
export class RoomStore extends Emitter<{
changed: undefined;
}> {
lastTime?: Date;
scroll?: number;
constructor(readonly rid: string) {
super();
debug && this.on('changed', () => console.log(`RoomStore ${this.rid} changed`, this));
}
update({ scroll, lastTime }: { scroll?: number; lastTime?: Date }): void {
if (scroll !== undefined) {
this.scroll = scroll;
}
if (lastTime !== undefined) {
this.lastTime = lastTime;
}
if (scroll || lastTime) {
this.emit('changed');
}
}
}
const debugRoomManager = !!(getConfig('debug') || getConfig('debug-RoomManager'));
export const RoomManager = new (class RoomManager extends Emitter<{
changed: IRoom['_id'] | undefined;
opened: IRoom['_id'];
closed: IRoom['_id'];
back: IRoom['_id'];
removed: IRoom['_id'];
}> {
private rid: IRoom['_id'] | undefined;
private lastRid: IRoom['_id'] | undefined;
private rooms: Map<IRoom['_id'], RoomStore> = new Map();
constructor() {
super();
debugRoomManager &&
this.on('opened', (rid) => {
console.log('room opened ->', rid);
});
debugRoomManager &&
this.on('back', (rid) => {
console.log('room moved to back ->', rid);
});
debugRoomManager &&
this.on('closed', (rid) => {
console.log('room close ->', rid);
});
}
get lastOpened(): IRoom['_id'] | undefined {
return this.lastRid;
}
get opened(): IRoom['_id'] | undefined {
return this.rid;
}
visitedRooms(): IRoom['_id'][] {
return [...this.rooms.keys()];
}
back(rid: IRoom['_id']): void {
if (rid === this.rid) {
this.lastRid = rid;
this.rid = undefined;
this.emit('back', rid);
}
}
close(rid: IRoom['_id']): void {
if (!this.rooms.has(rid)) {
this.rooms.delete(rid);
this.emit('closed', rid);
}
this.emit('changed', this.rid);
}
open(rid: IRoom['_id']): void {
if (rid === this.rid) {
return;
}
this.back(rid);
if (!this.rooms.has(rid)) {
this.rooms.set(rid, new RoomStore(rid));
}
this.rid = rid;
this.emit('opened', rid);
this.emit('changed', this.rid);
}
getStore(rid: IRoom['_id']): RoomStore | undefined {
return this.rooms.get(rid);
}
})();
const subscribeVistedRooms: Subscription<IRoom['_id'][]> = {
getCurrentValue: () => RoomManager.visitedRooms(),
subscribe(callback) {
return RoomManager.on('changed', callback);
},
};
const subscribeOpenedRoom: Subscription<IRoom['_id'] | undefined> = {
getCurrentValue: () => RoomManager.opened,
subscribe(callback) {
return RoomManager.on('opened', callback);
},
};
const fields = {};
export const useHandleRoom = (rid: IRoom['_id']): AsyncState<IRoom> => {
const { resolve, update, ...state } = useAsyncState<IRoom>();
const uid = useUserId();
const subscription = (useUserSubscription(rid, fields) as unknown) as IRoom;
const _room = (useUserRoom(rid, fields) as unknown) as IRoom;
const room = uid ? subscription || _room : _room;
useEffect(() => {
if (room) {
update();
resolve(room);
}
}, [resolve, update, room]);
return state;
};
export const useVisitedRooms = (): IRoom['_id'][] => useSubscription(subscribeVistedRooms);
export const useOpenedRoom = (): IRoom['_id'] | undefined => useSubscription(subscribeOpenedRoom);
export const useRoomStore = (rid: IRoom['_id']): RoomStore => {
const subscribeStore: Subscription<RoomStore | undefined> = useMemo(
() => ({
getCurrentValue: (): RoomStore | undefined => RoomManager.getStore(rid),
subscribe(callback): Unsubscribe {
return RoomManager.on('changed', callback);
},
}),
[rid],
);
const store = useSubscription(subscribeStore);
if (!store) {
throw new Error('Something wrong');
}
return store;
};

@ -4,7 +4,7 @@ import SideBarItemTemplateWithData from '../RoomList/SideBarItemTemplateWithData
import UserItem from './UserItem';
const Row = ({ item, data }) => {
const { t, SideBarItemTemplate, AvatarTemplate, useRealName, extended } = data;
const { t, SideBarItemTemplate, avatarTemplate: AvatarTemplate, useRealName, extended } = data;
if (item.t === 'd' && !item.u) {
return (

@ -6,11 +6,22 @@ createTemplateForComponent('MessageActions', () => import('./components/Message/
createTemplateForComponent('reactAttachments', () => import('./components/Message/Attachments'));
createTemplateForComponent('ThreadMetric', () => import('./components/Message/Metrics/Thread'));
createTemplateForComponent('ThreadMetric', () => import('./components/Message/Metrics/Thread'), {
renderContainerView: () =>
HTML.DIV({
style: 'min-height: 36px;',
}),
});
createTemplateForComponent(
'DiscussionMetric',
() => import('./components/Message/Metrics/Discussion'),
{
renderContainerView: () =>
HTML.DIV({
style: 'min-height: 36px;',
}),
},
);
createTemplateForComponent(

@ -1,6 +1,6 @@
import React from 'react';
import { RoomTemplate } from './components/RoomTemplate';
import { RoomTemplate } from './components/RoomTemplate/RoomTemplate';
export default {
title: 'views/Room',

@ -5,7 +5,7 @@ import { useTranslation } from '../../../contexts/TranslationContext';
import { useUserPreference } from '../../../contexts/UserContext';
import Header from '../Header';
import BlazeTemplate from '../components/BlazeTemplate';
import { RoomTemplate } from '../components/RoomTemplate';
import { RoomTemplate } from '../components/RoomTemplate/RoomTemplate';
import VerticalBarOldActions from '../components/VerticalBarOldActions';
import { useRoom } from '../providers/RoomProvider';
import {

@ -1,12 +1,16 @@
import React from 'react';
import { useOpenedRoom } from '../../../lib/RoomManager';
import RoomProvider from '../providers/RoomProvider';
import Room from './Room';
const RoomWithData = ({ _id }) => (
<RoomProvider rid={_id}>
<Room />
</RoomProvider>
);
const RoomWithData = () => {
const rid = useOpenedRoom();
return rid ? (
<RoomProvider rid={rid}>
<Room />
</RoomProvider>
) : null;
};
export default RoomWithData;

@ -0,0 +1,55 @@
import { Skeleton, Box, InputBox } from '@rocket.chat/fuselage';
import React, { FC, memo } from 'react';
import Header from '../../../components/Header';
import VerticalBarSkeleton from '../../../components/VerticalBar/VerticalBarSkeleton';
import { RoomTemplate } from '../components/RoomTemplate/RoomTemplate';
const RoomSkeleton: FC = () => (
<RoomTemplate>
<RoomTemplate.Header>
<Header>
<Header.Avatar>
<Skeleton variant='rect' width={36} height={36} />
</Header.Avatar>
<Header.Content>
<Header.Content.Row>
<Skeleton width='10%' />
</Header.Content.Row>
<Header.Content.Row>
<Skeleton width='30%' />
</Header.Content.Row>
</Header.Content>
</Header>
</RoomTemplate.Header>
<RoomTemplate.Body>
<Box display='flex' height='100%' justifyContent='flex-start' flexDirection='column'>
<Box pi='x24' pb='x16' display='flex'>
<Box>
<Skeleton variant='rect' width={36} height={36} />
</Box>
<Box mis='x8' flexGrow={1}>
<Skeleton width='100%' />
<Skeleton width='69%' />
</Box>
</Box>
<Box pi='x24' pb='x16' display='flex'>
<Box>
<Skeleton variant='rect' width={36} height={36} />
</Box>
<Box mis='x8' flexGrow={1}>
<Skeleton width='100%' />
<Skeleton width='40%' />
</Box>
</Box>
</Box>
<Box pi='x24' pb='x16' display='flex'>
<InputBox.Skeleton />
</Box>
</RoomTemplate.Body>
<RoomTemplate.Aside>
<VerticalBarSkeleton />
</RoomTemplate.Aside>
</RoomTemplate>
);
export default memo(RoomSkeleton);

@ -21,7 +21,7 @@ const BlazeTemplate = ({ name, flexShrink, overflow, onClick, children, ...props
view && Blaze.remove(view);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [name]);
}, [name, JSON.stringify(props)]);
return (
<Box
rcx-blaze-template

@ -1,43 +0,0 @@
import { Box } from '@rocket.chat/fuselage';
import React, { memo } from 'react';
import flattenChildren from 'react-keyed-flatten-children';
import VerticalBar from '../../../components/VerticalBar';
export const RoomTemplate = ({ children, ...props }) => {
const c = flattenChildren(children);
const header = c.filter((child) => child.type === RoomTemplate.Header);
const body = c.filter((child) => child.type === RoomTemplate.Body);
const footer = c.filter((child) => child.type === RoomTemplate.Footer);
const aside = c.filter((child) => child.type === RoomTemplate.Aside);
return (
<Box is='main' h='full' display='flex' flexDirection='column' {...props}>
{header.length > 0 && header}
<Box display='flex' flexGrow='1' overflow='hidden' height='full' position='relative'>
<Box display='flex' flexDirection='column' flexGrow='1'>
<Box is='div' display='flex' flexDirection='column' flexGrow='1'>
{body}
</Box>
{footer.length > 0 && <Box is='footer'>{footer}</Box>}
</Box>
{aside.length > 0 && <VerticalBar is='aside'>{aside}</VerticalBar>}
</Box>
</Box>
);
};
RoomTemplate.Header = function Header({ children }) {
return children;
};
RoomTemplate.Body = function Body({ children }) {
return children;
};
RoomTemplate.Footer = function Footer({ children }) {
return children;
};
RoomTemplate.Aside = function Aside({ children }) {
return children;
};
export default memo(RoomTemplate);

@ -0,0 +1,44 @@
import { Box } from '@rocket.chat/fuselage';
import React, { FC } from 'react';
import flattenChildren from 'react-keyed-flatten-children';
import VerticalBar from '../../../../components/VerticalBar/VerticalBar';
import { Aside } from './slots/Aside';
import { Body } from './slots/Body';
import { Footer } from './slots/Footer';
import { Header } from './slots/Header';
export const RoomTemplate: FC & {
Header: FC;
Body: FC;
Footer: FC;
Aside: FC;
} = ({ children, ...props }) => {
const c = flattenChildren(children);
const header = c.filter((child) => (child as any).type === RoomTemplate.Header);
const body = c.filter((child) => (child as any).type === RoomTemplate.Body);
const footer = c.filter((child) => (child as any).type === RoomTemplate.Footer);
const aside = c.filter((child) => (child as any).type === RoomTemplate.Aside);
return (
<Box is='main' h='full' display='flex' flexDirection='column' {...props}>
{header.length > 0 && header}
<Box display='flex' flexGrow={1} overflow='hidden' height='full' position='relative'>
<Box display='flex' flexDirection='column' flexGrow={1}>
<Box is='div' display='flex' flexDirection='column' flexGrow={1}>
{body}
</Box>
{footer.length > 0 && <Box is='footer'>{footer}</Box>}
</Box>
{aside.length > 0 && <VerticalBar is='aside'>{aside}</VerticalBar>}
</Box>
</Box>
);
};
RoomTemplate.Header = Header;
RoomTemplate.Body = Body;
RoomTemplate.Footer = Footer;
RoomTemplate.Aside = Aside;
export default RoomTemplate;

@ -0,0 +1,5 @@
import React, { FC } from 'react';
export const Aside: FC = function Aside({ children }) {
return <>{children}</>;
};

@ -0,0 +1,5 @@
import React, { FC } from 'react';
export const Body: FC = function Body({ children }) {
return <>{children}</>;
};

@ -0,0 +1,5 @@
import React, { FC } from 'react';
export const Footer: FC = function Footer({ children }) {
return <>{children}</>;
};

@ -0,0 +1,5 @@
import React, { FC } from 'react';
export const Header: FC = function Header({ children }) {
return <>{children}</>;
};

@ -1,25 +1,20 @@
import React, { ReactNode, useContext, useMemo } from 'react';
import React, { ReactNode, useContext, useMemo, memo } from 'react';
import { roomTypes } from '../../../../app/utils/client';
import { IRoom } from '../../../../definition/IRoom';
import { useUserId, useUserSubscription } from '../../../contexts/UserContext';
import { useHandleRoom } from '../../../lib/RoomManager';
import { AsyncStatePhase } from '../../../lib/asyncState';
import Skeleton from '../Room/Skeleton';
import { RoomContext } from '../contexts/RoomContext';
import { useUserRoom } from '../hooks/useUserRoom';
import ToolboxProvider from './ToolboxProvider';
const fields = {};
export type Props = {
children: ReactNode;
rid: IRoom['_id'];
};
const RoomProvider = ({ rid, children }: Props): JSX.Element => {
const uid = useUserId();
const subscription = (useUserSubscription(rid, fields) as unknown) as IRoom;
const _room = (useUserRoom(rid, fields) as unknown) as IRoom;
const room = uid ? subscription || _room : _room;
const { phase, value: room } = useHandleRoom(rid);
const context = useMemo(() => {
if (!room) {
return null;
@ -31,8 +26,8 @@ const RoomProvider = ({ rid, children }: Props): JSX.Element => {
};
}, [room, rid]);
if (!room) {
return <></>;
if (phase === AsyncStatePhase.LOADING || !room) {
return <Skeleton />;
}
return (
@ -43,4 +38,4 @@ const RoomProvider = ({ rid, children }: Props): JSX.Element => {
};
export const useRoom = (): undefined | IRoom => useContext(RoomContext)?.room;
export default RoomProvider;
export default memo(RoomProvider);

@ -94,16 +94,12 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom
);
useLayoutEffect(() => {
if (!(currentRoom === room._id)) {
return;
}
if (!tab) {
setActiveTabBar([undefined, undefined]);
}
setActiveTabBar([list.get(tab as string) as ToolboxActionConfig, context]);
}, [tab, list, currentRoom, room._id, context]);
}, [tab, list, currentRoom, context]);
const contextValue = useMemo(
() => ({
@ -129,7 +125,13 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom
(action as ToolboxActionConfig).anonymous),
)
.map(([id, item]) => (
<VirtualAction action={item} room={room} id={id} key={id} handleChange={handleChange} />
<VirtualAction
action={item}
room={room}
id={id}
key={id + room._id}
handleChange={handleChange}
/>
))}
{children}
</ToolboxContext.Provider>

@ -9,7 +9,7 @@ import { roomTypes } from '../../../../../app/utils/client';
import { useSetModal } from '../../../../contexts/ModalContext';
import { useRecordList } from '../../../../hooks/lists/useRecordList';
import { AsyncStatePhase } from '../../../../lib/asyncState';
import CreateChannel from '../../../../sidebar/header/CreateChannel';
import CreateChannelWithData from '../../../../sidebar/header/CreateChannelWithData';
import RoomInfo from '../../../room/contextualBar/Info';
import { useTabBarClose } from '../../../room/providers/ToolboxProvider';
import AddExistingModal from './AddExistingModal';
@ -50,7 +50,7 @@ const TeamsChannels = ({ teamId }) => {
}, []);
const addExisting = useReactModal(AddExistingModal, { teamId, reload });
const createNew = useReactModal(CreateChannel, { teamId, reload });
const createNew = useReactModal(CreateChannelWithData, { teamId, reload });
const goToRoom = useCallback((room) => roomTypes.openRouteLink(room.t, room), []);
const handleBack = useCallback(() => setState({}), [setState]);

Loading…
Cancel
Save