Merge changes I6f812fb5,Iffcb629d,I718d11da,Ie916fbb7

* changes:
  Fix bug where a Promise was not returned by the shared cache
  Add user links to nav
  Add logged-out nav menu
  Respect changes-per-page preference in change-list
This commit is contained in:
Dave Borowitz
2016-03-10 06:09:36 +00:00
committed by Gerrit Code Review
11 changed files with 292 additions and 27 deletions

View File

@@ -66,7 +66,7 @@ limitations under the License.
<gr-ajax
auto
url="/changes/"
params="[[_computeQueryParams(_query, _offset)]]"
params="[[_computeQueryParams(_query, _offset, changesPerPage)]]"
last-response="{{_changes}}"
last-error="{{_lastError}}"
loading="{{_loading}}"></gr-ajax>
@@ -80,10 +80,11 @@ limitations under the License.
selected-index="{{viewState.selectedChangeIndex}}"
show-star="[[loggedIn]]"></gr-change-list>
<nav>
<a href$="[[_computeNavLink(_query, _offset, -1)]]"
<a href$="[[_computeNavLink(_query, _offset, -1, changesPerPage)]]"
hidden$="[[_hidePrevArrow(_offset)]]">&larr; Prev</a>
<a href$="[[_computeNavLink(_query, _offset, 1)]]"
hidden$="[[_hideNextArrow(_changes.length)]]">Next &rarr;</a>
<a href$="[[_computeNavLink(_query, _offset, 1, changesPerPage)]]"
hidden$="[[_hideNextArrow(_changes.length, changesPerPage)]]">
Next &rarr;</a>
</nav>
</div>
</template>

View File

