Handle message timestamp taps with Nav API
Very similar to Ib045b8f. Removes more references to page.show, adds 'messageHash' field to getUrlForChange, and utilizes it in the change view. Change-Id: I496d731ceeba57843d617fd5b978cd377ea39787
This commit is contained in:
parent
b28b504f8a
commit
9c1a3db8e4
@ -587,6 +587,7 @@ limitations under the License.
|
|||||||
change-comments="[[_changeComments]]"
|
change-comments="[[_changeComments]]"
|
||||||
project-name="[[_change.project]]"
|
project-name="[[_change.project]]"
|
||||||
show-reply-buttons="[[_loggedIn]]"
|
show-reply-buttons="[[_loggedIn]]"
|
||||||
|
on-message-anchor-tap="_handleMessageAnchorTap"
|
||||||
on-reply="_handleMessageReply"></gr-messages-list>
|
on-reply="_handleMessageReply"></gr-messages-list>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[!_showMessagesView]]">
|
<template is="dom-if" if="[[!_showMessagesView]]">
|
||||||
|
@ -44,6 +44,8 @@
|
|||||||
|
|
||||||
const TRAILING_WHITESPACE_REGEX = /[ \t]+$/gm;
|
const TRAILING_WHITESPACE_REGEX = /[ \t]+$/gm;
|
||||||
|
|
||||||
|
const MSG_PREFIX = '#message-';
|
||||||
|
|
||||||
const ReloadToastMessage = {
|
const ReloadToastMessage = {
|
||||||
NEWER_REVISION: 'A newer patch set has been uploaded',
|
NEWER_REVISION: 'A newer patch set has been uploaded',
|
||||||
RESTORED: 'This change has been restored',
|
RESTORED: 'This change has been restored',
|
||||||
@ -720,10 +722,17 @@
|
|||||||
this.viewState.numFilesShown = numFilesShown;
|
this.viewState.numFilesShown = numFilesShown;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_handleMessageAnchorTap(e) {
|
||||||
|
const hash = MSG_PREFIX + e.detail.id;
|
||||||
|
const url = Gerrit.Nav.getUrlForChange(this._change,
|
||||||
|
this._patchRange.patchNum, this._patchRange.basePatchNum,
|
||||||
|
this._editMode, hash);
|
||||||
|
history.replaceState(null, '', url);
|
||||||
|
},
|
||||||
|
|
||||||
_maybeScrollToMessage(hash) {
|
_maybeScrollToMessage(hash) {
|
||||||
const msgPrefix = '#message-';
|
if (hash.startsWith(MSG_PREFIX)) {
|
||||||
if (hash.startsWith(msgPrefix)) {
|
this.messagesList.scrollToMessage(hash.substr(MSG_PREFIX.length));
|
||||||
this.messagesList.scrollToMessage(hash.substr(msgPrefix.length));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -94,6 +94,20 @@ limitations under the License.
|
|||||||
return element.getComputedStyleValue(cssParam);
|
return element.getComputedStyleValue(cssParam);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test('_handleMessageAnchorTap', () => {
|
||||||
|
element._changeNum = '1';
|
||||||
|
element._patchRange = {
|
||||||
|
basePatchNum: 'PARENT',
|
||||||
|
patchNum: 1,
|
||||||
|
};
|
||||||
|
const getUrlStub = sandbox.stub(Gerrit.Nav, 'getUrlForChange');
|
||||||
|
const replaceStateStub = sandbox.stub(history, 'replaceState');
|
||||||
|
element._handleMessageAnchorTap({detail: {id: 'a12345'}});
|
||||||
|
|
||||||
|
assert.equal(getUrlStub.lastCall.args[4], '#message-a12345');
|
||||||
|
assert.isTrue(replaceStateStub.called);
|
||||||
|
});
|
||||||
|
|
||||||
suite('keyboard shortcuts', () => {
|
suite('keyboard shortcuts', () => {
|
||||||
test('S should toggle the CL star', () => {
|
test('S should toggle the CL star', () => {
|
||||||
const starStub = sandbox.stub(element.$.changeStar, 'toggleStar');
|
const starStub = sandbox.stub(element.$.changeStar, 'toggleStar');
|
||||||
|
@ -132,9 +132,12 @@ limitations under the License.
|
|||||||
right: var(--default-horizontal-margin);
|
right: var(--default-horizontal-margin);
|
||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
.date {
|
span.date {
|
||||||
color: var(--deemphasized-text-color);
|
color: var(--deemphasized-text-color);
|
||||||
}
|
}
|
||||||
|
span.date:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
.dateContainer iron-icon {
|
.dateContainer iron-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -227,12 +230,12 @@ limitations under the License.
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[message.id]]">
|
<template is="dom-if" if="[[message.id]]">
|
||||||
<a class="date" href$="[[_computeMessageHash(message)]]" on-tap="_handleLinkTap">
|
<span class="date" on-tap="_handleAnchorTap">
|
||||||
<gr-date-formatter
|
<gr-date-formatter
|
||||||
has-tooltip
|
has-tooltip
|
||||||
show-date-and-time
|
show-date-and-time
|
||||||
date-str="[[message.date]]"></gr-date-formatter>
|
date-str="[[message.date]]"></gr-date-formatter>
|
||||||
</a>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<iron-icon
|
<iron-icon
|
||||||
id="expandToggle"
|
id="expandToggle"
|
||||||
|
@ -23,18 +23,18 @@
|
|||||||
Polymer({
|
Polymer({
|
||||||
is: 'gr-message',
|
is: 'gr-message',
|
||||||
|
|
||||||
/**
|
|
||||||
* Fired when this message's permalink is tapped.
|
|
||||||
*
|
|
||||||
* @event scroll-to
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fired when this message's reply link is tapped.
|
* Fired when this message's reply link is tapped.
|
||||||
*
|
*
|
||||||
* @event reply
|
* @event reply
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the message's timestamp is tapped.
|
||||||
|
*
|
||||||
|
* @event message-anchor-tap
|
||||||
|
*/
|
||||||
|
|
||||||
listeners: {
|
listeners: {
|
||||||
tap: '_handleTap',
|
tap: '_handleTap',
|
||||||
},
|
},
|
||||||
@ -223,22 +223,12 @@
|
|||||||
return classes.join(' ');
|
return classes.join(' ');
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeMessageHash(message) {
|
_handleAnchorTap(e) {
|
||||||
return '#message-' + message.id;
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleLinkTap(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
this.dispatchEvent(new CustomEvent('message-anchor-tap', {
|
||||||
this.fire('scroll-to', {message: this.message}, {bubbles: false});
|
bubbles: true,
|
||||||
|
detail: {id: this.message.id},
|
||||||
const hash = this._computeMessageHash(this.message);
|
}));
|
||||||
// Don't add the hash to the window history if it's already there.
|
|
||||||
// Otherwise you mess up expected back button behavior.
|
|
||||||
if (window.location.hash == hash) { return; }
|
|
||||||
// Change the URL but don’t trigger a nav event. Otherwise it will
|
|
||||||
// reload the page.
|
|
||||||
page.show(window.location.pathname + hash, null, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleReplyTap(e) {
|
_handleReplyTap(e) {
|
||||||
|
@ -164,6 +164,24 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('clicking on date link fires event', () => {
|
||||||
|
element.message = {
|
||||||
|
type: 'REVIEWER_UPDATE',
|
||||||
|
updated: '2016-01-12 20:24:49.448000000',
|
||||||
|
reviewer: {},
|
||||||
|
id: '47c43261_55aa2c41',
|
||||||
|
};
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
const stub = sinon.stub();
|
||||||
|
element.addEventListener('message-anchor-tap', stub);
|
||||||
|
const dateEl = element.$$('.date');
|
||||||
|
assert.ok(dateEl);
|
||||||
|
MockInteractions.tap(dateEl);
|
||||||
|
|
||||||
|
assert.isTrue(stub.called);
|
||||||
|
assert.deepEqual(stub.lastCall.args[0].detail, {id: element.message.id});
|
||||||
|
});
|
||||||
|
|
||||||
test('votes', () => {
|
test('votes', () => {
|
||||||
element.message = {
|
element.message = {
|
||||||
author: {},
|
author: {},
|
||||||
|
@ -109,7 +109,7 @@ limitations under the License.
|
|||||||
hide-automated="[[_hideAutomated]]"
|
hide-automated="[[_hideAutomated]]"
|
||||||
project-name="[[projectName]]"
|
project-name="[[projectName]]"
|
||||||
show-reply-button="[[showReplyButtons]]"
|
show-reply-button="[[showReplyButtons]]"
|
||||||
on-scroll-to="_handleScrollTo"
|
on-message-anchor-tap="_handleAnchorTap"
|
||||||
label-extremes="[[_labelExtremes]]"
|
label-extremes="[[_labelExtremes]]"
|
||||||
data-message-id$="[[message.id]]"></gr-message>
|
data-message-id$="[[message.id]]"></gr-message>
|
||||||
</template>
|
</template>
|
||||||
|
@ -184,8 +184,8 @@
|
|||||||
this.handleExpandCollapse(!this._expanded);
|
this.handleExpandCollapse(!this._expanded);
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleScrollTo(e) {
|
_handleAnchorTap(e) {
|
||||||
this.scrollToMessage(e.detail.message.id);
|
this.scrollToMessage(e.detail.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
_hasAutomatedMessages(messages) {
|
_hasAutomatedMessages(messages) {
|
||||||
|
@ -33,6 +33,8 @@ limitations under the License.
|
|||||||
// also be provided.
|
// also be provided.
|
||||||
// - `edit`, optional, Boolean: whether or not to load the file list with
|
// - `edit`, optional, Boolean: whether or not to load the file list with
|
||||||
// edit controls.
|
// edit controls.
|
||||||
|
// - `messageHash`, optional, String: the hash of the change message to
|
||||||
|
// scroll to.
|
||||||
//
|
//
|
||||||
// - Gerrit.Nav.View.SEARCH:
|
// - Gerrit.Nav.View.SEARCH:
|
||||||
// - `query`, optional, String: the literal search query. If provided,
|
// - `query`, optional, String: the literal search query. If provided,
|
||||||
@ -352,9 +354,11 @@ limitations under the License.
|
|||||||
* @param {number|string=} opt_basePatchNum The string 'PARENT' can be
|
* @param {number|string=} opt_basePatchNum The string 'PARENT' can be
|
||||||
* used for none.
|
* used for none.
|
||||||
* @param {boolean=} opt_isEdit
|
* @param {boolean=} opt_isEdit
|
||||||
|
* @param {string=} opt_messageHash
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getUrlForChange(change, opt_patchNum, opt_basePatchNum, opt_isEdit) {
|
getUrlForChange(change, opt_patchNum, opt_basePatchNum, opt_isEdit,
|
||||||
|
opt_messageHash) {
|
||||||
if (opt_basePatchNum === PARENT_PATCHNUM) {
|
if (opt_basePatchNum === PARENT_PATCHNUM) {
|
||||||
opt_basePatchNum = undefined;
|
opt_basePatchNum = undefined;
|
||||||
}
|
}
|
||||||
@ -368,6 +372,7 @@ limitations under the License.
|
|||||||
basePatchNum: opt_basePatchNum,
|
basePatchNum: opt_basePatchNum,
|
||||||
edit: opt_isEdit,
|
edit: opt_isEdit,
|
||||||
host: change.internalHost || undefined,
|
host: change.internalHost || undefined,
|
||||||
|
messageHash: opt_messageHash,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -395,6 +395,9 @@
|
|||||||
} else if (params.edit) {
|
} else if (params.edit) {
|
||||||
suffix += ',edit';
|
suffix += ',edit';
|
||||||
}
|
}
|
||||||
|
if (params.messageHash) {
|
||||||
|
suffix += params.messageHash;
|
||||||
|
}
|
||||||
if (params.project) {
|
if (params.project) {
|
||||||
const encodedProject = this.encodeURL(params.project, true);
|
const encodedProject = this.encodeURL(params.project, true);
|
||||||
return `/c/${encodedProject}/+/${params.changeNum}${suffix}`;
|
return `/c/${encodedProject}/+/${params.changeNum}${suffix}`;
|
||||||
|
@ -281,6 +281,9 @@ limitations under the License.
|
|||||||
paramsWithQuery.basePatchNum = 5;
|
paramsWithQuery.basePatchNum = 5;
|
||||||
assert.equal(element._generateUrl(paramsWithQuery),
|
assert.equal(element._generateUrl(paramsWithQuery),
|
||||||
'/c/test/+/1234/5..10?revert&foo=bar');
|
'/c/test/+/1234/5..10?revert&foo=bar');
|
||||||
|
|
||||||
|
params.messageHash = '#123';
|
||||||
|
assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10#123');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('change with repo name encoding', () => {
|
test('change with repo name encoding', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user