Add a model for router and change

Also adds a change-service, which maintains the change model.

The new router model is maintained by gr-router, which will eventually
be moved and renamed to router-service.

Change-Id: I15fcf39d1de21dea975a687fb6efb6823d658c22
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
index c0b6074..2a607a7 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
@@ -39,7 +39,6 @@
 import {getBaseUrl} from '../../../utils/url-util';
 import {
   GerritNav,
-  GerritView,
   GroupDetailView,
   RepoDetailView,
 } from '../../core/gr-navigation/gr-navigation';
@@ -66,6 +65,7 @@
 import {ValueChangeDetail} from '../../shared/gr-dropdown-list/gr-dropdown-list';
 import {GrJsApiInterface} from '../../shared/gr-js-api-interface/gr-js-api-interface-element';
 import {appContext} from '../../../services/app-context';
+import {GerritView} from '../../../services/router/router-model';
 
 const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
 
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
index 15211cf..cac409d 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view_test.js
@@ -18,9 +18,10 @@
 import '../../../test/common-test-setup-karma.js';
 import './gr-admin-view.js';
 import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
-import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation.js';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
 import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader.js';
 import {stubBaseUrl} from '../../../test/test-utils.js';
+import {GerritView} from '../../../services/router/router-model.js';
 
 const basicFixture = fixtureFromElement('gr-admin-view');
 
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
index 26368ec..973ccc8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
@@ -25,7 +25,7 @@
 import {PolymerElement} from '@polymer/polymer/polymer-element';
 import {htmlTemplate} from './gr-change-list-view_html';
 import {page} from '../../../utils/page-wrapper-utils';
