Handle reverse ordering of events
This commit is contained in:
parent
1178d4bb07
commit
14895357a3
2 changed files with 76 additions and 32 deletions
|
|
@ -341,8 +341,12 @@ body {
|
|||
|
||||
.scroll-to-end {
|
||||
position: absolute;
|
||||
top: -64px;
|
||||
bottom: 20px;
|
||||
right: 16px;
|
||||
&.reversed {
|
||||
top: 120px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.op-button {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
:readMarker="readMarker"
|
||||
:recordingMembers="typingMembers"
|
||||
v-on:start-recording="setShowRecorder()"
|
||||
v-on:loadnext="handleScrolledToBottom(false)"
|
||||
v-on:loadprevious="handleScrolledToTop()"
|
||||
v-on:loadnext="handleScrolledToLatest(false)"
|
||||
v-on:loadprevious="handleScrolledToOldest()"
|
||||
v-on:mark-read="sendRR"
|
||||
v-on:sendclap="sendClapReactionAtTime"
|
||||
/>
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
<div v-for="(event, index) in filteredEvents" :key="event.getId()" :eventId="event.getId()">
|
||||
<!-- DAY Marker, shown for every new day in the timeline -->
|
||||
<div v-if="showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
<div v-if="!reverseOrder && showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
|
||||
<div v-if="!event.isRelation() && !event.isRedaction()" :ref="event.getId()">
|
||||
<MessageErrorHandler>
|
||||
|
|
@ -67,7 +67,10 @@
|
|||
touchStart(e, event);
|
||||
}
|
||||
" v-on:touchend="touchEnd" v-on:touchcancel="touchCancel" v-on:touchmove="touchMove">
|
||||
<!-- Note: For threaded media messages, IF there is only one item we show that media item as a single component.
|
||||
|
||||
<div v-if="reverseOrder && event.getId() == readMarker && index > 0" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div>
|
||||
|
||||
<!-- Note: For threaded media messages, IF there is only one item we show that media item as a single component.
|
||||
We might therefore get calls to v-on:context-menu that has the event set to that single media item, not the top level thread event
|
||||
that is really displayed in the flow. Therefore, we rewrite these events with "{event: event, anchor: $event.anchor}",
|
||||
see below. Otherwise things like context menus won't work as designed.
|
||||
|
|
@ -89,24 +92,30 @@
|
|||
/>
|
||||
<!-- <div v-if="debugging" style="user-select:text">EventID: {{ event.getId() }}</div> -->
|
||||
<!-- <div v-if="debugging" style="user-select:text">Event: {{ JSON.stringify(event) }}<br /><br /></div> -->
|
||||
<div v-if="event.getId() == readMarker && index < filteredEvents.length - 1" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div>
|
||||
<div v-if="!reverseOrder && event.getId() == readMarker && index < filteredEvents.length - 1" class="read-marker"><div class="line"></div><div class="text">{{ $t('message.unread_messages') }}</div><div class="line"></div></div>
|
||||
</div>
|
||||
</MessageErrorHandler>
|
||||
</div>
|
||||
|
||||
<!-- Day marker when reverseOrder is set -->
|
||||
<div v-if="reverseOrder && showDayMarkerBeforeEvent(event) && !!componentForEvent(event, isForExport = false)" class="day-marker"><div class="line"></div><div class="text">{{ dayForEvent(event) }}</div><div class="line"></div></div>
|
||||
|
||||
</div>
|
||||
|
||||
<NoHistoryRoomWelcomeHeader v-if="showNoHistoryRoomWelcomeHeader" />
|
||||
|
||||
<!-- "Scroll to end"-button -->
|
||||
<v-btn v-if="!useVoiceMode" :class="{'scroll-to-end': true, 'reversed': reverseOrder}" v-show="showScrollToEnd" fab x-small elevation="0" color="black"
|
||||
@click.stop="scrollToEndOfTimeline">
|
||||
<v-icon color="white">arrow_downward</v-icon>
|
||||
</v-btn>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Input area -->
|
||||
<v-container v-if="!useVoiceMode && !useFileModeNonAdmin && room" fluid :class="['input-area-outer', replyToEvent ? 'reply-to' : '']">
|
||||
<div :class="[replyToEvent ? 'iput-area-inner-box' : '']">
|
||||
<!-- "Scroll to end"-button -->
|
||||
<v-btn v-if="!useVoiceMode" class="scroll-to-end" v-show="showScrollToEnd" fab x-small elevation="0" color="black"
|
||||
@click.stop="scrollToEndOfTimeline">
|
||||
<v-icon color="white">arrow_downward</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-row class="ma-0 pa-0">
|
||||
<div v-if="replyToEvent" class="row">
|
||||
<div class="col">
|
||||
|
|
@ -512,7 +521,8 @@ export default {
|
|||
heartPosition: {
|
||||
top: 0,
|
||||
left: 0
|
||||
}
|
||||
},
|
||||
reverseOrder: false
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -692,12 +702,12 @@ export default {
|
|||
(!e.getPrevContent() || e.getPrevContent().membership != "join") &&
|
||||
e.getStateKey() == this.$matrix.currentUserId) {
|
||||
// Our own join event.
|
||||
return this.events.slice(idx + 1);
|
||||
return this.reverseOrder ? this.events.slice(idx + 1).toReversed() : this.events.slice(idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.events;
|
||||
return this.reverseOrder ? this.events.toReversed() : this.events;
|
||||
},
|
||||
|
||||
roomCreatedByUsRecently() {
|
||||
|
|
@ -1080,7 +1090,7 @@ export default {
|
|||
});
|
||||
} else {
|
||||
// Can't paginate, just scroll to bottom of window!
|
||||
this.smoothScrollToEnd();
|
||||
this.smoothScrollToLatest();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -1150,7 +1160,7 @@ export default {
|
|||
this.$nextTick(() => {
|
||||
const container = this.chatContainer;
|
||||
if (container && container.scrollHeight <= container.clientHeight) {
|
||||
this.handleScrolledToTop();
|
||||
this.handleScrolledToOldest();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
@ -1162,15 +1172,23 @@ export default {
|
|||
const bufferHeight = container.clientHeight * WINDOW_BUFFER_SIZE;
|
||||
if (container.scrollTop <= bufferHeight) {
|
||||
// Scrolled to top
|
||||
this.handleScrolledToTop();
|
||||
if (this.reverseOrder) {
|
||||
this.handleScrolledToLatest(false);
|
||||
} else {
|
||||
this.handleScrolledToOldest();
|
||||
}
|
||||
} else if (container.scrollHeight - container.scrollTop.toFixed(0) - container.clientHeight <= bufferHeight) {
|
||||
this.handleScrolledToBottom(false);
|
||||
if (this.reverseOrder) {
|
||||
this.handleScrolledToOldest();
|
||||
} else {
|
||||
this.handleScrolledToLatest(false);
|
||||
}
|
||||
}
|
||||
|
||||
this.showScrollToEnd =
|
||||
container.scrollHeight === container.clientHeight
|
||||
? false
|
||||
: container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight ||
|
||||
: (this.reverseOrder ? (container.scrollTop.toFixed(0) > 0) : (container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight)) ||
|
||||
(this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS));
|
||||
|
||||
this.restartRRTimer();
|
||||
|
|
@ -1265,11 +1283,14 @@ export default {
|
|||
var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll
|
||||
const container = this.chatContainer;
|
||||
if (container) {
|
||||
if (container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) {
|
||||
if (this.reverseOrder && container.scrollTop.toFixed(0) == 0) {
|
||||
scrollToSeeNew = true;
|
||||
}
|
||||
else if (!this.reverseOrder && container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) {
|
||||
scrollToSeeNew = true;
|
||||
}
|
||||
}
|
||||
this.handleScrolledToBottom(scrollToSeeNew);
|
||||
this.handleScrolledToLatest(scrollToSeeNew);
|
||||
|
||||
// If kick or ban event, redirect to "goodbye"...
|
||||
if (event.getType() === "m.room.member" &&
|
||||
|
|
@ -1494,7 +1515,7 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
handleScrolledToTop() {
|
||||
handleScrolledToOldest() {
|
||||
if (
|
||||
this.timelineWindow &&
|
||||
this.timelineWindow.canPaginate(EventTimeline.BACKWARDS) &&
|
||||
|
|
@ -1505,7 +1526,7 @@ export default {
|
|||
.paginate(EventTimeline.BACKWARDS, 10, true)
|
||||
.then((success) => {
|
||||
if (success && this.scrollPosition) {
|
||||
this.scrollPosition.prepareFor("up");
|
||||
this.scrollPosition.prepareFor(this.reverseOrder ? "down" : "up");
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
this.$nextTick(() => {
|
||||
// restore scroll position!
|
||||
|
|
@ -1520,7 +1541,7 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
handleScrolledToBottom(scrollToEnd) {
|
||||
handleScrolledToLatest(smoothScrollToLatest) {
|
||||
if (
|
||||
this.timelineWindow &&
|
||||
this.timelineWindow.canPaginate(EventTimeline.FORWARDS) &&
|
||||
|
|
@ -1533,13 +1554,13 @@ export default {
|
|||
if (success) {
|
||||
this.setEvents(this.timelineWindow.getEvents());
|
||||
if (!this.useVoiceMode && this.scrollPosition) {
|
||||
this.scrollPosition.prepareFor("down");
|
||||
this.scrollPosition.prepareFor(this.reverseOrder ? "up" : "down");
|
||||
this.$nextTick(() => {
|
||||
// restore scroll position!
|
||||
console.log("Restore scroll!");
|
||||
this.scrollPosition.restore();
|
||||
if (scrollToEnd) {
|
||||
this.smoothScrollToEnd();
|
||||
if (smoothScrollToLatest) {
|
||||
this.smoothScrollToLatest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1555,6 +1576,7 @@ export default {
|
|||
* Scroll so that the given event is at the middle of the chat view (if more events) or else at the bottom.
|
||||
*/
|
||||
scrollToEvent(eventId) {
|
||||
console.log("Scroll to event", eventId);
|
||||
const container = this.chatContainer;
|
||||
const ref = this.$refs[eventId];
|
||||
if (container && ref) {
|
||||
|
|
@ -1562,7 +1584,7 @@ export default {
|
|||
const item = ref[0].getBoundingClientRect();
|
||||
let offsetY = (parent.bottom - parent.top) / 2;
|
||||
if (ref[0].clientHeight > offsetY) {
|
||||
offsetY = Math.max(0, (parent.bottom - parent.top) - ref[0].clientHeight);
|
||||
offsetY = this.reverseOrder ? 0 : Math.max(0, (parent.bottom - parent.top) - ref[0].clientHeight);
|
||||
}
|
||||
const targetY = parent.top + offsetY;
|
||||
const currentY = item.top;
|
||||
|
|
@ -1573,10 +1595,21 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
smoothScrollToEnd() {
|
||||
smoothScrollToLatest() {
|
||||
this.$nextTick(function () {
|
||||
const container = this.chatContainer;
|
||||
if (container && container.children.length > 0) {
|
||||
if (this.reverseOrder) {
|
||||
const firstChild = container.children[0];
|
||||
console.log("Scroll into view", firstChild);
|
||||
window.requestAnimationFrame(() => {
|
||||
firstChild.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "end",
|
||||
inline: "nearest",
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const lastChild = container.children[container.children.length - 1];
|
||||
console.log("Scroll into view", lastChild);
|
||||
window.requestAnimationFrame(() => {
|
||||
|
|
@ -1587,6 +1620,7 @@ export default {
|
|||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -1809,8 +1843,14 @@ export default {
|
|||
const elFirst = util.getFirstVisibleElement(container, (item) => item.hasAttribute("eventId"));
|
||||
const elLast = util.getLastVisibleElement(container, (item) => item.hasAttribute("eventId"));
|
||||
if (elFirst && elLast) {
|
||||
eventIdFirst = elFirst.getAttribute("eventId");
|
||||
eventIdLast = elLast.getAttribute("eventId");
|
||||
if (this.reverseOrder) {
|
||||
// For reverse order, the "first visible" is actually later in time, so swap them
|
||||
eventIdFirst = elLast.getAttribute("eventId");
|
||||
eventIdLast = elFirst.getAttribute("eventId");
|
||||
} else {
|
||||
eventIdFirst = elFirst.getAttribute("eventId");
|
||||
eventIdLast = elLast.getAttribute("eventId");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eventIdFirst && eventIdLast) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue