Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/message/__tests__/renderMessages-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ describe('renderMessages', () => {
const messageList = renderMessages(messages, narrow);

expect(messageList).toHaveLength(2);
expect(messageList[0].data[0].key).toEqual('time123');
expect(messageList[1].data[0].key).toEqual(12345);
expect(messageList[1].key).toEqual('header12345');
expect(messageList[1].data[0].key).toEqual('time123');
expect(messageList[1].data[1].key).toEqual(12345);
});

test('several messages in same stream, from same person result in time row, header for the stream, three messages, only first of which is full detail', () => {
Expand Down Expand Up @@ -62,8 +63,9 @@ describe('renderMessages', () => {

const messageKeys = messageList[1].data.map(x => x.key);
const messageBriefs = messageList[1].data.map(x => x.isBrief);
expect(messageKeys).toEqual([1, 2, 3]);
expect(messageBriefs).toEqual([false, true, true]);
expect(messageList[1].key).toEqual('header1');
expect(messageKeys).toEqual(['time123', 1, 2, 3]);
expect(messageBriefs).toEqual([undefined, false, true, true]);
});

test('several messages in same stream, from different people result in time row, header for the stream, three messages, only all full detail', () => {
Expand Down Expand Up @@ -101,8 +103,9 @@ describe('renderMessages', () => {

const messageKeys = messageList[1].data.map(x => x.key);
const messageBriefs = messageList[1].data.map(x => x.isBrief);
expect(messageKeys).toEqual([1, 2, 3]);
expect(messageBriefs).toEqual([false, false, false]);
expect(messageList[1].key).toEqual('header1');
expect(messageKeys).toEqual(['time123', 1, 2, 3]);
expect(messageBriefs).toEqual([undefined, false, false, false]);
});

test('private messages between two people, results in time row, header and two full messages', () => {
Expand All @@ -129,7 +132,8 @@ describe('renderMessages', () => {

const messageKeys = messageList[1].data.map(x => x.key);
const messageBriefs = messageList[1].data.map(x => x.isBrief);
expect(messageKeys).toEqual([1, 2]);
expect(messageBriefs).toEqual([false, false]);
expect(messageList[1].key).toEqual('header1');
expect(messageKeys).toEqual(['time123', 1, 2]);
expect(messageBriefs).toEqual([undefined, false, false]);
});
});
16 changes: 8 additions & 8 deletions src/message/renderMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ export default (
messages.forEach(item => {
const diffDays =
prevItem && !isSameDay(new Date(prevItem.timestamp * 1000), new Date(item.timestamp * 1000));
if (!prevItem || diffDays) {
sections[sections.length - 1].data.push({
key: `time${item.timestamp}`,
type: 'time',
timestamp: item.timestamp,
firstMessage: item,
});
}
const diffRecipient = !isSameRecipient(prevItem, item);
if (showHeader && diffRecipient) {
sections.push({
Expand All @@ -31,6 +23,14 @@ export default (
data: [],
});
}
if (!prevItem || diffDays) {
sections[sections.length - 1].data.push({
key: `time${item.timestamp}`,
type: 'time',
timestamp: item.timestamp,
firstMessage: item,
});
}
const shouldGroupWithPrev =
!diffRecipient
&& !diffDays
Expand Down
3 changes: 2 additions & 1 deletion src/webview/MessageList.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import renderMessagesAsHtml from './html/renderMessagesAsHtml';
import { getUpdateEvents } from './webViewHandleUpdates';
import { handleMessageListEvent } from './webViewEventHandlers';
import { base64Utf8Encode } from '../utils/encoding';
import { isPrivateOrGroupNarrow } from '../utils/narrow';

// ESLint doesn't notice how `this.props` escapes, and complains about some
// props not being used here.
Expand Down Expand Up @@ -174,7 +175,7 @@ class MessageList extends Component<Props> {
} = this.props;
const messagesHtml = renderMessagesAsHtml(backgroundData, narrow, renderedMessages);
const { auth } = backgroundData;
const html = getHtml(messagesHtml, theme, {
const html = getHtml(messagesHtml, theme, !isPrivateOrGroupNarrow(narrow), {
anchor,
auth,
showMessagePlaceholders,
Expand Down
42 changes: 25 additions & 17 deletions src/webview/css/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BRAND_COLOR } from '../../styles';
import cssEmojis from './cssEmojis';
import cssNight from './cssNight';

const cssBase = `
const cssBase = (hasRecipientHeaders: boolean) => `
html {
-webkit-user-select: none; /* Safari 3.1+ */
-moz-user-select: none; /* Firefox 2+ */
Expand Down Expand Up @@ -76,24 +76,32 @@ hr {
justify-content: space-between;
margin-bottom: 6px;
}
#date-pill-sticky {
position: fixed;
top: ${hasRecipientHeaders ? '2.3em' : '0.3em'};
left: 50%;
z-index: 100;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2), 0 2px 4px 0 rgba(0, 0, 0, 0.2);
transform: translateX(-50%);
transition: top 0.2s;
}
#date-pill-sticky.hide {
top: -2.5em;
}
#date-pill-sticky:empty {
display: none;
}
.timerow {
text-align: center;
color: hsl(0, 0%, 60%);
display: flex;
align-items: center;
padding: 8px 0;
}
.timerow-left,
.timerow-right {
flex: 1;
height: 1px;
margin: 8px;
}
.timerow-left {
background: -webkit-linear-gradient(left, transparent 10%, hsl(0, 0%, 60%) 100%);
.date-pill, #date-pill-sticky {
color: hsla(0, 0%, 0%, 0.65);
background: hsl(0, 0%, 92%);
border-radius: 3px;
}
.timerow-right {
background: -webkit-linear-gradient(left, hsl(0, 0%, 60%) 0%, transparent 90%);
.date-pill, #date-pill-sticky {
padding: 0.25em 0.5em;
}
.message,
.loading {
Expand Down Expand Up @@ -177,7 +185,7 @@ hr {
position: -webkit-sticky;
position: sticky;
top: -1px;
z-index: 100;
z-index: 10;
display: flex;
justify-content: space-between;
}
Expand Down Expand Up @@ -494,9 +502,9 @@ blockquote {
}
`;

export default (theme: ThemeName) => `
export default (theme: ThemeName, hasRecipientHeaders: boolean) => `
<style>
${cssBase}
${cssBase(hasRecipientHeaders)}
${theme === 'night' ? cssNight : ''}
${cssEmojis}
</style>
Expand Down
4 changes: 4 additions & 0 deletions src/webview/css/cssNight.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ body {
.timestamp {
background: hsl(212, 28%, 25%);
}
.date-pill, #date-pill-sticky {
color: hsla(0, 0%, 100%, 0.5);
background: hsl(213, 14%, 34%);
}
.highlight {
background-color: hsla(51, 100%, 64%, 0.42);
}
Expand Down
9 changes: 7 additions & 2 deletions src/webview/html/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ type InitOptionsType = {|
showMessagePlaceholders: boolean,
|};

export default (content: string, theme: ThemeName, initOptions: InitOptionsType) => template`
export default (
content: string,
theme: ThemeName,
hasRecipientHeaders: boolean,
initOptions: InitOptionsType,
) => template`
$!${script(initOptions.anchor, initOptions.auth)}
$!${css(theme)}
$!${css(theme, hasRecipientHeaders)}

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<body style="overflow-x: hidden;">
Expand Down
1 change: 1 addition & 0 deletions src/webview/html/renderMessagesAsHtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default (
renderedMessages: RenderedSectionDescriptor[],
): string => {
const pieces = [];
pieces.push('<div id="date-pill-sticky"></div>');
renderedMessages.forEach(section => {
pieces.push(messageHeaderAsHtml(backgroundData, narrow, section.message));
section.data.forEach(item => {
Expand Down
4 changes: 1 addition & 3 deletions src/webview/html/timeRowAsHtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { humanDate } from '../../utils/date';

export default (timestamp: number, nextMessage: Message | Outbox): string => template`
<div class="timerow" data-msg-id="${nextMessage.id}">
<div class="timerow-left"></div>
${humanDate(new Date(timestamp * 1000))}
<div class="timerow-right"></div>
<span class="date-pill">${humanDate(new Date(timestamp * 1000))}</span>
</div>
`;
75 changes: 65 additions & 10 deletions src/webview/js/generatedEs3.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,38 +121,36 @@ function midMessagePeer(top, bottom) {
return midElements[midElements.length - 3];
}

function walkToMessage(start, step) {
function walkToElement(start, elementType, step) {
var element = start;

while (element && !element.classList.contains('message')) {
while (element && !element.classList.contains(elementType)) {
element = element[step];
}

return element;
}

function firstMessage() {
return walkToMessage(documentBody.firstElementChild, 'nextElementSibling');
return walkToElement(documentBody.firstElementChild, 'message', 'nextElementSibling');
}

function lastMessage() {
return walkToMessage(documentBody.lastElementChild, 'previousElementSibling');
return walkToElement(documentBody.lastElementChild, 'message', 'previousElementSibling');
}

var minOverlap = 20;

function isVisible(element, top, bottom) {
function isVisible(element, top, bottom, minOverlap) {
var rect = element.getBoundingClientRect();
return top + minOverlap < rect.bottom && rect.top + minOverlap < bottom;
}

function someVisibleMessage(top, bottom) {
function checkVisible(candidate) {
return candidate && isVisible(candidate, top, bottom) ? candidate : null;
return candidate && isVisible(candidate, top, bottom, 20) ? candidate : null;
}

var midPeer = midMessagePeer(top, bottom);
return checkVisible(walkToMessage(midPeer, 'previousElementSibling')) || checkVisible(walkToMessage(midPeer, 'nextElementSibling')) || checkVisible(firstMessage()) || checkVisible(lastMessage());
return checkVisible(walkToElement(midPeer, 'message', 'previousElementSibling')) || checkVisible(walkToElement(midPeer, 'message', 'nextElementSibling')) || checkVisible(firstMessage()) || checkVisible(lastMessage());
}

function idFromMessage(element) {
Expand All @@ -174,7 +172,7 @@ function visibleMessageIds() {
function walkElements(start, step) {
var element = start;

while (element && isVisible(element, top, bottom)) {
while (element && isVisible(element, top, bottom, 20)) {
if (element.classList.contains('message')) {
var id = idFromMessage(element);
first = Math.min(first, id);
Expand All @@ -194,6 +192,29 @@ function visibleMessageIds() {
};
}

function getFirstVisibleMessage() {
var header = document.getElementsByClassName('header')[0];
var top = header ? header.offsetHeight : 0;
var bottom = viewportHeight;
var message = document.createElement('null');

function walkElements(start) {
var element = start;

while (element && isVisible(element, top, bottom, 0)) {
if (element.classList.contains('message') || element.classList.contains('header')) {
message = element;
}

element = element.previousElementSibling;
}
}

var start = someVisibleMessage(top, bottom);
walkElements(start);
return message;
}

var getMessageNode = function getMessageNode(node) {
var curNode = node;

Expand Down Expand Up @@ -246,6 +267,39 @@ var sendScrollMessage = function sendScrollMessage() {
prevMessageRange = messageRange;
};

var dateTimeout;

var handleStickyDatePill = function handleStickyDatePill() {
var firstVisibleMessage = getFirstVisibleMessage();
var timerowAbove = walkToElement(firstVisibleMessage, 'timerow', 'previousElementSibling');

if (!(firstVisibleMessage && timerowAbove)) {
return;
}

var replaceableDate = timerowAbove.getElementsByClassName('date-pill')[0].innerHTML;
var datePillSticky = document.getElementById('date-pill-sticky');

if (!datePillSticky) {
throw new Error('No date-pill-sticky element!');
}

if (!replaceableDate) {
throw new Error('No date-pill element in timerow!');
}

datePillSticky.classList.remove('hide');

if (datePillSticky.innerHTML !== replaceableDate) {
datePillSticky.innerHTML = replaceableDate;
}

clearTimeout(dateTimeout);
dateTimeout = setTimeout(function () {
return datePillSticky.classList.add('hide');
}, 1000);
};

var sendScrollMessageIfListShort = function sendScrollMessageIfListShort() {
if (documentBody.scrollHeight === documentBody.clientHeight) {
sendScrollMessage();
Expand Down Expand Up @@ -592,6 +646,7 @@ documentBody.addEventListener('touchcancel', function (e) {
clearTimeout(longPressTimeout);
});
documentBody.addEventListener('touchmove', function (e) {
handleStickyDatePill();
clearTimeout(longPressTimeout);
});
documentBody.addEventListener('drag', function (e) {
Expand Down
Loading