@@ -14,8 +14,6 @@
(function() {
'use strict';
var DEFAULT_NUM_CHANGES = 25;
Polymer({
is: 'gr-change-list-view',
@@ -51,6 +49,8 @@
value: function() { return {}; },
},
changesPerPage: Number,
/**
* Currently active query.
*/
@@ -103,13 +103,13 @@
this.fire('title-change', {title: this._query});
},
_computeQueryParams: function(query, offset) {
_computeQueryParams: function(query, offset, changesPerPage) {
var options = this.listChangesOptionsToHex(
this.ListChangesOption.LABELS,
this.ListChangesOption.DETAILED_ACCOUNTS
);
var obj = {
n: DEFAULT_NUM_CHANGES, // Number of results to return.
n: changesPerPage,
O: options,
S: offset || 0,
};
@@ -119,10 +119,10 @@
return obj;
},
_computeNavLink: function(query, offset, direction) {
_computeNavLink: function(query, offset, direction, changesPerPage) {
// Offset could be a string when passed from the router.
offset = +(offset || 0);
var newOffset = Math.max(0, offset + (25 * direction));
var newOffset = Math.max(0, offset + (changesPerPage * direction));
var href = '/q/' + query;
if (newOffset > 0) {
href += ',' + newOffset;
@@ -142,8 +142,8 @@
return offset == 0;
},
_hideNextArrow: function(changesLen) {
return changesLen < DEFAULT_NUM_CHANGES;
_hideNextArrow: function(changesLen, changesPerPage) {
return changesLen < changesPerPage;
},
});
})();

View File

@@ -25,9 +25,11 @@ limitations under the License.
<template>
<style>
:host {
display: block;
}
nav {
align-items: center;
display: flex;
overflow: hidden;
}
.bigTitle {
color: var(--primary-text-color);
@@ -37,6 +39,60 @@ limitations under the License.
.bigTitle:hover {
text-decoration: underline;
}
ul {
list-style: none;
}
.links {
margin-left: 1em;
}
.links ul {
display: none;
}
.links > li {
cursor: default;
display: inline-block;
margin-left: 1em;
padding: .4em 0;
position: relative;
}
.links li:hover ul {
background-color: #fff;
box-shadow: 0 1px 1px rgba(0, 0, 0, .3);
display: block;
left: -.75em;
position: absolute;
top: 2em;
z-index: 1000;
}
.links li ul li a:link,
.links li ul li a:visited {
color: #00e;
display: block;
padding: .5em .75em;
text-decoration: none;
white-space: nowrap;
}
.links li ul li:hover a {
background-color: var(--selection-background-color);
}
.linksTitle {
display: inline-block;
padding-right: 1em;
position: relative;
}
.downArrow {
border-left: .36em solid transparent;
border-right: .36em solid transparent;
border-top: .36em solid #ccc;
height: 0;
position: absolute;
right: 0;
top: calc(50% - .1em);
width: 0;
}
.links li:hover .downArrow {
border-top-color: #666;
}
.rightItems {
display: flex;
flex: 1;
@@ -73,14 +129,30 @@ limitations under the License.
}
}
</style>
<a href="/" class="bigTitle">PolyGerrit</a>
<div class="rightItems">
<gr-search-bar value="{{params.query}}" role="search"></gr-search-bar>
<div class="accountContainer" id="accountContainer">
<a class="loginButton" href="/login" on-tap="_loginTapHandler">Login</a>
<gr-account-dropdown account="[[_account]]"></gr-account-dropdown>
<nav>
<a href="/" class="bigTitle">PolyGerrit</a>
<ul class="links">
<template is="dom-repeat" items="[[_links]]" as="linkGroup">
<li>
<span class="linksTitle">
[[linkGroup.title]] <i class="downArrow"></i>
</span>
<ul>
<template is="dom-repeat" items="[[linkGroup.links]]" as="link">
<li><a href="[[link.url]]">[[link.name]]</a></li>
</template>
</ul>
</li>
</template>
</ul>
<div class="rightItems">
<gr-search-bar value="{{searchQuery}}" role="search"></gr-search-bar>
<div class="accountContainer" id="accountContainer">
<a class="loginButton" href="/login" on-tap="_loginTapHandler">Login</a>
<gr-account-dropdown account="[[_account]]"></gr-account-dropdown>
</div>
</div>
</div>
</nav>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-main-header.js"></script>

View File

@@ -14,6 +14,24 @@
(function() {
'use strict';
var DEFAULT_LINKS = [{
title: 'Changes',
links: [
{
url: '/q/status:open',
name: 'Open',
},
{
url: '/q/status:merged',
name: 'Merged',
},
{
url: '/q/status:abandoned',
name: 'Abandoned',
},
],
}];
Polymer({
is: 'gr-main-header',
@@ -22,15 +40,74 @@
},
properties: {
searchQuery: {
type: String,
notify: true,
},
_account: Object,
_defaultLinks: {
type: Array,
value: function() {
return DEFAULT_LINKS;
},
},
_links: {
type: Array,
computed: '_computeLinks(_defaultLinks, _userLinks)',
},
_userLinks: {
type: Array,
value: function() { return []; },
},
},
observers: [
'_accountLoaded(_account)',
],
attached: function() {
this._loadAccount();
},
_computeLinks: function(defaultLinks, userLinks) {
var links = defaultLinks.slice();
if (userLinks && userLinks.length > 0) {
links.push({
title: 'Your',
links: userLinks,
});
}
return links;
},
_loadAccount: function() {
this.$.restAPI.getAccount().then(function(account) {
this._account = account;
this.$.accountContainer.classList.toggle('loggedIn', account != null);
this.$.accountContainer.classList.toggle('loggedOut', account == null);
}.bind(this));
},
_accountLoaded: function(account) {
if (!account) { return; }
this.$.restAPI.getPreferences().then(function(prefs) {
this._userLinks =
prefs.my.map(this._stripHashPrefix).filter(this._isSupportedLink);
}.bind(this));
},
_stripHashPrefix: function(linkObj) {
if (linkObj.url.indexOf('#') === 0) {
linkObj.url = linkObj.url.slice(1);
}
return linkObj;
},
_isSupportedLink: function(linkObj) {
// Groups are not yet supported.
return linkObj.url.indexOf('/groups') !== 0;
},
});
})();

View File

@@ -0,0 +1,89 @@
<!DOCTYPE html>
<!--
Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-main-header</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-main-header.html">
<test-fixture id="basic">
<template>
<gr-main-header></gr-main-header>
</template>
</test-fixture>
<script>
suite('gr-main-header tests', function() {
var element;
setup(function() {
stub('gr-main-header', {
_loadAccount: function() {},
});
element = fixture('basic');
});
test('strip hash prefix', function() {
assert.deepEqual([
{url: '#/q/owner:self+is:draft'},
{url: 'https://awesometown.com/#hashyhash'},
].map(element._stripHashPrefix),
[
{url: '/q/owner:self+is:draft'},
{url: 'https://awesometown.com/#hashyhash'},
]);
});
test('filter unsupported urls', function() {
assert.deepEqual([
{url: '/q/owner:self+is:draft'},
{url: '/c/331788/'},
{url: '/groups/self'},
{url: 'https://awesometown.com/#hashyhash'},
].filter(element._isSupportedLink),
[
{url: '/q/owner:self+is:draft'},
{url: '/c/331788/'},
{url: 'https://awesometown.com/#hashyhash'},
]);
});
test('user links', function() {
var defaultLinks = [{
title: 'Faves',
links: [{
name: 'Pinterest',
url: 'https://pinterest.com',
}],
}];
var userLinks = [{
name: 'Facebook',
url: 'https://facebook.com',
}];
assert.deepEqual(element._computeLinks(defaultLinks, []), defaultLinks);
assert.deepEqual(element._computeLinks(defaultLinks, userLinks),
defaultLinks.concat({
title: 'Your',
links: userLinks,
}));
});
});
</script>

View File

@@ -30,12 +30,11 @@ limitations under the License.
}
input {
border: 1px solid #d1d2d3;
outline: none;
}
input {
border-radius: 2px 0 0 2px;
flex: 1;
font: inherit;
border-radius: 2px 0 0 2px;
outline: none;
padding: 0 .25em;
}
gr-button {
background-color: #f1f2f3;

View File

@@ -61,12 +61,13 @@ limitations under the License.
<gr-ajax id="diffPreferencesXHR"
url="/accounts/self/preferences.diff"
last-response="{{_diffPreferences}}"></gr-ajax>
<gr-main-header></gr-main-header>
<gr-main-header search-query="{{params.query}}"></gr-main-header>
<main>
<template is="dom-if" if="{{_showChangeListView}}" restamp="true">
<gr-change-list-view
params="[[params]]"
view-state="{{_viewState.changeListView}}"
changes-per-page="[[_preferences.changes_per_page]]"
logged-in="[[_computeLoggedIn(account)]]"></gr-change-list-view>
</template>
<template is="dom-if" if="{{_showDashboardView}}" restamp="true">

View File

@@ -54,6 +54,7 @@
},
_diffPreferences: Object,
_preferences: Object,
_showChangeListView: Boolean,
_showDashboardView: Boolean,
_showChangeView: Boolean,
@@ -106,6 +107,10 @@
this._resolveAccountReady();
if (this.loggedIn) {
this.$.diffPreferencesXHR.generateRequest();
this.$.restAPI.getPreferences().then(function(preferences) {
this._preferences = preferences;
}.bind(this));
} else {
// These defaults should match the defaults in
// gerrit-extension-api/src/main/jcg/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -125,6 +130,10 @@
tab_size: 8,
theme: 'DEFAULT',
};
this._preferences = {
changes_per_page: 25,
};
}
},

