Move basic change state from router to change view model
Release-Notes: skip
Google-Bug-Id: b/247042673
Change-Id: I3c59896837b18709fbc4bb8584d3abb7354492f1
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 8d5a372..5fa959b4 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
@@ -134,7 +134,6 @@
fireReload,
fireTitleChange,
} from '../../../utils/event-util';
-import {routerModelToken} from '../../../services/router/router-model';
import {
debounce,
DelayedTask,
@@ -537,8 +536,6 @@
private readonly getChangeModel = resolve(this, changeModelToken);
- private readonly getRouterModel = resolve(this, routerModelToken);
-
private readonly getCommentsModel = resolve(this, commentsModelToken);
private readonly getConfigModel = resolve(this, configModelToken);
@@ -571,7 +568,7 @@
/** Simply reflects the router-model value. */
// visible for testing
- routerPatchNum?: PatchSetNum;
+ viewModelPatchNum?: PatchSetNum;
private readonly shortcutsController = new ShortcutController(this);
@@ -705,9 +702,9 @@
);
subscribe(
this,
- () => this.getRouterModel().routerPatchNum$,
+ () => this.getViewModel().patchNum$,
patchNum => {
- this.routerPatchNum = patchNum;
+ this.viewModelPatchNum = patchNum;
}
);
subscribe(
@@ -2649,7 +2646,7 @@
// is under change-model control. `patchRange.patchNum` should eventually
// also be model managed, so we can reconcile these two code snippets into
// one location.
- if (!this.routerPatchNum && latestPsNum === editParentRev._number) {
+ if (!this.viewModelPatchNum && latestPsNum === editParentRev._number) {
this.patchRange = {...this.patchRange, patchNum: EDIT};
// The file list is not reactive (yet) with regards to patch range
// changes, so we have to actively trigger it.
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
index cd8b9af..9b78e64 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
@@ -1987,7 +1987,7 @@
// When edit is set, but patchNum as well, then keep patchNum.
element.patchRange.patchNum = 5 as RevisionPatchSetNum;
- element.routerPatchNum = 5 as RevisionPatchSetNum;
+ element.viewModelPatchNum = 5 as RevisionPatchSetNum;
element.processEdit(change);
assert.equal(element.patchRange.patchNum, 5 as RevisionPatchSetNum);
});
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
index 5d09694..28b7e3b 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -360,13 +360,14 @@
if ('repo' in state && state.repo !== undefined && 'changeNum' in state)
this.restApiService.setInProjectLookup(state.changeNum, state.repo);
- this.routerModel.setState({
- view: state.view,
- changeNum: 'changeNum' in state ? state.changeNum : undefined,
- patchNum: 'patchNum' in state ? state.patchNum ?? undefined : undefined,
- basePatchNum:
- 'basePatchNum' in state ? state.basePatchNum ?? undefined : undefined,
- });
+ this.routerModel.setState({view: state.view});
+ // We are trying to reset the change (view) model when navigating to other
+ // views, because we don't trust our reset logic at the moment. The models
+ // singletons and might unintentionally keep state from one change to
+ // another. TODO: Let's find some way to avoid that.
+ if (state.view !== GerritView.CHANGE) {
+ this.changeViewModel.setState(undefined);
+ }
this.appElement().params = state;
}
diff --git a/polygerrit-ui/app/models/change/change-model.ts b/polygerrit-ui/app/models/change/change-model.ts
index 8282a3f..eaaf697 100644
--- a/polygerrit-ui/app/models/change/change-model.ts
+++ b/polygerrit-ui/app/models/change/change-model.ts
@@ -22,7 +22,6 @@
startWith,
switchMap,
} from 'rxjs/operators';
-import {RouterModel} from '../../services/router/router-model';
import {
computeAllPatchSets,
computeLatestPatchNum,
@@ -38,6 +37,7 @@
import {UserModel} from '../user/user-model';
import {define} from '../dependency';
import {isOwner} from '../../utils/change-util';
+import {ChangeViewModel} from '../views/change';
export enum LoadingStatus {
NOT_LOADED = 'NOT_LOADED',
@@ -76,7 +76,7 @@
export function updateChangeWithEdit(
change?: ParsedChangeInfo,
edit?: EditInfo,
- routerPatchNum?: PatchSetNum
+ viewModelPatchNum?: PatchSetNum
): ParsedChangeInfo | undefined {
if (!change || !edit) return change;
assertIsDefined(edit.commit.commit, 'edit.commit.commit');
@@ -95,7 +95,7 @@
// which is still done in change-view. `_patchRange.patchNum` should
// eventually also be model managed, so we can reconcile these two code
// snippets into one location.
- if (routerPatchNum === undefined) {
+ if (viewModelPatchNum === undefined) {
change.current_revision = edit.commit.commit;
}
return change;
@@ -103,20 +103,20 @@
/**
* Derives the base patchset number from all the data that can potentially
- * influence it. Mostly just returns `routerBasePatchNum` or PARENT, but has
+ * influence it. Mostly just returns `viewModelBasePatchNum` or PARENT, but has
* some special logic when looking at merge commits.
*
- * NOTE: At the moment this returns just `routerBasePatchNum ?? PARENT`, see
+ * NOTE: At the moment this returns just `viewModelBasePatchNum ?? PARENT`, see
* TODO below.
*/
function computeBase(
- routerBasePatchNum: BasePatchSetNum | undefined,
+ viewModelBasePatchNum: BasePatchSetNum | undefined,
patchNum: RevisionPatchSetNum | undefined,
change: ParsedChangeInfo | undefined,
preferences: PreferencesInfo
): BasePatchSetNum {
- if (routerBasePatchNum && routerBasePatchNum !== PARENT) {
- return routerBasePatchNum;
+ if (viewModelBasePatchNum && viewModelBasePatchNum !== PARENT) {
+ return viewModelBasePatchNum;
}
if (!change || !patchNum) return PARENT;
@@ -129,7 +129,7 @@
// but we are not sure whether this was ever 100% working correctly. A
// major challenge is being able to select PARENT explicitly even if your
// preference for the default choice is FIRST_PARENT. <gr-file-list-header>
- // just uses `navigation.setUrl()` and the router does not have any
+ // just uses `navigation.setUrl()` and the view model does not have any
// way of forcing the basePatchSetNum to stick to PARENT without being
// altered back to FIRST_PARENT here.
// See also corresponding TODO in gr-settings-view.
@@ -192,57 +192,57 @@
public readonly patchNum$: Observable<RevisionPatchSetNum | undefined> =
select(
combineLatest([
- this.routerModel.state$,
+ this.viewModel.state$,
this.state$,
this.latestPatchNum$,
]).pipe(
/**
- * If you depend on both, router and change state, then you want to
- * filter out inconsistent state, e.g. router changeNum already updated,
- * change not yet reset to undefined.
+ * If you depend on both, view model and change state, then you want to
+ * filter out inconsistent state, e.g. view model changeNum already
+ * updated, change not yet reset to undefined.
*/
- filter(([routerState, changeState, _latestPatchN]) => {
+ filter(([viewModelState, changeState, _latestPatchN]) => {
const changeNum = changeState.change?._number;
- const routerChangeNum = routerState.changeNum;
- return changeNum === undefined || changeNum === routerChangeNum;
+ const viewModelChangeNum = viewModelState?.changeNum;
+ return changeNum === undefined || changeNum === viewModelChangeNum;
})
),
- ([routerState, _changeState, latestPatchN]) =>
- routerState?.patchNum || latestPatchN
+ ([viewModelState, _changeState, latestPatchN]) =>
+ viewModelState?.patchNum || latestPatchN
);
/**
* Emits the base patchset number. This is identical to the
- * `routerBasePatchNum$`, but has some special logic for merges.
+ * `viewModel.basePatchNum$`, but has some special logic for merges.
*
* Note that this selector can emit without the change being available!
*/
public readonly basePatchNum$: Observable<BasePatchSetNum> =
/**
- * If you depend on both, router and change state, then you want to filter
- * out inconsistent state, e.g. router changeNum already updated, change not
- * yet reset to undefined.
+ * If you depend on both, view model and change state, then you want to
+ * filter out inconsistent state, e.g. view model changeNum already
+ * updated, change not yet reset to undefined.
*/
select(
combineLatest([
- this.routerModel.state$,
+ this.viewModel.state$,
this.state$,
this.userModel.state$,
]).pipe(
- filter(([routerState, changeState, _]) => {
+ filter(([viewModelState, changeState, _]) => {
const changeNum = changeState.change?._number;
- const routerChangeNum = routerState.changeNum;
- return changeNum === undefined || changeNum === routerChangeNum;
+ const viewModelChangeNum = viewModelState?.changeNum;
+ return changeNum === undefined || changeNum === viewModelChangeNum;
}),
withLatestFrom(
- this.routerModel.routerBasePatchNum$,
+ this.viewModel.basePatchNum$,
this.patchNum$,
this.change$,
this.userModel.preferences$
)
),
- ([_, routerBasePatchNum, patchNum, change, preferences]) =>
- computeBase(routerBasePatchNum, patchNum, change, preferences)
+ ([_, viewModelBasePatchNum, patchNum, change, preferences]) =>
+ computeBase(viewModelBasePatchNum, patchNum, change, preferences)
);
public readonly isOwner$: Observable<boolean> = select(
@@ -257,13 +257,13 @@
);
constructor(
- private readonly routerModel: RouterModel,
+ private readonly viewModel: ChangeViewModel,
private readonly restApiService: RestApiService,
private readonly userModel: UserModel
) {
super(initialState);
this.subscriptions = [
- combineLatest([this.routerModel.routerChangeNum$, this.reload$])
+ combineLatest([this.viewModel.changeNum$, this.reload$])
.pipe(
map(([changeNum, _]) => changeNum),
switchMap(changeNum => {
@@ -272,7 +272,7 @@
const edit = from(this.restApiService.getChangeEdit(changeNum));
return forkJoin([change, edit]);
}),
- withLatestFrom(this.routerModel.routerPatchNum$),
+ withLatestFrom(this.viewModel.patchNum$),
map(([[change, edit], patchNum]) =>
updateChangeWithEdit(change, edit, patchNum)
)
diff --git a/polygerrit-ui/app/models/change/change-model_test.ts b/polygerrit-ui/app/models/change/change-model_test.ts
index a2fc7c9..4bc23d8 100644
--- a/polygerrit-ui/app/models/change/change-model_test.ts
+++ b/polygerrit-ui/app/models/change/change-model_test.ts
@@ -9,6 +9,7 @@
import {
createChange,
createChangeMessageInfo,
+ createChangeViewState,
createEditInfo,
createParsedChange,
createRevision,
@@ -28,12 +29,12 @@
} from '../../types/common';
import {ParsedChangeInfo} from '../../types/types';
import {getAppContext} from '../../services/app-context';
-import {GerritView, routerModelToken} from '../../services/router/router-model';
import {ChangeState, LoadingStatus, updateChangeWithEdit} from './change-model';
import {ChangeModel} from './change-model';
import {assert} from '@open-wc/testing';
import {testResolver} from '../../test/common-test-setup';
import {userModelToken} from '../user/user-model';
+import {changeViewModelToken} from '../views/change';
suite('updateChangeWithEdit() tests', () => {
test('undefined change', async () => {
@@ -83,7 +84,7 @@
setup(() => {
changeModel = new ChangeModel(
- testResolver(routerModelToken),
+ testResolver(changeViewModelToken),
getAppContext().restApiService,
testResolver(userModelToken)
);
@@ -121,10 +122,7 @@
assert.equal(stub.callCount, 0);
assert.isUndefined(state?.change);
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: knownChange._number,
- });
+ testResolver(changeViewModelToken).setState(createChangeViewState());
state = await waitForLoadingStatus(LoadingStatus.LOADING);
assert.equal(stub.callCount, 1);
assert.isUndefined(state?.change);
@@ -140,10 +138,7 @@
const promise = mockPromise<ParsedChangeInfo | undefined>();
const stub = stubRestApi('getChangeDetail').callsFake(() => promise);
let state: ChangeState;
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: knownChange._number,
- });
+ testResolver(changeViewModelToken).setState(createChangeViewState());
promise.resolve(knownChange);
state = await waitForLoadingStatus(LoadingStatus.LOADED);
@@ -164,10 +159,7 @@
let promise = mockPromise<ParsedChangeInfo | undefined>();
const stub = stubRestApi('getChangeDetail').callsFake(() => promise);
let state: ChangeState;
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: knownChange._number,
- });
+ testResolver(changeViewModelToken).setState(createChangeViewState());
promise.resolve(knownChange);
state = await waitForLoadingStatus(LoadingStatus.LOADED);
@@ -178,8 +170,8 @@
_number: 123 as NumericChangeId,
};
promise = mockPromise<ParsedChangeInfo | undefined>();
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
+ testResolver(changeViewModelToken).setState({
+ ...createChangeViewState(),
changeNum: otherChange._number,
});
state = await waitForLoadingStatus(LoadingStatus.LOADING);
@@ -197,10 +189,7 @@
let promise = mockPromise<ParsedChangeInfo | undefined>();
const stub = stubRestApi('getChangeDetail').callsFake(() => promise);
let state: ChangeState;
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: knownChange._number,
- });
+ testResolver(changeViewModelToken).setState(createChangeViewState());
promise.resolve(knownChange);
state = await waitForLoadingStatus(LoadingStatus.LOADED);
@@ -208,10 +197,7 @@
promise = mockPromise<ParsedChangeInfo | undefined>();
promise.resolve(undefined);
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: undefined,
- });
+ testResolver(changeViewModelToken).setState(undefined);
state = await waitForLoadingStatus(LoadingStatus.NOT_LOADED);
assert.equal(stub.callCount, 2);
assert.isUndefined(state?.change);
@@ -220,10 +206,7 @@
promise = mockPromise<ParsedChangeInfo | undefined>();
promise.resolve(knownChange);
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: knownChange._number,
- });
+ testResolver(changeViewModelToken).setState(createChangeViewState());
state = await waitForLoadingStatus(LoadingStatus.LOADED);
assert.equal(stub.callCount, 3);
assert.equal(state?.change, knownChange);
@@ -299,7 +282,7 @@
assert.equal(spy.lastCall.firstArg, PARENT);
// test update
- testResolver(routerModelToken).updateState({
+ testResolver(changeViewModelToken).updateState({
basePatchNum: 1 as PatchSetNumber,
});
assert.equal(spy.callCount, 2);
diff --git a/polygerrit-ui/app/models/comments/comments-model.ts b/polygerrit-ui/app/models/comments/comments-model.ts
index 51b4591..1fdf342 100644
--- a/polygerrit-ui/app/models/comments/comments-model.ts
+++ b/polygerrit-ui/app/models/comments/comments-model.ts
@@ -29,7 +29,6 @@
} from '../../utils/comment-util';
import {deepEqual} from '../../utils/deep-util';
import {select} from '../../utils/observable-util';
-import {RouterModel} from '../../services/router/router-model';
import {define} from '../dependency';
import {combineLatest, forkJoin, from, Observable, of} from 'rxjs';
import {fire, fireAlert, fireEvent} from '../../utils/event-util';
@@ -54,6 +53,7 @@
switchMap,
} from 'rxjs/operators';
import {isDefined} from '../../types/types';
+import {ChangeViewModel} from '../views/change';
export interface CommentState {
/** undefined means 'still loading' */
@@ -415,7 +415,7 @@
private discardedDrafts: DraftInfo[] = [];
constructor(
- private readonly routerModel: RouterModel,
+ private readonly changeViewModel: ChangeViewModel,
private readonly changeModel: ChangeModel,
private readonly accountsModel: AccountsModel,
private readonly restApiService: RestApiService,
@@ -432,7 +432,7 @@
this.changeModel.patchNum$.subscribe(x => (this.patchNum = x))
);
this.subscriptions.push(
- this.routerModel.routerChangeNum$.subscribe(changeNum => {
+ this.changeViewModel.changeNum$.subscribe(changeNum => {
this.changeNum = changeNum;
this.setState({...initialState});
this.reloadAllComments();
diff --git a/polygerrit-ui/app/models/comments/comments-model_test.ts b/polygerrit-ui/app/models/comments/comments-model_test.ts
index cab36d2..a689e42 100644
--- a/polygerrit-ui/app/models/comments/comments-model_test.ts
+++ b/polygerrit-ui/app/models/comments/comments-model_test.ts
@@ -6,6 +6,7 @@
import '../../test/common-test-setup';
import {
createAccountWithEmail,
+ createChangeViewState,
createDraft,
} from '../../test/test-data-generators';
import {
@@ -20,17 +21,16 @@
import {
createComment,
createParsedChange,
- TEST_NUMERIC_CHANGE_ID,
} from '../../test/test-data-generators';
import {stubRestApi, waitUntil, waitUntilCalled} from '../../test/test-utils';
import {getAppContext} from '../../services/app-context';
-import {GerritView, routerModelToken} from '../../services/router/router-model';
import {PathToCommentsInfoMap} from '../../types/common';
import {changeModelToken} from '../change/change-model';
import {assert} from '@open-wc/testing';
import {testResolver} from '../../test/common-test-setup';
import {accountsModelToken} from '../accounts-model/accounts-model';
import {ChangeComments} from '../../elements/diff/gr-comment-api/gr-comment-api';
+import {changeViewModelToken} from '../views/change';
suite('comments model tests', () => {
test('updateStateDeleteDraft', () => {
@@ -72,7 +72,7 @@
test('loads comments', async () => {
const model = new CommentsModel(
- testResolver(routerModelToken),
+ testResolver(changeViewModelToken),
testResolver(changeModelToken),
testResolver(accountsModelToken),
getAppContext().restApiService,
@@ -100,10 +100,7 @@
model.portedComments$.subscribe(c => (portedComments = c ?? {}))
);
- testResolver(routerModelToken).setState({
- view: GerritView.CHANGE,
- changeNum: TEST_NUMERIC_CHANGE_ID,
- });
+ testResolver(changeViewModelToken).setState(createChangeViewState());
testResolver(changeModelToken).updateStateChange(createParsedChange());
await waitUntilCalled(diffCommentsSpy, 'diffCommentsSpy');
@@ -133,7 +130,7 @@
};
stubRestApi('getAccountDetails').returns(Promise.resolve(account));
const model = new CommentsModel(
- testResolver(routerModelToken),
+ testResolver(changeViewModelToken),
testResolver(changeModelToken),
testResolver(accountsModelToken),
getAppContext().restApiService,
@@ -161,7 +158,7 @@
};
stubRestApi('getAccountDetails').returns(Promise.resolve(account));
const model = new CommentsModel(
- testResolver(routerModelToken),
+ testResolver(changeViewModelToken),
testResolver(changeModelToken),
testResolver(accountsModelToken),
getAppContext().restApiService,
@@ -199,7 +196,7 @@
})
);
const model = new CommentsModel(
- testResolver(routerModelToken),
+ testResolver(changeViewModelToken),
testResolver(changeModelToken),
testResolver(accountsModelToken),
getAppContext().restApiService,
diff --git a/polygerrit-ui/app/models/views/change.ts b/polygerrit-ui/app/models/views/change.ts
index 9701b54..3f3c76b 100644
--- a/polygerrit-ui/app/models/views/change.ts
+++ b/polygerrit-ui/app/models/views/change.ts
@@ -200,6 +200,15 @@
define<ChangeViewModel>('change-view-model');
export class ChangeViewModel extends Model<ChangeViewState | undefined> {
+ public readonly changeNum$ = select(this.state$, state => state?.changeNum);
+
+ public readonly patchNum$ = select(this.state$, state => state?.patchNum);
+
+ public readonly basePatchNum$ = select(
+ this.state$,
+ state => state?.basePatchNum
+ );
+
public readonly childView$ = select(this.state$, state => state?.childView);
public readonly tab$ = select(this.state$, state => state?.tab);
diff --git a/polygerrit-ui/app/services/app-context-init.ts b/polygerrit-ui/app/services/app-context-init.ts
index 93dc131..3a15de9 100644
--- a/polygerrit-ui/app/services/app-context-init.ts
+++ b/polygerrit-ui/app/services/app-context-init.ts
@@ -148,7 +148,7 @@
changeModelToken,
() =>
new ChangeModel(
- resolver(routerModelToken),
+ resolver(changeViewModelToken),
appContext.restApiService,
resolver(userModelToken)
),
@@ -157,7 +157,7 @@
commentsModelToken,
() =>
new CommentsModel(
- resolver(routerModelToken),
+ resolver(changeViewModelToken),
resolver(changeModelToken),
resolver(accountsModelToken),
appContext.restApiService,
diff --git a/polygerrit-ui/app/services/router/router-model.ts b/polygerrit-ui/app/services/router/router-model.ts
index 18372b3..c3c1cb6 100644
--- a/polygerrit-ui/app/services/router/router-model.ts
+++ b/polygerrit-ui/app/services/router/router-model.ts
@@ -4,11 +4,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {Observable} from 'rxjs';
-import {
- NumericChangeId,
- RevisionPatchSetNum,
- BasePatchSetNum,
-} from '../../types/common';
import {Model} from '../../models/model';
import {select} from '../../utils/observable-util';
import {define} from '../../models/dependency';
@@ -29,12 +24,6 @@
export interface RouterState {
// Note that this router model view must be updated before view model state.
view?: GerritView;
- // TODO: Move into ChangeViewState.
- changeNum?: NumericChangeId;
- // TODO: Move into ChangeViewState.
- patchNum?: RevisionPatchSetNum;
- // TODO: Move into ChangeViewState.
- basePatchNum?: BasePatchSetNum;
}
export const routerModelToken = define<RouterModel>('router-model');
@@ -44,20 +33,6 @@
state => state.view
);
- // TODO: Move into ChangeViewState.
- readonly routerChangeNum$: Observable<NumericChangeId | undefined> = select(
- this.state$,
- state => state.changeNum
- );
-
- // TODO: Move into ChangeViewState.
- readonly routerPatchNum$: Observable<RevisionPatchSetNum | undefined> =
- select(this.state$, state => state.patchNum);
-
- // TODO: Move into ChangeViewState.
- readonly routerBasePatchNum$: Observable<BasePatchSetNum | undefined> =
- select(this.state$, state => state.basePatchNum);
-
constructor() {
super({});
}