Merge "Replace email with getUserId"
diff --git a/Documentation/config-groups.txt b/Documentation/config-groups.txt
index 0917515..4abb223 100644
--- a/Documentation/config-groups.txt
+++ b/Documentation/config-groups.txt
@@ -34,7 +34,7 @@
group, there is a ref, stored as a sharded UUID, e.g.
----
- refs/groups/ef/deafbeefdeafbeefdeafbeefdeafbeefdeafbeef
+ refs/groups/de/deafbeefdeafbeefdeafbeefdeafbeefdeafbeef
----
The ref points to commits holding files. The files are
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
index 88bf943..3af856f 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
@@ -41,18 +41,22 @@
firePageError,
fireTitleChange,
} from '../../../utils/event-util';
-import {GerritView} from '../../../services/router/router-model';
import {RELOAD_DASHBOARD_INTERVAL_MS} from '../../../constants/constants';
import {ChangeListSection} from '../gr-change-list/gr-change-list';
import {a11yStyles} from '../../../styles/gr-a11y-styles';
import {sharedStyles} from '../../../styles/shared-styles';
-import {LitElement, PropertyValues, html, css} from 'lit';
+import {LitElement, html, css, nothing} from 'lit';
import {customElement, property, state, query} from 'lit/decorators.js';
import {assertIsDefined} from '../../../utils/common-util';
import {Shortcut} from '../../../services/shortcuts/shortcuts-config';
import {ShortcutController} from '../../lit/shortcut-controller';
-import {DashboardViewState} from '../../../models/views/dashboard';
+import {
+ dashboardViewModelToken,
+ DashboardViewState,
+} from '../../../models/views/dashboard';
import {createSearchUrl} from '../../../models/views/search';
+import {subscribe} from '../../lit/subscription-controller';
+import {resolve} from '../../../models/dependency';
const PROJECT_PLACEHOLDER_PATTERN = /\${project}/g;
@@ -79,13 +83,13 @@
@query('#confirmDeleteOverlay') protected confirmDeleteOverlay?: GrOverlay;
@property({type: Object})
- account: AccountDetailInfo | null = null;
+ account?: AccountDetailInfo;
@property({type: Object})
preferences?: PreferencesInput;
- @property({type: Object})
- params?: DashboardViewState;
+ @state()
+ viewState?: DashboardViewState;
// private but used in test
@state() results?: ChangeListSection[];
@@ -103,12 +107,29 @@
private readonly restApiService = getAppContext().restApiService;
+ private readonly userModel = getAppContext().userModel;
+
+ private readonly getViewModel = resolve(this, dashboardViewModelToken);
+
private lastVisibleTimestampMs = 0;
private readonly shortcuts = new ShortcutController(this);
constructor() {
super();
+ subscribe(
+ this,
+ () => this.userModel.account$,
+ x => (this.account = x)
+ );
+ subscribe(
+ this,
+ () => this.getViewModel().state$,
+ x => {
+ this.viewState = x;
+ this.reload();
+ }
+ );
this.addEventListener('reload', () => this.reload());
this.shortcuts.addAbstract(Shortcut.UP_TO_DASHBOARD, () => this.reload());
}
@@ -184,6 +205,7 @@
}
override render() {
+ if (!this.viewState) return nothing;
return html`
${this.renderBanner()} ${this.renderContent()}
<gr-overlay id="confirmDeleteOverlay" with-backdrop>
@@ -271,17 +293,15 @@
private renderUserHeader() {
if (
- !this.params ||
- this.params.view !== GerritView.DASHBOARD ||
- !!this.params.project ||
- !this.params.user ||
- this.params.user === 'self'
+ !!this.viewState?.project ||
+ !this.viewState?.user ||
+ this.viewState?.user === 'self'
) {
return;
}
return html`
- <gr-user-header .userId=${this.params?.user}></gr-user-header>
+ <gr-user-header .userId=${this.viewState?.user}></gr-user-header>
`;
}
@@ -297,12 +317,6 @@
`;
}
- override updated(changedProperties: PropertyValues) {
- if (changedProperties.has('params')) {
- this.paramsChanged();
- }
- }
-
private loadPreferences() {
return this.restApiService.getLoggedIn().then(loggedIn => {
if (loggedIn) {
@@ -354,26 +368,15 @@
return 'Dashboard for ' + user;
}
- private isViewActive(params: DashboardViewState) {
- return params.view === GerritView.DASHBOARD;
- }
-
- // private but used in test
- paramsChanged() {
- return this.reload();
- }
-
/**
* Reloads the element.
*
* private but used in test
*/
reload() {
- if (!this.params || !this.isViewActive(this.params)) {
- return Promise.resolve();
- }
+ if (!this.viewState) return Promise.resolve();
this.loading = true;
- const {project, dashboard, title, user, sections} = this.params;
+ const {project, dashboard, title, user, sections} = this.viewState;
const dashboardPromise: Promise<UserDashboard | undefined> = project
? this.getProjectDashboard(project, dashboard)
: Promise.resolve(
@@ -476,7 +479,7 @@
* And then we want to emphasize the changes where the waiting time is larger.
*/
private maybeSortResults(name: string, results: ChangeInfo[]) {
- const userId = this.account && this.account._account_id;
+ const userId = this.account?._account_id;
const sortedResults = [...results];
if (name === YOUR_TURN.name && userId) {
sortedResults.sort((c1, c2) => {
@@ -544,9 +547,7 @@
*/
maybeShowDraftsBanner() {
this.showDraftsBanner = false;
- if (!(this.params?.user === 'self')) {
- return;
- }
+ if (!(this.viewState?.user === 'self')) return;
if (!this.results) {
throw new Error('this.results must be set. restAPI returned undefined');
@@ -555,16 +556,12 @@
const draftSection = this.results.find(
section => section.query === 'has:draft'
);
- if (!draftSection || !draftSection.results.length) {
- return;
- }
+ if (!draftSection || !draftSection.results.length) return;
const closedChanges = draftSection.results.filter(
change => !changeIsOpen(change)
);
- if (!closedChanges.length) {
- return;
- }
+ if (!closedChanges.length) return;
this.showDraftsBanner = true;
}
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts
index 92afae1..7889e20 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.ts
@@ -41,7 +41,6 @@
suite('gr-dashboard-view tests', () => {
let element: GrDashboardView;
- let paramsChangedPromise: Promise<any>;
let getChangesStub: SinonStubbedMember<
RestApiService['getChangesForMultipleQueries']
>;
@@ -54,30 +53,25 @@
registered_on: '2015-03-12 18:32:08.000000000' as Timestamp,
})
);
- stubRestApi('getAccountStatus').returns(Promise.resolve(undefined));
element = await fixture<GrDashboardView>(html`
<gr-dashboard-view></gr-dashboard-view>
`);
await element.updateComplete;
- let resolver: (value?: any) => void;
- paramsChangedPromise = new Promise(resolve => {
- resolver = resolve;
- });
- const paramsChanged = element.paramsChanged.bind(element);
- sinon
- .stub(element, 'paramsChanged')
- .callsFake(() => paramsChanged().then(() => resolver()));
});
test('render', async () => {
- const sections = [
- {name: 'test1', query: 'test1', hideIfEmpty: true},
- {name: 'test2', query: 'test2', hideIfEmpty: true},
- ];
+ element.viewState = {
+ view: GerritView.DASHBOARD,
+ user: 'self',
+ sections: [
+ {name: 'test1', query: 'test1', hideIfEmpty: true},
+ {name: 'test2', query: 'test2', hideIfEmpty: true},
+ ],
+ };
getChangesStub.returns(Promise.resolve([[createChange()]]));
- await element.fetchDashboardChanges({sections}, false);
+ await element.reload();
element.loading = false;
stubFlags('isEnabled').returns(true);
element.requestUpdate();
@@ -125,14 +119,18 @@
suite('bulk actions', () => {
setup(async () => {
- const sections = [
- {name: 'test1', query: 'test1', hideIfEmpty: true},
- {name: 'test2', query: 'test2', hideIfEmpty: true},
- ];
+ element.viewState = {
+ view: GerritView.DASHBOARD,
+ user: 'user',
+ sections: [
+ {name: 'test1', query: 'test1', hideIfEmpty: true},
+ {name: 'test2', query: 'test2', hideIfEmpty: true},
+ ],
+ };
getChangesStub.returns(Promise.resolve([[createChange()]]));
- await element.fetchDashboardChanges({sections}, false);
- element.loading = false;
stubFlags('isEnabled').returns(true);
+ await element.reload();
+ element.loading = false;
element.requestUpdate();
await element.updateComplete;
});
@@ -151,11 +149,6 @@
getChangesStub.restore();
getChangesStub.returns(Promise.resolve([[createChange()]]));
- element.params = {
- view: GerritView.DASHBOARD,
- user: 'notself',
- dashboard: '' as DashboardId,
- };
await element.reload();
await element.updateComplete;
assert.isTrue(checkbox.checked);
@@ -163,9 +156,20 @@
});
suite('drafts banner functionality', () => {
+ setup(async () => {
+ element.viewState = {
+ view: GerritView.DASHBOARD,
+ user: 'self',
+ sections: [
+ {name: 'test1', query: 'test1', hideIfEmpty: true},
+ {name: 'test2', query: 'test2', hideIfEmpty: true},
+ ],
+ };
+ });
+
suite('maybeShowDraftsBanner', () => {
test('not dashboard/self', () => {
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'notself',
dashboard: '' as DashboardId,
@@ -176,7 +180,7 @@
test('no drafts at all', () => {
element.results = [];
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'self',
dashboard: '' as DashboardId,
@@ -190,7 +194,7 @@
element.results = [
{countLabel: '', name: '', query: 'has:draft', results: [openChange]},
];
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'self',
dashboard: '' as DashboardId,
@@ -210,7 +214,7 @@
},
];
assert.isFalse(changeIsOpen(element.results[0].results[0]));
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'self',
dashboard: '' as DashboardId,
@@ -314,27 +318,10 @@
});
});
- suite('_isViewActive', () => {
- test('nothing happens when user param is falsy', async () => {
- element.params = undefined;
- await element.updateComplete;
- assert.equal(getChangesStub.callCount, 0);
- });
-
- test('content is refreshed when user param is updated', async () => {
- element.params = {
- view: GerritView.DASHBOARD,
- user: 'self',
- dashboard: '' as DashboardId,
- };
- await paramsChangedPromise;
- assert.isTrue(getChangesStub.called);
- });
- });
-
suite('selfOnly sections', () => {
test('viewing self dashboard includes selfOnly sections', async () => {
- element.params = {
+ element.account = undefined;
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'self',
dashboard: '' as DashboardId,
@@ -343,13 +330,13 @@
{name: '', query: '2', selfOnly: true},
],
};
- await paramsChangedPromise;
+ await element.reload();
assert.isTrue(getChangesStub.calledWith(undefined, ['1', '2']));
});
test('viewing dashboard when logged in includes owner:self query', async () => {
element.account = createAccountDetailWithId(1);
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'self',
dashboard: '' as DashboardId,
@@ -358,14 +345,14 @@
{name: '', query: '2', selfOnly: true},
],
};
- await paramsChangedPromise;
+ await element.reload();
assert.isTrue(
getChangesStub.calledWith(undefined, ['1', '2', 'owner:self limit:1'])
);
});
test("viewing another user's dashboard omits selfOnly sections", async () => {
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
user: 'user',
dashboard: '' as DashboardId,
@@ -374,13 +361,13 @@
{name: '', query: '2', selfOnly: true},
],
};
- await paramsChangedPromise;
+ await element.reload();
assert.isTrue(getChangesStub.calledWith(undefined, ['1']));
});
});
test('suffixForDashboard is included in getChanges query', async () => {
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
dashboard: '' as DashboardId,
sections: [
@@ -388,7 +375,7 @@
{name: '', query: '2', suffixForDashboard: 'suffix'},
],
};
- await paramsChangedPromise;
+ await element.reload();
assert.isTrue(getChangesStub.calledWith(undefined, ['1', '2 suffix']));
});
@@ -520,6 +507,9 @@
});
test('showNewUserHelp', async () => {
+ element.viewState = {
+ view: GerritView.DASHBOARD,
+ };
element.loading = false;
element.showNewUserHelp = false;
await element.updateComplete;
@@ -547,11 +537,11 @@
});
test('gr-user-header', async () => {
- element.params = undefined;
+ element.viewState = undefined;
await element.updateComplete;
assert.isNotOk(query(element, 'gr-user-header'));
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
dashboard: '' as DashboardId,
user: 'self',
@@ -560,7 +550,7 @@
assert.isNotOk(query(element, 'gr-user-header'));
element.loading = false;
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
dashboard: '' as DashboardId,
user: 'user',
@@ -568,7 +558,7 @@
await element.updateComplete;
assert.isOk(query(element, 'gr-user-header'));
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
dashboard: '' as DashboardId,
project: 'p' as RepoName,
@@ -593,16 +583,16 @@
assert.strictEqual((e as PageErrorEvent).detail.response, response);
promise.resolve();
});
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
dashboard: 'dashboard' as DashboardId,
project: 'project' as RepoName,
user: '',
};
- await Promise.all([paramsChangedPromise, promise]);
+ await Promise.all([element.reload(), promise]);
});
- test('params change triggers dashboardDisplayed()', async () => {
+ test('viewState change triggers dashboardDisplayed()', async () => {
stubRestApi('getDashboard').returns(
Promise.resolve({
id: '' as DashboardId,
@@ -618,13 +608,13 @@
);
getChangesStub.returns(Promise.resolve([]));
const dashboardDisplayedStub = stubReporting('dashboardDisplayed');
- element.params = {
+ element.viewState = {
view: GerritView.DASHBOARD,
dashboard: 'dashboard' as DashboardId,
project: 'project' as RepoName,
user: '',
};
- await paramsChangedPromise;
+ await element.reload();
assert.isTrue(dashboardDisplayedStub.calledOnce);
});
});
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 2bd217a..c20ca1d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -3224,6 +3224,7 @@
controls.openDeleteDialog(path);
break;
case GrEditConstants.Actions.OPEN.id:
+ assertIsDefined(this.patchRange.patchNum, 'patchset number');
GerritNav.navigateToRelativeUrl(
createEditUrl({
changeNum: this.change._number,
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts
index 83bb4cf..6ef5406 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts
@@ -423,6 +423,7 @@
this.closeDialog(this.openDialog);
return;
}
+ assertIsDefined(this.patchNum, 'patchset number');
const url = createEditUrl({
changeNum: this.change._number,
project: this.change.project,
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
index c0b3760..4cbde8f 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
@@ -11,11 +11,8 @@
import {GerritNav} from '../../core/gr-navigation/gr-navigation';
import {computeTruncatedPath} from '../../../utils/path-list-util';
import {
- PatchSetNum,
EditPreferencesInfo,
Base64FileContent,
- NumericChangeId,
- EDIT,
PatchSetNumber,
} from '../../../types/common';
import {ParsedChangeInfo} from '../../../types/types';
@@ -31,11 +28,10 @@
import {LitElement, PropertyValues, html, css, nothing} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {subscribe} from '../../lit/subscription-controller';
-import {GerritView} from '../../../services/router/router-model';
import {resolve} from '../../../models/dependency';
import {changeModelToken} from '../../../models/change/change-model';
import {ShortcutController} from '../../lit/shortcut-controller';
-import {EditViewState} from '../../../models/views/edit';
+import {editViewModelToken, EditViewState} from '../../../models/views/edit';
const RESTORED_MESSAGE = 'Content restored from a previous edit.';
const SAVING_MESSAGE = 'Saving changes...';
@@ -61,21 +57,12 @@
*/
@property({type: Object})
- params?: EditViewState;
+ viewState?: EditViewState;
// private but used in test
@state() change?: ParsedChangeInfo;
// private but used in test
- @state() changeNum?: NumericChangeId;
-
- // private but used in test
- @state() patchNum?: PatchSetNum;
-
- // private but used in test
- @state() path?: string;
-
- // private but used in test
@state() type?: string;
// private but used in test
@@ -92,9 +79,8 @@
@state() private editPrefs?: EditPreferencesInfo;
- @state() private lineNum?: number;
-
- @state() private latestPatchsetNumber?: PatchSetNumber;
+ // private but used in test
+ @state() latestPatchsetNumber?: PatchSetNumber;
private readonly restApiService = getAppContext().restApiService;
@@ -106,6 +92,8 @@
private readonly getChangeModel = resolve(this, changeModelToken);
+ private readonly getEditViewModel = resolve(this, editViewModelToken);
+
private readonly shortcuts = new ShortcutController(this);
// Tests use this so needs to be non private
@@ -119,8 +107,14 @@
subscribe(
this,
() => this.userModel.editPreferences$,
- editPreferences => {
- this.editPrefs = editPreferences;
+ editPreferences => (this.editPrefs = editPreferences)
+ );
+ subscribe(
+ this,
+ () => this.getEditViewModel().state$,
+ state => {
+ this.viewState = state;
+ this.viewStateChanged();
}
);
subscribe(
@@ -207,6 +201,7 @@
}
override render() {
+ if (!this.viewState) return;
return html` ${this.renderHeader()} ${this.renderEndpoint()} `;
}
@@ -220,7 +215,7 @@
<span class="separator"></span>
<gr-editable-label
labelText="File path"
- .value=${this.path}
+ .value=${this.viewState?.path}
placeholder="File path..."
@changed=${this.handlePathChanged}
></gr-editable-label>
@@ -254,7 +249,7 @@
}
private renderEditingOldPatchsetWarning() {
- const patchset = this.params?.patchNum;
+ const patchset = this.viewState?.patchNum;
if (patchset === this.latestPatchsetNumber) return nothing;
return html`<span class="warning"> (Old Patchset)</span>`;
}
@@ -277,7 +272,7 @@
></gr-endpoint-param>
<gr-endpoint-param
name="lineNum"
- .value=${this.lineNum}
+ .value=${this.viewState?.lineNum}
></gr-endpoint-param>
<gr-default-editor
id="file"
@@ -289,58 +284,40 @@
}
override willUpdate(changedProperties: PropertyValues) {
- if (changedProperties.has('params')) {
- this.paramsChanged();
- }
-
if (changedProperties.has('change')) {
this.navigateToChangeIfEdit();
}
-
if (changedProperties.has('change') || changedProperties.has('type')) {
this.navigateToChangeIfEditType();
}
}
get storageKey() {
- return `c${this.changeNum}_ps${this.patchNum}_${this.path}`;
+ return `c${this.viewState?.changeNum}_ps${this.viewState?.patchNum}_${this.viewState?.path}`;
}
// private but used in test
- paramsChanged() {
- if (!this.params) return;
-
- if (this.params.view !== GerritView.EDIT) {
- return;
- }
-
- this.changeNum = this.params.changeNum;
- this.path = this.params.path;
- this.patchNum = this.params.patchNum || (EDIT as PatchSetNum);
- this.lineNum =
- typeof this.params.lineNum === 'string'
- ? Number(this.params.lineNum)
- : this.params.lineNum;
+ viewStateChanged() {
+ if (!this.viewState) return;
// NOTE: This may be called before attachment (e.g. while parentElement is
// null). Fire title-change in an async so that, if attachment to the DOM
// has been queued, the event can bubble up to the handler in gr-app.
setTimeout(() => {
- if (!this.params) return;
- const title = `Editing ${computeTruncatedPath(this.params.path)}`;
+ if (!this.viewState) return;
+ const title = `Editing ${computeTruncatedPath(this.viewState.path)}`;
fireTitleChange(this, title);
});
const promises = [];
-
- assertIsDefined(this.changeNum, 'change number');
- assertIsDefined(this.path, 'path');
- promises.push(this.getChangeDetail(this.changeNum));
- promises.push(this.getFileData(this.changeNum, this.path, this.patchNum));
+ promises.push(this.getChangeDetail());
+ promises.push(this.getFileData());
return Promise.all(promises);
}
- private async getChangeDetail(changeNum: NumericChangeId) {
+ private async getChangeDetail() {
+ const changeNum = this.viewState?.changeNum;
+ assertIsDefined(changeNum, 'change number');
this.change = await this.restApiService.getChangeDetail(changeNum);
}
@@ -364,16 +341,17 @@
// private but used in test
async handlePathChanged(e: CustomEvent<string>): Promise<void> {
- // TODO(TS) could be cleaned up, it was added for type requirements
- if (this.changeNum === undefined || !this.path) {
- throw new Error('changeNum or path undefined');
- }
- const path = e.detail;
- if (path === this.path) return;
+ const changeNum = this.viewState?.changeNum;
+ const currentPath = this.viewState?.path;
+ assertIsDefined(changeNum, 'change number');
+ assertIsDefined(currentPath, 'path');
+
+ const newPath = e.detail;
+ if (newPath === currentPath) return;
const res = await this.restApiService.renameFileInChangeEdit(
- this.changeNum,
- this.path,
- path
+ changeNum,
+ currentPath,
+ newPath
);
if (!res?.ok) return;
@@ -383,22 +361,22 @@
// private but used in test
viewEditInChangeView() {
- if (this.change)
- GerritNav.navigateToChange(this.change, {
- isEdit: true,
- forceReload: true,
- });
+ if (!this.change) return;
+ GerritNav.navigateToChange(this.change, {
+ isEdit: true,
+ forceReload: true,
+ });
}
// private but used in test
- getFileData(
- changeNum: NumericChangeId,
- path: string,
- patchNum?: PatchSetNum
- ) {
- if (patchNum === undefined) {
- return Promise.reject(new Error('patchNum undefined'));
- }
+ getFileData() {
+ const changeNum = this.viewState?.changeNum;
+ const patchNum = this.viewState?.patchNum;
+ const path = this.viewState?.path;
+ assertIsDefined(changeNum, 'change number');
+ assertIsDefined(patchNum, 'patchset number');
+ assertIsDefined(path, 'path');
+
const storedContent = this.storage.getEditableContentItem(this.storageKey);
return this.restApiService
@@ -431,16 +409,18 @@
// private but used in test
saveEdit() {
- if (this.changeNum === undefined || !this.path) {
- return Promise.reject(new Error('changeNum or path undefined'));
- }
+ const changeNum = this.viewState?.changeNum;
+ const path = this.viewState?.path;
+ assertIsDefined(changeNum, 'change number');
+ assertIsDefined(path, 'path');
+
this.saving = true;
this.showAlert(SAVING_MESSAGE);
this.storage.eraseEditableContentItem(this.storageKey);
if (!this.newContent)
return Promise.reject(new Error('new content undefined'));
return this.restApiService
- .saveChangeEdit(this.changeNum, this.path, this.newContent)
+ .saveChangeEdit(changeNum, path, this.newContent)
.then(res => {
this.saving = false;
this.showAlert(res.ok ? SAVED_MESSAGE : SAVE_FAILED_MSG);
@@ -464,9 +444,7 @@
return true;
}
- if (this.saving) {
- return true;
- }
+ if (this.saving) return true;
return this.content === this.newContent;
}
@@ -483,9 +461,9 @@
};
private handlePublishTap = () => {
- assertIsDefined(this.changeNum, 'changeNum');
+ const changeNum = this.viewState?.changeNum;
+ assertIsDefined(changeNum, 'change number');
- const changeNum = this.changeNum;
this.saveEdit().then(() => {
const handleError: ErrorCallback = response => {
this.showAlert(PUBLISH_FAILED_MSG);
@@ -528,9 +506,7 @@
// private but used in test
handleSaveShortcut() {
- if (!this.computeSaveDisabled()) {
- this.saveEdit();
- }
+ if (!this.computeSaveDisabled()) this.saveEdit();
}
}
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
index 30d935c..8bc788c 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
@@ -15,7 +15,12 @@
stubRestApi,
stubStorage,
} from '../../../test/test-utils';
-import {EDIT, NumericChangeId, PatchSetNum} from '../../../types/common';
+import {
+ EDIT,
+ NumericChangeId,
+ PatchSetNumber,
+ RevisionPatchSetNum,
+} from '../../../types/common';
import {
createChangeViewChange,
createEditViewState,
@@ -41,6 +46,11 @@
saveFileStub = stubRestApi('saveChangeEdit');
changeDetailStub = stubRestApi('getChangeDetail');
navigateStub = sinon.stub(element, 'viewEditInChangeView');
+ element.viewState = {
+ ...createEditViewState(),
+ patchNum: 1 as PatchSetNumber,
+ };
+ element.latestPatchsetNumber = 1 as PatchSetNumber;
await element.updateComplete;
});
@@ -58,7 +68,7 @@
labeltext="File path"
placeholder="File path..."
tabindex="0"
- title="File path..."
+ title="${element.viewState?.path}"
>
</gr-editable-label>
</span>
@@ -112,8 +122,8 @@
);
});
- suite('paramsChanged', () => {
- test('good params proceed', async () => {
+ suite('viewStateChanged', () => {
+ test('good view state proceed', async () => {
changeDetailStub.returns(Promise.resolve({}));
const fileStub = sinon.stub(element, 'getFileData').callsFake(() => {
element.content = 'text';
@@ -122,20 +132,14 @@
return Promise.resolve();
});
- element.params = {...createEditViewState()};
- const promises = element.paramsChanged();
+ element.viewState = {...createEditViewState()};
+ const promises = element.viewStateChanged();
await element.updateComplete;
const changeNum = 42 as NumericChangeId;
- assert.equal(element.changeNum, changeNum);
- assert.equal(element.path, 'foo/bar.baz');
assert.deepEqual(changeDetailStub.lastCall.args[0], changeNum);
- assert.deepEqual(fileStub.lastCall.args, [
- changeNum,
- 'foo/bar.baz',
- EDIT as PatchSetNum,
- ]);
+ assert.isTrue(fileStub.called);
return promises?.then(() => {
assert.equal(element.content, 'text');
@@ -146,8 +150,7 @@
});
test('edit file path', () => {
- element.changeNum = 42 as NumericChangeId;
- element.path = 'foo/bar.baz';
+ element.viewState = {...createEditViewState()};
savePathStub.onFirstCall().returns(Promise.resolve({}));
savePathStub.onSecondCall().returns(Promise.resolve({ok: true}));
@@ -197,8 +200,7 @@
const newText = 'file text changed';
setup(async () => {
- element.changeNum = 42 as NumericChangeId;
- element.path = 'foo/bar.baz';
+ element.viewState = {...createEditViewState()};
element.content = originalText;
element.newContent = originalText;
await element.updateComplete;
@@ -363,30 +365,38 @@
content: 'new content',
})
);
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: EDIT,
+ path: 'test/path',
+ };
// Ensure no data is set with a bad response.
- return element
- .getFileData(1 as NumericChangeId, 'test/path', EDIT as PatchSetNum)
- .then(() => {
- assert.equal(element.newContent, 'new content');
- assert.equal(element.content, 'new content');
- assert.equal(element.type, 'text/javascript');
- });
+ return element.getFileData().then(() => {
+ assert.equal(element.newContent, 'new content');
+ assert.equal(element.content, 'new content');
+ assert.equal(element.type, 'text/javascript');
+ });
});
test('!res.ok', () => {
stubRestApi('getFileContent').returns(
Promise.resolve(new Response(null, {status: 500}))
);
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: EDIT,
+ path: 'test/path',
+ };
// Ensure no data is set with a bad response.
- return element
- .getFileData(1 as NumericChangeId, 'test/path', EDIT as PatchSetNum)
- .then(() => {
- assert.equal(element.newContent, '');
- assert.equal(element.content, '');
- assert.equal(element.type, '');
- });
+ return element.getFileData().then(() => {
+ assert.equal(element.newContent, '');
+ assert.equal(element.content, '');
+ assert.equal(element.type, '');
+ });
});
test('content is undefined', () => {
@@ -397,28 +407,36 @@
type: 'text/javascript' as ResponseType,
})
);
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: EDIT,
+ path: 'test/path',
+ };
- return element
- .getFileData(1 as NumericChangeId, 'test/path', EDIT as PatchSetNum)
- .then(() => {
- assert.equal(element.newContent, '');
- assert.equal(element.content, '');
- assert.equal(element.type, 'text/javascript');
- });
+ return element.getFileData().then(() => {
+ assert.equal(element.newContent, '');
+ assert.equal(element.content, '');
+ assert.equal(element.type, 'text/javascript');
+ });
});
test('content and type is undefined', () => {
stubRestApi('getFileContent').returns(
Promise.resolve({...new Response(), ok: true})
);
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: EDIT,
+ path: 'test/path',
+ };
- return element
- .getFileData(1 as NumericChangeId, 'test/path', EDIT as PatchSetNum)
- .then(() => {
- assert.equal(element.newContent, '');
- assert.equal(element.content, '');
- assert.equal(element.type, '');
- });
+ return element.getFileData().then(() => {
+ assert.equal(element.newContent, '');
+ assert.equal(element.content, '');
+ assert.equal(element.type, '');
+ });
});
});
@@ -438,7 +456,6 @@
element.change = createChangeViewChange();
navigateStub.restore();
const navStub = sinon.stub(GerritNav, 'navigateToChange');
- element.patchNum = EDIT;
element.viewEditInChangeView();
assert.equal(navStub.lastCall.args[1]!.patchNum, undefined);
assert.equal(navStub.lastCall.args[1]!.isEdit, true);
@@ -500,20 +517,24 @@
content: 'old content',
})
);
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: 1 as RevisionPatchSetNum,
+ path: 'test',
+ };
const alertStub = sinon.stub();
element.addEventListener(EventType.SHOW_ALERT, alertStub);
- return element
- .getFileData(1 as NumericChangeId, 'test', 1 as PatchSetNum)
- .then(async () => {
- await element.updateComplete;
+ return element.getFileData().then(async () => {
+ await element.updateComplete;
- assert.isTrue(alertStub.called);
- assert.equal(element.newContent, 'pending edit');
- assert.equal(element.content, 'old content');
- assert.equal(element.type, 'text/javascript');
- });
+ assert.isTrue(alertStub.called);
+ assert.equal(element.newContent, 'pending edit');
+ assert.equal(element.content, 'old content');
+ assert.equal(element.type, 'text/javascript');
+ });
});
test('local edit exists, is same as remote edit', () => {
@@ -528,26 +549,33 @@
content: 'pending edit',
})
);
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: 1 as RevisionPatchSetNum,
+ path: 'test',
+ };
const alertStub = sinon.stub();
element.addEventListener(EventType.SHOW_ALERT, alertStub);
- return element
- .getFileData(1 as NumericChangeId, 'test', 1 as PatchSetNum)
- .then(async () => {
- await element.updateComplete;
+ return element.getFileData().then(async () => {
+ await element.updateComplete;
- assert.isFalse(alertStub.called);
- assert.equal(element.newContent, 'pending edit');
- assert.equal(element.content, 'pending edit');
- assert.equal(element.type, 'text/javascript');
- });
+ assert.isFalse(alertStub.called);
+ assert.equal(element.newContent, 'pending edit');
+ assert.equal(element.content, 'pending edit');
+ assert.equal(element.type, 'text/javascript');
+ });
});
test('storage key computation', () => {
- element.changeNum = 1 as NumericChangeId;
- element.patchNum = 1 as PatchSetNum;
- element.path = 'test';
+ element.viewState = {
+ ...createEditViewState(),
+ changeNum: 1 as NumericChangeId,
+ patchNum: 1 as RevisionPatchSetNum,
+ path: 'test',
+ };
assert.equal(element.storageKey, 'c1_ps1_test');
});
});
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index 6c96965..e4e9310 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -447,12 +447,7 @@
private renderDashboardView() {
return cache(
this.view === GerritView.DASHBOARD
- ? html`
- <gr-dashboard-view
- .account=${this.account}
- .params=${this.params}
- ></gr-dashboard-view>
- `
+ ? html`<gr-dashboard-view></gr-dashboard-view>`
: nothing
);
}
@@ -479,7 +474,7 @@
private renderEditorView() {
if (this.view !== GerritView.EDIT) return nothing;
- return html`<gr-editor-view .params=${this.params}></gr-editor-view>`;
+ return html`<gr-editor-view></gr-editor-view>`;
}
private renderDiffView() {
@@ -500,7 +495,6 @@
if (this.view !== GerritView.SETTINGS) return nothing;
return html`
<gr-settings-view
- .params=${this.params}
@account-detail-update=${this.handleAccountDetailUpdate}
>
</gr-settings-view>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
index 470d847..1c5d30d 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
@@ -23,7 +23,6 @@
import '../gr-ssh-editor/gr-ssh-editor';
import '../gr-watched-projects-editor/gr-watched-projects-editor';
import {getDocsBaseUrl} from '../../../utils/url-util';
-import {AppElementParams} from '../../gr-app-types';
import {GrAccountInfo} from '../gr-account-info/gr-account-info';
import {GrWatchedProjectsEditor} from '../gr-watched-projects-editor/gr-watched-projects-editor';
import {GrGroupList} from '../gr-group-list/gr-group-list';
@@ -35,7 +34,6 @@
import {GrEmailEditor} from '../gr-email-editor/gr-email-editor';
import {fireAlert, fireTitleChange} from '../../../utils/event-util';
import {getAppContext} from '../../../services/app-context';
-import {GerritView} from '../../../services/router/router-model';
import {
ColumnNames,
DateFormat,
@@ -48,13 +46,7 @@
} from '../../../constants/constants';
import {BindValueChangeEvent, ValueChangedEvent} from '../../../types/events';
import {LitElement, css, html, nothing} from 'lit';
-import {
- customElement,
- property,
- query,
- queryAsync,
- state,
-} from 'lit/decorators.js';
+import {customElement, query, queryAsync, state} from 'lit/decorators.js';
import {sharedStyles} from '../../../styles/shared-styles';
import {paperStyles} from '../../../styles/gr-paper-styles';
import {fontStyles} from '../../../styles/gr-font-styles';
@@ -64,6 +56,8 @@
import {formStyles} from '../../../styles/gr-form-styles';
import {KnownExperimentId} from '../../../services/flags/flags';
import {subscribe} from '../../lit/subscription-controller';
+import {resolve} from '../../../models/dependency';
+import {settingsViewModelToken} from '../../../models/views/settings';
const GERRIT_DOCS_BASE_URL =
'https://gerrit-review.googlesource.com/' + 'Documentation';
@@ -146,8 +140,6 @@
@state() prefs: PreferencesInput = {};
- @property({type: Object}) params?: AppElementParams;
-
@state() private accountInfoChanged = false;
// private but used in test
@@ -189,6 +181,9 @@
@state() private emailsChanged = false;
// private but used in test
+ @state() emailToken?: string;
+
+ // private but used in test
@state() showNumber?: boolean;
// private but used in test
@@ -200,10 +195,20 @@
private readonly flagsService = getAppContext().flagsService;
+ private readonly getViewModel = resolve(this, settingsViewModelToken);
+
constructor() {
super();
subscribe(
this,
+ () => this.getViewModel().emailToken$,
+ x => {
+ this.emailToken = x;
+ this.confirmEmail();
+ }
+ );
+ subscribe(
+ this,
() => this.userModel.preferences$,
prefs => {
if (!prefs) {
@@ -223,6 +228,15 @@
);
}
+ // private, but used in tests
+ async confirmEmail() {
+ if (!this.emailToken) return;
+ const message = await this.restApiService.confirmEmail(this.emailToken);
+ if (message) fireAlert(this, message);
+ this.getViewModel().clearToken();
+ this.emailEditor.loadData();
+ }
+
override connectedCallback() {
super.connectedCallback();
// Polymer 2: anchor tag won't work on shadow DOM
@@ -266,24 +280,7 @@
})
);
- if (
- this.params &&
- this.params.view === GerritView.SETTINGS &&
- this.params.emailToken
- ) {
- promises.push(
- this.restApiService
- .confirmEmail(this.params.emailToken)
- .then(message => {
- if (message) {
- fireAlert(this, message);
- }
- this.emailEditor.loadData();
- })
- );
- } else {
- promises.push(this.emailEditor.loadData());
- }
+ promises.push(this.emailEditor.loadData());
this._testOnly_loadingPromise = Promise.all(promises).then(() => {
this.loading = false;
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
index 5e0c381..72c19b4 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
@@ -6,7 +6,6 @@
import '../../../test/common-test-setup';
import './gr-settings-view';
import {GrSettingsView} from './gr-settings-view';
-import {GerritView} from '../../../services/router/router-model';
import {queryAll, stubRestApi, waitEventLoop} from '../../../test/test-utils';
import {
AuthInfo,
@@ -34,7 +33,6 @@
import {GrSelect} from '../../shared/gr-select/gr-select';
import {fixture, html, assert} from '@open-wc/testing';
import {EventType} from '../../../types/events';
-import {SettingsViewState} from '../../../models/views/settings';
suite('gr-settings-view tests', () => {
let element: GrSettingsView;
@@ -755,9 +753,6 @@
test('emails are loaded without emailToken', () => {
const emailEditorLoadDataStub = sinon.stub(element.emailEditor, 'loadData');
- element.params = {
- view: GerritView.SETTINGS,
- } as SettingsViewState;
element.firstUpdated();
assert.isTrue(emailEditorLoadDataStub.calledOnce);
});
@@ -861,8 +856,8 @@
})
);
- element.params = {view: GerritView.SETTINGS, emailToken: 'foo'};
- element.firstUpdated();
+ element.emailToken = 'foo';
+ element.confirmEmail();
});
test('it is used to confirm email via rest API', () => {
diff --git a/polygerrit-ui/app/models/views/dashboard.ts b/polygerrit-ui/app/models/views/dashboard.ts
index 0de6bf8..979debb 100644
--- a/polygerrit-ui/app/models/views/dashboard.ts
+++ b/polygerrit-ui/app/models/views/dashboard.ts
@@ -29,10 +29,6 @@
title?: string;
}
-const DEFAULT_STATE: DashboardViewState = {
- view: GerritView.DASHBOARD,
-};
-
const REPO_TOKEN_PATTERN = /\${(project|repo)}/g;
function sectionsToEncodedParams(
@@ -73,9 +69,9 @@
'dashboard-view-model'
);
-export class DashboardViewModel extends Model<DashboardViewState> {
+export class DashboardViewModel extends Model<DashboardViewState | undefined> {
constructor() {
- super(DEFAULT_STATE);
+ super(undefined);
}
finalize() {}
diff --git a/polygerrit-ui/app/models/views/edit.ts b/polygerrit-ui/app/models/views/edit.ts
index 7079630..0eeaae8 100644
--- a/polygerrit-ui/app/models/views/edit.ts
+++ b/polygerrit-ui/app/models/views/edit.ts
@@ -17,17 +17,13 @@
export interface EditViewState extends ViewState {
view: GerritView.EDIT;
- changeNum?: NumericChangeId;
- project?: RepoName;
- path?: string;
- patchNum?: RevisionPatchSetNum;
+ changeNum: NumericChangeId;
+ project: RepoName;
+ path: string;
+ patchNum: RevisionPatchSetNum;
lineNum?: number;
}
-const DEFAULT_STATE: EditViewState = {
- view: GerritView.EDIT,
-};
-
export function createEditUrl(state: Omit<EditViewState, 'view'>): string {
if (state.patchNum === undefined) {
state = {...state, patchNum: EDIT};
@@ -53,9 +49,9 @@
export const editViewModelToken = define<EditViewModel>('edit-view-model');
-export class EditViewModel extends Model<EditViewState> {
+export class EditViewModel extends Model<EditViewState | undefined> {
constructor() {
- super(DEFAULT_STATE);
+ super(undefined);
}
finalize() {}
diff --git a/polygerrit-ui/app/models/views/settings.ts b/polygerrit-ui/app/models/views/settings.ts
index 6bbf4f3..af02d0a 100644
--- a/polygerrit-ui/app/models/views/settings.ts
+++ b/polygerrit-ui/app/models/views/settings.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {GerritView} from '../../services/router/router-model';
+import {select} from '../../utils/observable-util';
import {getBaseUrl} from '../../utils/url-util';
import {define} from '../dependency';
import {Model} from '../model';
@@ -14,8 +15,6 @@
emailToken?: string;
}
-const DEFAULT_STATE: SettingsViewState = {view: GerritView.SETTINGS};
-
export function createSettingsUrl() {
return getBaseUrl() + '/settings';
}
@@ -24,10 +23,16 @@
'settings-view-model'
);
-export class SettingsViewModel extends Model<SettingsViewState> {
+export class SettingsViewModel extends Model<SettingsViewState | undefined> {
constructor() {
- super(DEFAULT_STATE);
+ super(undefined);
}
finalize() {}
+
+ public emailToken$ = select(this.state$, state => state?.emailToken);
+
+ clearToken() {
+ this.updateState({emailToken: undefined});
+ }
}
diff --git a/polygerrit-ui/app/services/gr-reporting/gr-reporting_impl.ts b/polygerrit-ui/app/services/gr-reporting/gr-reporting_impl.ts
index 106f1f3..33b9176 100644
--- a/polygerrit-ui/app/services/gr-reporting/gr-reporting_impl.ts
+++ b/polygerrit-ui/app/services/gr-reporting/gr-reporting_impl.ts
@@ -16,8 +16,7 @@
LifeCycle,
Timing,
} from '../../constants/reporting';
-import {getCLS, getFID, getLCP} from 'web-vitals';
-import {Metric} from 'web-vitals/src/types';
+import {getCLS, getFID, getLCP, Metric} from 'web-vitals';
// Latency reporting constants.