View File

@@ -86,13 +86,17 @@
return this._fetchSharedCacheURL('/accounts/self/detail');
},
getPreferences: function() {
return this._fetchSharedCacheURL('/accounts/self/preferences');
},
_fetchSharedCacheURL: function(url) {
if (this._sharedFetchPromises[url]) {
return this._sharedFetchPromises[url];
}
// TODO(andybons): Periodic cache invalidation.
if (this._cache[url] !== undefined) {
return this._cache[url];
return Promise.resolve(this._cache[url]);
}
this._sharedFetchPromises[url] = this.fetchJSON(url).then(
function(response) {

View File

@@ -65,7 +65,19 @@ limitations under the License.
Promise.all(promises).then(function(results) {
assert.deepEqual(results, [1, 1, 1]);
fetchJSONStub.restore();
element._fetchSharedCacheURL('/foo').then(function(foo) {
assert.equal(foo, 1);
fetchJSONStub.restore();
done();
});
});
});
test('cached promise', function(done) {
var promise = Promise.reject('foo');
element._cache['/foo'] = promise;
element._fetchSharedCacheURL('/foo').catch(function(p) {
assert.equal(p, 'foo');
done();
});
});

View File

@@ -19,6 +19,7 @@ limitations under the License.
--primary-text-color: #000;
--search-border-color: #ddd;
--secondary-color: #f1f2f3;
--selection-background-color: #ebf5fb;
--default-text-color: #000;
--view-background-color: #fff;
--default-horizontal-margin: 1.25rem;