sforkowany z mirror/soapbox
Merge branch 'chats-ff' into 'develop'
Chats: FireFox fixes Closes #399 See merge request soapbox-pub/soapbox-fe!218better-thread-display
commit
8c137b0c3c
|
@ -13,8 +13,6 @@ import { escape, throttle } from 'lodash';
|
||||||
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
||||||
import Bundle from 'soapbox/features/ui/components/bundle';
|
import Bundle from 'soapbox/features/ui/components/bundle';
|
||||||
|
|
||||||
const scrollBottom = (elem) => elem.scrollHeight - elem.offsetHeight - elem.scrollTop;
|
|
||||||
|
|
||||||
const makeEmojiMap = record => record.get('emojis', ImmutableList()).reduce((map, emoji) => {
|
const makeEmojiMap = record => record.get('emojis', ImmutableList()).reduce((map, emoji) => {
|
||||||
return map.set(`:${emoji.get('shortcode')}:`, emoji);
|
return map.set(`:${emoji.get('shortcode')}:`, emoji);
|
||||||
}, ImmutableMap());
|
}, ImmutableMap());
|
||||||
|
@ -45,12 +43,13 @@ class ChatMessageList extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
initialLoad: true,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToBottom = () => {
|
scrollToBottom = () => {
|
||||||
if (!this.messagesEnd) return;
|
if (!this.messagesEnd) return;
|
||||||
this.messagesEnd.scrollIntoView();
|
this.messagesEnd.scrollIntoView(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMessageEndRef = (el) => {
|
setMessageEndRef = (el) => {
|
||||||
|
@ -82,22 +81,45 @@ class ChatMessageList extends ImmutablePureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isNearBottom = () => {
|
||||||
|
const elem = this.node;
|
||||||
|
if (!elem) return false;
|
||||||
|
|
||||||
|
const scrollBottom = elem.scrollHeight - elem.offsetHeight - elem.scrollTop;
|
||||||
|
return scrollBottom < elem.offsetHeight * 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { dispatch, chatId } = this.props;
|
const { dispatch, chatId } = this.props;
|
||||||
dispatch(fetchChatMessages(chatId));
|
dispatch(fetchChatMessages(chatId));
|
||||||
|
|
||||||
this.node.addEventListener('scroll', this.handleScroll);
|
this.node.addEventListener('scroll', this.handleScroll);
|
||||||
|
this.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
getSnapshotBeforeUpdate(prevProps, prevState) {
|
||||||
|
const { scrollHeight, scrollTop } = this.node;
|
||||||
|
return scrollHeight - scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreScrollPosition = (scrollBottom) => {
|
||||||
|
this.lastComputedScroll = this.node.scrollHeight - scrollBottom;
|
||||||
|
this.node.scrollTop = this.lastComputedScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState, scrollBottom) {
|
||||||
|
const { initialLoad } = this.state;
|
||||||
const oldCount = prevProps.chatMessages.count();
|
const oldCount = prevProps.chatMessages.count();
|
||||||
const newCount = this.props.chatMessages.count();
|
const newCount = this.props.chatMessages.count();
|
||||||
const isNearBottom = scrollBottom(this.node) < 150;
|
const isNearBottom = this.isNearBottom();
|
||||||
const historyAdded = prevProps.chatMessages.getIn([-1, 'id']) !== this.props.chatMessages.getIn([-1, 'id']);
|
const historyAdded = prevProps.chatMessages.getIn([0, 'id']) !== this.props.chatMessages.getIn([0, 'id']);
|
||||||
|
|
||||||
|
// Retain scroll bar position when loading old messages
|
||||||
|
this.restoreScrollPosition(scrollBottom);
|
||||||
|
|
||||||
if (oldCount !== newCount) {
|
if (oldCount !== newCount) {
|
||||||
if (isNearBottom) this.scrollToBottom();
|
if (isNearBottom || initialLoad) this.scrollToBottom();
|
||||||
if (historyAdded) this.setState({ isLoading: false });
|
if (historyAdded) this.setState({ isLoading: false, initialLoad: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,13 +129,20 @@ class ChatMessageList extends ImmutablePureComponent {
|
||||||
|
|
||||||
handleLoadMore = () => {
|
handleLoadMore = () => {
|
||||||
const { dispatch, chatId, chatMessages } = this.props;
|
const { dispatch, chatId, chatMessages } = this.props;
|
||||||
const maxId = chatMessages.getIn([-1, 'id']);
|
const maxId = chatMessages.getIn([0, 'id']);
|
||||||
dispatch(fetchChatMessages(chatId, maxId));
|
dispatch(fetchChatMessages(chatId, maxId));
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleScroll = throttle(() => {
|
handleScroll = throttle(() => {
|
||||||
if (this.node.scrollTop < 150 && !this.state.isLoading) this.handleLoadMore();
|
const { lastComputedScroll } = this;
|
||||||
|
const { isLoading, initialLoad } = this.state;
|
||||||
|
const { scrollTop, offsetHeight } = this.node;
|
||||||
|
const computedScroll = lastComputedScroll === scrollTop;
|
||||||
|
const nearTop = scrollTop < offsetHeight * 2;
|
||||||
|
|
||||||
|
if (nearTop && !isLoading && !initialLoad && !computedScroll)
|
||||||
|
this.handleLoadMore();
|
||||||
}, 150, {
|
}, 150, {
|
||||||
trailing: true,
|
trailing: true,
|
||||||
});
|
});
|
||||||
|
@ -126,15 +155,17 @@ class ChatMessageList extends ImmutablePureComponent {
|
||||||
const attachment = chatMessage.get('attachment');
|
const attachment = chatMessage.get('attachment');
|
||||||
if (!attachment) return null;
|
if (!attachment) return null;
|
||||||
return (
|
return (
|
||||||
<Bundle fetchComponent={MediaGallery}>
|
<div className='chat-message__media'>
|
||||||
{Component => (
|
<Bundle fetchComponent={MediaGallery}>
|
||||||
<Component
|
{Component => (
|
||||||
media={ImmutableList([attachment])}
|
<Component
|
||||||
height={120}
|
media={ImmutableList([attachment])}
|
||||||
onOpenMedia={this.onOpenMedia}
|
height={120}
|
||||||
/>
|
onOpenMedia={this.onOpenMedia}
|
||||||
)}
|
/>
|
||||||
</Bundle>
|
)}
|
||||||
|
</Bundle>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +190,6 @@ class ChatMessageList extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='chat-messages' ref={this.setRef}>
|
<div className='chat-messages' ref={this.setRef}>
|
||||||
<div style={{ float: 'left', clear: 'both' }} ref={this.setMessageEndRef} />
|
|
||||||
{chatMessages.map(chatMessage => (
|
{chatMessages.map(chatMessage => (
|
||||||
<div
|
<div
|
||||||
className={classNames('chat-message', {
|
className={classNames('chat-message', {
|
||||||
|
@ -181,6 +211,7 @@ class ChatMessageList extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
<div style={{ float: 'left', clear: 'both' }} ref={this.setMessageEndRef} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutabl
|
||||||
const initialState = ImmutableMap();
|
const initialState = ImmutableMap();
|
||||||
|
|
||||||
const idComparator = (a, b) => {
|
const idComparator = (a, b) => {
|
||||||
if (a < b) return 1;
|
if (a < b) return -1;
|
||||||
if (a > b) return -1;
|
if (a > b) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -99,18 +99,12 @@
|
||||||
.chat-messages {
|
.chat-messages {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message {
|
.chat-message {
|
||||||
padding: 7px 10px;
|
margin: 14px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-top: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__bubble {
|
&__bubble {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
@ -225,9 +219,10 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
|
color: var(--primary-text-color--faint);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 8px;
|
top: calc(50% - 13px);
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
|
@ -237,7 +232,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-box__send .icon-button {
|
.chat-box__send .icon-button {
|
||||||
top: 12px;
|
top: calc(50% - 9px);
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
|
@ -347,8 +342,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-message__media {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-message .media-gallery {
|
.chat-message .media-gallery {
|
||||||
height: auto !important;
|
height: 100% !important;
|
||||||
margin: 4px 0 8px;
|
margin: 4px 0 8px;
|
||||||
|
|
||||||
.spoiler-button {
|
.spoiler-button {
|
||||||
|
@ -358,7 +357,7 @@
|
||||||
.media-gallery__item:not(.media-gallery__item--image) {
|
.media-gallery__item:not(.media-gallery__item--image) {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
width: 120px !important;
|
width: 120px !important;
|
||||||
height: 70px !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__preview {
|
&__preview {
|
||||||
|
|
Ładowanie…
Reference in New Issue