-import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {customElement, property} from '@polymer/decorators';
 import {AppElementParams} from '../../gr-app-types';
 import {
@@ -41,6 +41,7 @@
 import {ChangeListViewState} from '../../../types/types';
 import {fireTitleChange} from '../../../utils/event-util';
 import {appContext} from '../../../services/app-context';
+import {GerritView} from '../../../services/router/router-model';
 
 const LookupQueryPatterns = {
   CHANGE_ID: /^\s*i?[0-9a-f]{7,40}\s*$/i,
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 f3ed408..0a87503 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
@@ -29,7 +29,6 @@
 import {htmlTemplate} from './gr-dashboard-view_html';
 import {
   GerritNav,
-  GerritView,
   UserDashboard,
   YOUR_TURN,
 } from '../../core/gr-navigation/gr-navigation';
@@ -57,6 +56,7 @@
 import {ChangeStarToggleStarDetail} from '../../shared/gr-change-star/gr-change-star';
 import {DashboardViewState} from '../../../types/types';
 import {firePageError, fireTitleChange} from '../../../utils/event-util';
+import {GerritView} from '../../../services/router/router-model';
 
 const PROJECT_PLACEHOLDER_PATTERN = /\$\{project\}/g;
 
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js
index 1fa24ba..1f5d519 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.js
@@ -18,7 +18,8 @@
 import '../../../test/common-test-setup-karma.js';
 import './gr-dashboard-view.js';
 import {isHidden} from '../../../test/test-utils.js';
-import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation.js';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation.js';
+import {GerritView} from '../../../services/router/router-model.js';
 import {changeIsOpen} from '../../../utils/change-util.js';
 import {ChangeStatus} from '../../../constants/constants.js';
 import {createAccountWithId} from '../../../test/test-data-generators.js';
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 df52323..950fbf3 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
@@ -55,7 +55,7 @@
 import {GrEditConstants} from '../../edit/gr-edit-constants';
 import {pluralize} from '../../../utils/string-util';
 import {getComputedStyleValue} from '../../../utils/dom-util';
-import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {getPluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints';
 import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader';
 import {RevisionInfo as RevisionInfoClass} from '../../shared/revision-info/revision-info';
@@ -152,6 +152,7 @@
 import {fireAlert, fireEvent, firePageError} from '../../../utils/event-util';
 import {KnownExperimentId} from '../../../services/flags/flags';
 import {fireTitleChange} from '../../../utils/event-util';
+import {GerritView} from '../../../services/router/router-model';
 
 const CHANGE_ID_ERROR = {
   MISMATCH: 'mismatch',
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 5b4e65d..5cb84b4 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
@@ -30,7 +30,7 @@
 import {GrEditConstants} from '../../edit/gr-edit-constants';
 import {_testOnly_resetEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints';
 import {getComputedStyleValue} from '../../../utils/dom-util';
-import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader';
 import {_testOnly_initGerritPluginApi} from '../../shared/gr-js-api-interface/gr-gerrit';
 import {EventType, PluginApi} from '../../plugins/gr-plugin-types';
@@ -102,6 +102,7 @@
 import 'lodash/lodash';
 import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
 import {ChangeComments} from '../../diff/gr-comment-api/gr-comment-api';
+import {GerritView} from '../../../services/router/router-model';
 
 const pluginApi = _testOnly_initGerritPluginApi();
 const fixture = fixtureFromElement('gr-change-view');
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
index 8470611..83bf3ca 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts
@@ -16,23 +16,24 @@
  */
 import {
   BranchName,
+  ChangeConfigInfo,
   ChangeInfo,
+  CommentLinks,
+  CommitId,
+  DashboardId,
+  EditPatchSetNum,
+  GroupId,
+  Hashtag,
+  NumericChangeId,
+  ParentPatchSetNum,
   PatchSetNum,
   RepoName,
-  TopicName,
-  GroupId,
-  DashboardId,
-  NumericChangeId,
-  EditPatchSetNum,
-  ChangeConfigInfo,
-  CommitId,
-  Hashtag,
-  UrlEncodedCommentId,
-  CommentLinks,
-  ParentPatchSetNum,
   ServerInfo,
+  TopicName,
+  UrlEncodedCommentId,
 } from '../../../types/common';
 import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser';
+import {GerritView} from '../../../services/router/router-model';
 
 // Navigation parameters object format:
 //
@@ -396,22 +397,6 @@
   url?: string;
 }
 
-export enum GerritView {
-  ADMIN = 'admin',
-  AGREEMENTS = 'agreements',
-  CHANGE = 'change',
-  DASHBOARD = 'dashboard',
-  DIFF = 'diff',
-  DOCUMENTATION_SEARCH = 'documentation-search',
-  EDIT = 'edit',
-  GROUP = 'group',
-  PLUGIN_SCREEN = 'plugin-screen',
-  REPO = 'repo',
-  ROOT = 'root',
-  SEARCH = 'search',
-  SETTINGS = 'settings',
-}
-
 export enum GroupDetailView {
   MEMBERS = 'members',
   LOG = 'log',
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 0665cca..07045cd 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -26,6 +26,7 @@
 import {encodeURL, getBaseUrl} from '../../../utils/url-util';
 import {
   DashboardSection,
+  GeneratedWebLink,
   GenerateUrlChangeViewParameters,
   GenerateUrlDashboardViewParameters,
   GenerateUrlDiffViewParameters,
@@ -38,18 +39,16 @@
   GenerateWebLinksFileParameters,
   GenerateWebLinksParameters,
   GenerateWebLinksPatchsetParameters,
-  GerritView,
+  GerritNav,
+  GroupDetailView,
   isGenerateUrlDiffViewParameters,
   RepoDetailView,
   WeblinkType,
-  GroupDetailView,
-  GerritNav,
-  GeneratedWebLink,
 } from '../gr-navigation/gr-navigation';
 import {appContext} from '../../../services/app-context';
 import {
-  patchNumEquals,
   convertToPatchSetNum,
+  patchNumEquals,
 } from '../../../utils/patch-set-util';
 import {customElement, property} from '@polymer/decorators';
 import {assertNever} from '../../../utils/common-util';
@@ -64,10 +63,11 @@
 } from '../../../types/common';
 import {
   AppElement,
-  AppElementParams,
   AppElementAgreementParam,
+  AppElementParams,
 } from '../../gr-app-types';
 import {LocationChangeEventDetail} from '../../../types/events';
+import {GerritView, updateState} from '../../../services/router/router-model';
 
 const RoutePattern = {
   ROOT: '/',
@@ -308,8 +308,13 @@
 
   private readonly restApiService = appContext.restApiService;
 
+  private readonly changeService = appContext.changeService;
+
   constructor() {
     super();
+    // TODO: This is just an artificical dependdency such that the service is
+    // instantiated and its observables subscribed. Remove this later.
+    this.changeService.dontDoAnything();
   }
 
   start() {
@@ -320,6 +325,11 @@
   }
 
   _setParams(params: AppElementParams | GenerateUrlParameters) {
+    updateState(
+      params.view,
+      'changeNum' in params ? params.changeNum : undefined,
+      'patchNum' in params ? params.patchNum ?? undefined : undefined
+    );
     this._appElement().params = params;
   }
 
@@ -1535,8 +1545,7 @@
     // Parameter order is based on the regex group number matched.
     const params: GenerateUrlChangeViewParameters = {
       project: ctx.params[0] as RepoName,
-      // TODO(TS): remove as unknown
-      changeNum: (ctx.params[1] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[1]) as NumericChangeId,
       basePatchNum: convertToPatchSetNum(ctx.params[4]),
       patchNum: convertToPatchSetNum(ctx.params[6]),
       view: GerritView.CHANGE,
@@ -1550,7 +1559,7 @@
   _handleCommentRoute(ctx: PageContextWithQueryMap) {
     const params: GenerateUrlDiffViewParameters = {
       project: ctx.params[0] as RepoName,
-      changeNum: (ctx.params[1] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[1]) as NumericChangeId,
       commentId: ctx.params[2] as UrlEncodedCommentId,
       view: GerritView.DIFF,
       commentLink: true,
@@ -1563,7 +1572,7 @@
     // Parameter order is based on the regex group number matched.
     const params: GenerateUrlDiffViewParameters = {
       project: ctx.params[0] as RepoName,
-      changeNum: (ctx.params[1] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[1]) as NumericChangeId,
       basePatchNum: convertToPatchSetNum(ctx.params[4]),
       patchNum: convertToPatchSetNum(ctx.params[6]),
       path: ctx.params[8],
@@ -1581,7 +1590,7 @@
   _handleChangeLegacyRoute(ctx: PageContextWithQueryMap) {
     // Parameter order is based on the regex group number matched.
     const params: GenerateUrlLegacyChangeViewParameters = {
-      changeNum: (ctx.params[0] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[0]) as NumericChangeId,
       basePatchNum: convertToPatchSetNum(ctx.params[3]),
       patchNum: convertToPatchSetNum(ctx.params[5]),
       view: GerritView.CHANGE,
@@ -1598,8 +1607,7 @@
   _handleDiffLegacyRoute(ctx: PageContextWithQueryMap) {
     // Parameter order is based on the regex group number matched.
     const params: GenerateUrlLegacyDiffViewParameters = {
-      // TODO(TS): remove "as unknown"
-      changeNum: (ctx.params[0] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[0]) as NumericChangeId,
       basePatchNum: convertToPatchSetNum(ctx.params[2]),
       patchNum: convertToPatchSetNum(ctx.params[4]),
       path: ctx.params[5],
@@ -1620,7 +1628,7 @@
     const project = ctx.params[0] as RepoName;
     this._redirectOrNavigate({
       project,
-      changeNum: (ctx.params[1] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[1]) as NumericChangeId,
       // for edit view params, patchNum cannot be undefined
       patchNum: convertToPatchSetNum(ctx.params[2])!,
       path: ctx.params[3],
@@ -1635,8 +1643,7 @@
     const project = ctx.params[0] as RepoName;
     this._redirectOrNavigate({
       project,
-      // TODO(TS): remove "as unknown"
-      changeNum: (ctx.params[1] as unknown) as NumericChangeId,
+      changeNum: Number(ctx.params[1]) as NumericChangeId,
       patchNum: convertToPatchSetNum(ctx.params[3]),
       view: GerritView.CHANGE,
       edit: true,
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js
index cdec405..ff8f8df 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js
@@ -1540,7 +1540,7 @@
           ]);
           assertDataToParams({params: groups.slice(1)}, '_handleCommentRoute', {
             project: 'gerrit',
-            changeNum: '264833',
+            changeNum: 264833,
             commentId: '00049681_f34fd6a9',
             commentLink: true,
             view: GerritNav.View.DIFF,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index 853d3c6..36317db 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -40,7 +40,7 @@
   Shortcut,
 } from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {pluralize} from '../../../utils/string-util';
-import {GerritNav, GerritView} from '../../core/gr-navigation/gr-navigation';
+import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {appContext} from '../../../services/app-context';
 import {
   computeAllPatchSets,
@@ -97,6 +97,7 @@
 import {fireAlert, fireTitleChange} from '../../../utils/event-util';
 
 import {KnownExperimentId} from '../../../services/flags/flags';
+import {GerritView} from '../../../services/router/router-model';
 const ERR_REVIEW_STATUS = 'Couldn’t change file review status.';
 const MSG_LOADING_BLAME = 'Loading blame...';
 const MSG_LOADED_BLAME = 'Blame loaded';
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
index 1cb22e8..92494c2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
@@ -23,7 +23,7 @@
 import {SPECIAL_PATCH_SET_NUM} from '../../../utils/patch-set-util.js';
 import {Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
 import {_testOnly_findCommentById} from '../gr-comment-api/gr-comment-api.js';
-import {GerritView} from '../../core/gr-navigation/gr-navigation.js';
+import {GerritView} from '../../../services/router/router-model.js';
 import {
   createChange,
   createRevisions,
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index feca35c..1d42bba 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -48,7 +48,7 @@
   Shortcut,
   SPECIAL_SHORTCUT,
 } from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
-import {GerritNav, GerritView} from './core/gr-navigation/gr-navigation';
+import {GerritNav} from './core/gr-navigation/gr-navigation';
 import {appContext} from '../services/app-context';
 import {flush} from '@polymer/polymer/lib/utils/flush';
 import {customElement, observe, property} from '@polymer/decorators';
@@ -78,6 +78,7 @@
 } from '../types/events';
 import {ViewState} from '../types/types';
 import {EventType} from '../utils/event-util';
+import {GerritView} from '../services/router/router-model';
 
 interface ErrorInfo {
   text: string;
diff --git a/polygerrit-ui/app/elements/gr-app-types.ts b/polygerrit-ui/app/elements/gr-app-types.ts
index b05117f..4809562 100644
--- a/polygerrit-ui/app/elements/gr-app-types.ts
+++ b/polygerrit-ui/app/elements/gr-app-types.ts
@@ -16,7 +16,6 @@
  */
 import {
   GenerateUrlParameters,
-  GerritView,
   GroupDetailView,
   RepoDetailView,
 } from './core/gr-navigation/gr-navigation';
@@ -28,6 +27,7 @@
   RepoName,
   UrlEncodedCommentId,
 } from '../types/common';
+import {GerritView} from '../services/router/router-model';
 
 export interface AppElement extends HTMLElement {
   params: AppElementParams | GenerateUrlParameters;
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 e428a0a..e5e8537 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
@@ -63,11 +63,11 @@
 } from '../../../types/common';
 import {GrSshEditor} from '../gr-ssh-editor/gr-ssh-editor';
 import {GrGpgEditor} from '../gr-gpg-editor/gr-gpg-editor';
-import {GerritView} from '../../core/gr-navigation/gr-navigation';
 import {GrEmailEditor} from '../gr-email-editor/gr-email-editor';
 import {CustomKeyboardEvent} from '../../../types/events';
 import {fireAlert, fireTitleChange} from '../../../utils/event-util';
 import {appContext} from '../../../services/app-context';
+import {GerritView} from '../../../services/router/router-model';
 
 const PREFS_SECTION_FIELDS: Array<keyof PreferencesInput> = [
   'changes_per_page',
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
index a01edac..62e2a9e 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.js
@@ -19,7 +19,7 @@
 import {getComputedStyleValue} from '../../../utils/dom-util.js';
 import './gr-settings-view.js';
 import {flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
-import {GerritView} from '../../core/gr-navigation/gr-navigation.js';
+import {GerritView} from '../../../services/router/router-model.js';
 
 const basicFixture = fixtureFromElement('gr-settings-view');
 const blankFixture = fixtureFromElement('div');