Revert "Compute diff URLs in the view model"

This reverts commit 5e4738de4e73c2ebb3b195ae611e9009088bfc95.

Reason for revert: per https://gerrit-review.googlesource.com/c/gerrit/+/429638

Change-Id: I03c807706bb13233f51841ab743deeff2d9c19d5
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 781993e..ed42b3b 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
@@ -145,6 +145,7 @@
   changeViewModelToken,
   ChangeViewState,
   createChangeUrl,
+  createEditUrl,
 } from '../../../models/views/change';
 import {rootUrl} from '../../../utils/url-util';
 import {userModelToken} from '../../../models/user/user-model';
@@ -2398,8 +2399,14 @@
         controls.openDeleteDialog(path);
         break;
       case GrEditConstants.Actions.OPEN.id:
+        assertIsDefined(this.patchNum, 'patchset number');
         this.getNavigation().setUrl(
-          this.getViewModel().editUrl({editView: {path}})
+          createEditUrl({
+            changeNum: this.change._number,
+            repo: this.change.project,
+            patchNum: this.patchNum,
+            editView: {path},
+          })
         );
         break;
       case GrEditConstants.Actions.RENAME.id:
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 7b9c291..1fd4aa2 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
@@ -73,10 +73,7 @@
 import {Modifier} from '../../../utils/dom-util';
 import {GrButton} from '../../shared/gr-button/gr-button';
 import {GrCopyLinks} from '../gr-copy-links/gr-copy-links';
-import {
-  ChangeChildView,
-  changeViewModelToken,
-} from '../../../models/views/change';
+import {ChangeChildView} from '../../../models/views/change';
 import {rootUrl} from '../../../utils/url-util';
 import {testResolver} from '../../../test/common-test-setup';
 import {UserModel, userModelToken} from '../../../models/user/user-model';
@@ -311,12 +308,6 @@
 
   setup(async () => {
     setUrlStub = sinon.stub(testResolver(navigationToken), 'setUrl');
-    sinon
-      .stub(testResolver(changeViewModelToken), 'editUrl')
-      .returns('fakeEditUrl');
-    sinon
-      .stub(testResolver(changeViewModelToken), 'diffUrl')
-      .returns('fakeDiffUrl');
 
     stubRestApi('getConfig').returns(
       Promise.resolve({
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 4ceca90..7a09518 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -80,8 +80,9 @@
 import {incrementalRepeat} from '../../lit/incremental-repeat';
 import {ifDefined} from 'lit/directives/if-defined.js';
 import {
+  createDiffUrl,
+  createEditUrl,
   createChangeUrl,
-  changeViewModelToken,
 } from '../../../models/views/change';
 import {userModelToken} from '../../../models/user/user-model';
 import {pluginLoaderToken} from '../../shared/gr-js-api-interface/gr-plugin-loader';
@@ -211,7 +212,7 @@
   diffViewMode?: DiffViewMode;
 
   @property({type: Boolean})
-  editMode = false;
+  editMode?: boolean;
 
   private _filesExpanded = FilesExpandedState.NONE;
 
@@ -312,8 +313,6 @@
 
   private readonly getNavigation = resolve(this, navigationToken);
 
-  private readonly getViewModel = resolve(this, changeViewModelToken);
-
   // private but used in test
   fileCursor = new GrCursorManager();
 
@@ -2172,7 +2171,10 @@
       throw new Error('change, diff and patchRange must be all set and valid');
     }
     this.getNavigation().setUrl(
-      this.getViewModel().diffUrl({
+      createDiffUrl({
+        change: this.change,
+        patchNum: this.patchRange.patchNum,
+        basePatchNum: this.patchRange.basePatchNum,
         diffView: {path: diff.path},
       })
     );
@@ -2190,7 +2192,10 @@
       throw new Error('change and patchRange must be set');
     }
     this.getNavigation().setUrl(
-      this.getViewModel().diffUrl({
+      createDiffUrl({
+        change: this.change,
+        patchNum: this.patchRange.patchNum,
+        basePatchNum: this.patchRange.basePatchNum,
         diffView: {path: this.files[this.fileCursor.index].__path},
       })
     );
@@ -2209,16 +2214,31 @@
     );
   }
 
-  /** Returns an edit or diff URL depending on `editMode`. */
   // Private but used in tests
-  computeDiffURL(path?: string): string | undefined {
-    if (path === undefined) {
+  computeDiffURL(path?: string) {
+    if (
+      this.change === undefined ||
+      this.patchRange?.patchNum === undefined ||
+      path === undefined ||
+      this.editMode === undefined
+    ) {
       return;
     }
     if (this.editMode && path !== SpecialFilePath.MERGE_LIST) {
-      return this.getViewModel().editUrl({editView: {path}});
+      return createEditUrl({
+        changeNum: this.change._number,
+        repo: this.change.project,
+        patchNum: this.patchRange.patchNum,
+        editView: {path},
+      });
     }
-    return this.getViewModel().diffUrl({diffView: {path}});
+    return createDiffUrl({
+      changeNum: this.change._number,
+      repo: this.change.project,
+      patchNum: this.patchRange.patchNum,
+      basePatchNum: this.patchRange.basePatchNum,
+      diffView: {path},
+    });
   }
 
   // Private but used in tests.
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
index 7f51a40..eec829a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
@@ -60,11 +60,6 @@
 import {FileMode} from '../../../utils/file-util';
 import {SinonStubbedMember} from 'sinon';
 import {GrDiffCursor} from '../../../embed/diff/gr-diff-cursor/gr-diff-cursor';
-import {
-  ChangeChildView,
-  changeViewModelToken,
-} from '../../../models/views/change';
-import {GerritView} from '../../../services/router/router-model';
 
 suite('gr-diff a11y test', () => {
   test('audit', async () => {
@@ -84,16 +79,6 @@
   let element: GrFileList;
   let saveStub: sinon.SinonStub;
 
-  setup(async () => {
-    testResolver(changeViewModelToken).setState({
-      view: GerritView.CHANGE,
-      childView: ChangeChildView.OVERVIEW,
-      changeNum: 42 as NumericChangeId,
-      repo: 'gerrit' as RepoName,
-      patchNum: 1 as RevisionPatchSetNum,
-    });
-  });
-
   suite('basic tests', async () => {
     setup(async () => {
       stubRestApi('getDiffComments').returns(Promise.resolve({}));
@@ -194,7 +179,7 @@
             <gr-file-status></gr-file-status>
           </div>
           <span class="path" role="gridcell">
-            <a class="pathLink" href="/c/gerrit/+/42/1/path/file0">
+            <a class="pathLink">
               <span class="fullFileName" title="path/file0">
                 <span class="newFilePath"> path/ </span>
                 <span class="fileName"> file0 </span>
@@ -314,7 +299,7 @@
         fileRows[0].querySelector('.path'),
         /* HTML */ `
           <span class="path" role="gridcell">
-            <a class="pathLink" href="/c/gerrit/+/42/1/path/file0">
+            <a class="pathLink">
               <span class="fullFileName" title="path/file0">
                 <span class="newFilePath"> path/ </span>
                 <span class="fileName"> file0 </span>
@@ -332,7 +317,7 @@
         fileRows[1].querySelector('.path'),
         /* HTML */ `
           <span class="path" role="gridcell">
-            <a class="pathLink" href="/c/gerrit/+/42/1/path/file1">
+            <a class="pathLink">
               <span class="fullFileName" title="path/file1">
                 <span class="matchingFilePath"> path/ </span>
                 <span class="fileName"> file1 </span>
@@ -962,10 +947,10 @@
         ];
         element.changeNum = 42 as NumericChangeId;
         element.basePatchNum = PARENT;
-        element.patchNum = 1 as RevisionPatchSetNum;
+        element.patchNum = 2 as RevisionPatchSetNum;
         element.change = {
           _number: 42 as NumericChangeId,
-          project: 'gerrit',
+          project: 'test-project',
         } as ParsedChangeInfo;
         element.fileCursor.setCursorAtIndex(0);
         await element.updateComplete;
@@ -1024,7 +1009,7 @@
         assert.equal(setUrlStub.callCount, 1);
         assert.equal(
           setUrlStub.lastCall.firstArg,
-          '/c/gerrit/+/42/1/file_added_in_rev2.txt'
+          '/c/test-project/+/42/2/file_added_in_rev2.txt'
         );
 
         pressKey(element, 'k');
@@ -1720,24 +1705,35 @@
 
   suite('diff url file list', () => {
     test('diff url', () => {
+      element.change = {
+        ...createParsedChange(),
+        _number: 1 as NumericChangeId,
+        project: 'gerrit' as RepoName,
+      };
+      element.basePatchNum = PARENT;
+      element.patchNum = 1 as RevisionPatchSetNum;
       const path = 'index.php';
       element.editMode = false;
-      assert.equal(element.computeDiffURL(path), '/c/gerrit/+/42/1/index.php');
+      assert.equal(element.computeDiffURL(path), '/c/gerrit/+/1/1/index.php');
     });
 
     test('diff url commit msg', () => {
-      const path = '/COMMIT_MSG';
+      element.change = {
+        ...createParsedChange(),
+        _number: 1 as NumericChangeId,
+        project: 'gerrit' as RepoName,
+      };
+      element.basePatchNum = PARENT;
+      element.patchNum = 1 as RevisionPatchSetNum;
       element.editMode = false;
-      assert.equal(
-        element.computeDiffURL(path),
-        '/c/gerrit/+/42/1//COMMIT_MSG'
-      );
+      const path = '/COMMIT_MSG';
+      assert.equal(element.computeDiffURL(path), '/c/gerrit/+/1/1//COMMIT_MSG');
     });
 
     test('edit url', () => {
       element.change = {
         ...createParsedChange(),
-        _number: 42 as NumericChangeId,
+        _number: 1 as NumericChangeId,
         project: 'gerrit' as RepoName,
       };
       element.basePatchNum = PARENT;
@@ -1746,14 +1742,14 @@
       const path = 'index.php';
       assert.equal(
         element.computeDiffURL(path),
-        '/c/gerrit/+/42/1/index.php,edit'
+        '/c/gerrit/+/1/1/index.php,edit'
       );
     });
 
     test('edit url commit msg', () => {
       element.change = {
         ...createParsedChange(),
-        _number: 42 as NumericChangeId,
+        _number: 1 as NumericChangeId,
         project: 'gerrit' as RepoName,
       };
       element.basePatchNum = PARENT;
@@ -1762,7 +1758,7 @@
       const path = '/COMMIT_MSG';
       assert.equal(
         element.computeDiffURL(path),
-        '/c/gerrit/+/42/1//COMMIT_MSG,edit'
+        '/c/gerrit/+/1/1//COMMIT_MSG,edit'
       );
     });
   });
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index d0e44c5..72d3268 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -58,7 +58,6 @@
 import {
   DropdownLink,
   LabelNameToInfoMap,
-  PARENT,
   PatchSetNumber,
 } from '../../types/common';
 import {spinnerStyles} from '../../styles/gr-spinner-styles';
@@ -79,7 +78,7 @@
 import {when} from 'lit/directives/when.js';
 import {DropdownItem} from '../shared/gr-dropdown-list/gr-dropdown-list';
 import './gr-checks-attempt';
-import {changeViewModelToken} from '../../models/views/change';
+import {createDiffUrl, changeViewModelToken} from '../../models/views/change';
 import {formStyles} from '../../styles/form-styles';
 
 /**
@@ -636,8 +635,6 @@
 
   private getChangeModel = resolve(this, changeModelToken);
 
-  private readonly getViewModel = resolve(this, changeViewModelToken);
-
   static override get styles() {
     return [
       sharedStyles,
@@ -722,8 +719,9 @@
       return {
         icon: LinkIcon.CODE,
         tooltip: `${path}${rangeText}`,
-        url: this.getViewModel().diffUrl({
-          basePatchNum: PARENT,
+        url: createDiffUrl({
+          changeNum: change._number,
+          repo: change.project,
           patchNum: patchset,
           checksPatchset: patchset,
           diffView: {path, lineNum: line},
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 81442c3..ee2a806 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
@@ -92,6 +92,7 @@
 import {when} from 'lit/directives/when.js';
 import {styleMap} from 'lit/directives/style-map.js';
 import {
+  createDiffUrl,
   ChangeChildView,
   changeViewModelToken,
 } from '../../../models/views/change';
@@ -1456,7 +1457,7 @@
     if (!newPath) return;
     if (newPath.up) return this.getChangeModel().changeUrl();
     if (!newPath.path) return;
-    return this.getViewModel().diffUrl({diffView: {path: newPath.path}});
+    return this.getChangeModel().diffUrl({path: newPath.path});
   }
 
   private goToEditFile() {
@@ -1503,8 +1504,15 @@
   }
 
   private updateUrlToDiffUrl(lineNum?: number, leftSide?: boolean) {
+    if (!this.change) return;
+    if (!this.patchNum) return;
+    if (!this.changeNum) return;
     if (!this.path) return;
-    const url = this.getViewModel().diffUrl({
+    const url = createDiffUrl({
+      changeNum: this.changeNum,
+      repo: this.change.project,
+      patchNum: this.patchNum,
+      basePatchNum: this.basePatchNum,
       diffView: {
         path: this.path,
         lineNum,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
index 96467b4..93424b1 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
@@ -137,9 +137,9 @@
       stubRestApi('getDiffDrafts').returns(Promise.resolve({}));
       stubRestApi('getPortedComments').returns(Promise.resolve({}));
 
+      element = await fixture(html`<gr-diff-view></gr-diff-view>`);
       viewModel = testResolver(changeViewModelToken);
       viewModel.setState(createDiffViewState());
-      element = await fixture(html`<gr-diff-view></gr-diff-view>`);
       await waitUntil(() => element.changeNum === TEST_NUMERIC_CHANGE_ID);
       element.path = 'some/path.txt';
       element.change = createParsedChange();
@@ -188,18 +188,16 @@
 
     test('renders', async () => {
       browserModel.setScreenWidth(0);
-      const patchNum = 10 as RevisionPatchSetNum;
-      element.patchNum = patchNum;
+      element.patchNum = 10 as RevisionPatchSetNum;
       element.basePatchNum = PARENT;
       const change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
         revisions: {
-          a: createRevision(patchNum),
+          a: createRevision(10),
         },
       };
       changeModel.updateStateChange(change);
-      viewModel.updateState({patchNum});
       element.files = getFilesFromFileList([
         'chell.go',
         'glados.txt',
@@ -1014,16 +1012,14 @@
       });
 
       test('prev/up/next links', async () => {
-        const patchNum = 10 as RevisionPatchSetNum;
         viewModel.setState({
           ...createDiffViewState(),
-          patchNum,
         });
         const change = {
           ...createParsedChange(),
           _number: 42 as NumericChangeId,
           revisions: {
-            a: createRevision(patchNum),
+            a: createRevision(10),
           },
         };
         changeModel.updateStateChange(change);
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 d6f55ff..1e871ce 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
@@ -29,7 +29,7 @@
 import {customElement, property, query, state} from 'lit/decorators.js';
 import {BindValueChangeEvent} from '../../../types/events';
 import {IronInputElement} from '@polymer/iron-input/iron-input';
-import {changeViewModelToken} from '../../../models/views/change';
+import {createEditUrl} from '../../../models/views/change';
 import {resolve} from '../../../models/dependency';
 import {modalStyles} from '../../../styles/gr-modal-styles';
 import {whenVisible} from '../../../utils/dom-util';
@@ -81,8 +81,6 @@
 
   private readonly getChangeModel = resolve(this, changeModelToken);
 
-  private readonly getViewModel = resolve(this, changeViewModelToken);
-
   private readonly getNavigation = resolve(this, navigationToken);
 
   static override get styles() {
@@ -433,7 +431,13 @@
       return;
     }
     assertIsDefined(this.patchNum, 'patchset number');
-    const url = this.getViewModel().editUrl({editView: {path: this.path}});
+    const url = createEditUrl({
+      changeNum: this.change._number,
+      repo: this.change.project,
+      patchNum: this.patchNum,
+      editView: {path: this.path},
+    });
+
     this.getNavigation().setUrl(url);
     this.closeDialog(this.getDialogFromEvent(e));
   };
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts
index aacbb36..0e6778a 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.ts
@@ -32,11 +32,6 @@
   changeModelToken,
 } from '../../../models/change/change-model';
 import {SinonStubbedMember} from 'sinon';
-import {
-  ChangeChildView,
-  changeViewModelToken,
-} from '../../../models/views/change';
-import {GerritView} from '../../../services/router/router-model';
 
 suite('gr-edit-controls tests', () => {
   let element: GrEditControls;
@@ -50,13 +45,6 @@
   >;
 
   setup(async () => {
-    testResolver(changeViewModelToken).setState({
-      view: GerritView.CHANGE,
-      childView: ChangeChildView.OVERVIEW,
-      changeNum: 42 as NumericChangeId,
-      repo: 'gerrit' as RepoName,
-    });
-
     element = await fixture<GrEditControls>(html`
       <gr-edit-controls></gr-edit-controls>
     `);
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index 5bd822b..0b03581 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -70,11 +70,7 @@
 import {commentsModelToken} from '../../../models/comments/comments-model';
 import {changeModelToken} from '../../../models/change/change-model';
 import {whenRendered} from '../../../utils/dom-util';
-import {
-  changeViewModelToken,
-  createChangeUrl,
-  createDiffUrl,
-} from '../../../models/views/change';
+import {createChangeUrl, createDiffUrl} from '../../../models/views/change';
 import {userModelToken} from '../../../models/user/user-model';
 import {highlightServiceToken} from '../../../services/highlight/highlight-service';
 import {noAwait, waitUntil} from '../../../utils/async-util';
@@ -252,8 +248,6 @@
 
   private readonly getUserModel = resolve(this, userModelToken);
 
-  private readonly getViewModel = resolve(this, changeViewModelToken);
-
   private readonly shortcuts = new ShortcutController(this);
 
   private readonly syntaxLayer = new GrSyntaxLayerWorker(
@@ -715,7 +709,9 @@
     if (!this.changeNum || !this.repoName || !this.thread?.path) {
       return undefined;
     }
-    return this.getViewModel().diffUrl({
+    return createDiffUrl({
+      changeNum: this.changeNum,
+      repo: this.repoName,
       patchNum: this.thread.patchNum,
       diffView: {path: this.thread.path},
     });
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
index b56fcfd..571a322 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_test.ts
@@ -38,11 +38,6 @@
   commentsModelToken,
 } from '../../../models/comments/comments-model';
 import {testResolver} from '../../../test/common-test-setup';
-import {
-  ChangeChildView,
-  changeViewModelToken,
-} from '../../../models/views/change';
-import {GerritView} from '../../../services/router/router-model';
 
 const c1: CommentInfo = {
   author: {name: 'Kermit'},
@@ -85,12 +80,6 @@
   let element: GrCommentThread;
 
   setup(async () => {
-    testResolver(changeViewModelToken).setState({
-      view: GerritView.CHANGE,
-      childView: ChangeChildView.OVERVIEW,
-      changeNum: 1 as NumericChangeId,
-      repo: 'test-repo-name' as RepoName,
-    });
     stubRestApi('getLoggedIn').returns(Promise.resolve(false));
     element = await fixture(html`<gr-comment-thread></gr-comment-thread>`);
     element.changeNum = 1 as NumericChangeId;
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.ts b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.ts
index c1a76a5..3964422 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.ts
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.ts
@@ -43,7 +43,7 @@
 import {storageServiceToken} from '../../../services/storage/gr-storage_impl';
 import {resolve} from '../../../models/dependency';
 import {formStyles} from '../../../styles/form-styles';
-import {changeViewModelToken} from '../../../models/views/change';
+import {createEditUrl} from '../../../models/views/change';
 import {SpecialFilePath} from '../../../constants/constants';
 
 const RESTORED_MESSAGE = 'Content restored from a previous edit.';
@@ -128,8 +128,6 @@
 
   private readonly getNavigation = resolve(this, navigationToken);
 
-  private readonly getViewModel = resolve(this, changeViewModelToken);
-
   // Tests use this so needs to be non private
   storeTask?: DelayedTask;
 
@@ -503,7 +501,10 @@
       assertIsDefined(this.changeNum, 'changeNum');
       assertIsDefined(this.repoName, 'repoName');
       this.getNavigation().setUrl(
-        this.getViewModel().editUrl({
+        createEditUrl({
+          changeNum: this.changeNum,
+          repo: this.repoName,
+          patchNum: this.patchNum,
           editView: {path: SpecialFilePath.COMMIT_MESSAGE},
         })
       );
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.ts b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.ts
index 4243042..e8ccdd1 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.ts
@@ -14,8 +14,11 @@
 import {testResolver} from '../../../test/common-test-setup';
 import {GrDropdownList} from '../gr-dropdown-list/gr-dropdown-list';
 import {navigationToken} from '../../core/gr-navigation/gr-navigation';
-import {NumericChangeId, RepoName} from '../../../api/rest-api';
-import {changeViewModelToken} from '../../../models/views/change';
+import {
+  NumericChangeId,
+  RepoName,
+  RevisionPatchSetNum,
+} from '../../../api/rest-api';
 
 const emails = [
   {
@@ -186,15 +189,11 @@
 
     suite('in editMode', () => {
       test('click opens edit url', async () => {
-        const editUrlStub = sinon.stub(
-          testResolver(changeViewModelToken),
-          'editUrl'
-        );
-        editUrlStub.returns('fakeUrl');
         const setUrlStub = sinon.stub(testResolver(navigationToken), 'setUrl');
         element.editMode = true;
         element.changeNum = 42 as NumericChangeId;
         element.repoName = 'Test Repo' as RepoName;
+        element.patchNum = '1' as RevisionPatchSetNum;
         await element.updateComplete;
         const editButton = queryAndAssert<GrButton>(
           element,
@@ -202,7 +201,10 @@
         );
         editButton.click();
         assert.isTrue(setUrlStub.called);
-        assert.equal(setUrlStub.lastCall.args[0], 'fakeUrl');
+        assert.equal(
+          setUrlStub.lastCall.args[0],
+          '/c/Test+Repo/+/42/1//COMMIT_MSG,edit'
+        );
       });
     });
   });
diff --git a/polygerrit-ui/app/models/change/change-model.ts b/polygerrit-ui/app/models/change/change-model.ts
index 815b93a..bba7e47 100644
--- a/polygerrit-ui/app/models/change/change-model.ts
+++ b/polygerrit-ui/app/models/change/change-model.ts
@@ -46,6 +46,8 @@
   ChangeChildView,
   ChangeViewModel,
   createChangeUrl,
+  createDiffUrl,
+  createEditUrl,
 } from '../views/change';
 import {NavigationService} from '../../elements/core/gr-navigation/gr-navigation';
 import {getRevertCreatedChangeIds} from '../../utils/message-util';
@@ -655,12 +657,27 @@
     return this.getState().change;
   }
 
+  diffUrl(
+    diffView: {path: string; lineNum?: number},
+    patchNum = this.patchNum,
+    basePatchNum = this.basePatchNum
+  ) {
+    if (!this.change) return;
+    if (!this.patchNum) return;
+    return createDiffUrl({
+      change: this.change,
+      patchNum,
+      basePatchNum,
+      diffView,
+    });
+  }
+
   navigateToDiff(
     diffView: {path: string; lineNum?: number},
     patchNum = this.patchNum,
     basePatchNum = this.basePatchNum
   ) {
-    const url = this.viewModel.diffUrl({diffView, patchNum, basePatchNum});
+    const url = this.diffUrl(diffView, patchNum, basePatchNum);
     if (!url) return;
     this.navigation.setUrl(url);
   }
@@ -698,8 +715,18 @@
     this.navigation.setUrl(url);
   }
 
+  editUrl(editView: {path: string; lineNum?: number}) {
+    if (!this.change) return;
+    return createEditUrl({
+      changeNum: this.change._number,
+      repo: this.change.project,
+      patchNum: this.patchNum,
+      editView,
+    });
+  }
+
   navigateToEdit(editView: {path: string; lineNum?: number}) {
-    const url = this.viewModel.editUrl({editView});
+    const url = this.editUrl(editView);
     if (!url) return;
     this.navigation.setUrl(url);
   }
diff --git a/polygerrit-ui/app/models/views/change.ts b/polygerrit-ui/app/models/views/change.ts
index 13babae..06d981a 100644
--- a/polygerrit-ui/app/models/views/change.ts
+++ b/polygerrit-ui/app/models/views/change.ts
@@ -11,12 +11,11 @@
   ChangeInfo,
   PatchSetNumber,
   EDIT,
-  PARENT,
 } from '../../api/rest-api';
 import {Tab} from '../../constants/constants';
 import {GerritView} from '../../services/router/router-model';
 import {UrlEncodedCommentId} from '../../types/common';
-import {assertIsDefined, toggleSet} from '../../utils/common-util';
+import {toggleSet} from '../../utils/common-util';
 import {select} from '../../utils/observable-util';
 import {
   encodeURL,
@@ -27,7 +26,6 @@
 import {define} from '../dependency';
 import {Model} from '../base/model';
 import {ViewState} from './base';
-import {isNumber} from '../../utils/patch-set-util';
 
 export enum ChangeChildView {
   OVERVIEW = 'OVERVIEW',
@@ -147,7 +145,7 @@
 
 export function createChangeUrl(
   obj: CreateChangeUrlObject | Omit<ChangeViewState, 'view' | 'childView'>
-): string {
+) {
   const state: ChangeViewState = objToState({
     ...obj,
     childView: ChangeChildView.OVERVIEW,
@@ -200,7 +198,7 @@
 
 export function createDiffUrl(
   obj: CreateChangeUrlObject | Omit<ChangeViewState, 'view' | 'childView'>
-): string {
+) {
   const state: ChangeViewState = objToState({
     ...obj,
     childView: ChangeChildView.DIFF,
@@ -380,49 +378,6 @@
     }
   }
 
-  /**
-   * Wrapper around createDiffUrl() that falls back to the current state for all
-   * properties that are not explicitly provided as an override.
-   */
-  diffUrl(override: Partial<ChangeViewState>): string {
-    const current = this.getState();
-    assertIsDefined(current?.changeNum);
-    assertIsDefined(current?.repo);
-
-    const patchNum = override.patchNum ?? current.patchNum;
-    let basePatchNum = override.basePatchNum ?? current.basePatchNum;
-    if (isNumber(basePatchNum) && isNumber(patchNum)) {
-      if ((patchNum as number) < (basePatchNum as number)) {
-        basePatchNum = PARENT;
-      }
-    }
-    return createDiffUrl({
-      changeNum: override.changeNum ?? current.changeNum,
-      repo: override.repo ?? current.repo,
-      patchNum,
-      basePatchNum,
-      checksPatchset: override.checksPatchset ?? current.checksPatchset,
-      diffView: override.diffView ?? current.diffView,
-    });
-  }
-
-  /**
-   * Wrapper around createEditUrl() that falls back to the current state for all
-   * properties that are not explicitly provided as an override.
-   */
-  editUrl(override: Partial<ChangeViewState>): string {
-    const current = this.getState();
-    assertIsDefined(current?.changeNum);
-    assertIsDefined(current?.repo);
-
-    return createEditUrl({
-      changeNum: override.changeNum ?? current.changeNum,
-      repo: override.repo ?? current.repo,
-      patchNum: override.patchNum ?? current.patchNum,
-      editView: override.editView ?? current.editView,
-    });
-  }
-
   toggleSelectedCheckRun(checkName: string) {
     const current = this.getState()?.checksRunsSelected ?? new Set();
     const next = new Set(current);
diff --git a/polygerrit-ui/app/utils/patch-set-util.ts b/polygerrit-ui/app/utils/patch-set-util.ts
index dc7adc1..7a5cd45 100644
--- a/polygerrit-ui/app/utils/patch-set-util.ts
+++ b/polygerrit-ui/app/utils/patch-set-util.ts
@@ -70,7 +70,7 @@
   return patchset as PatchSetNum;
 }
 
-export function isNumber(psn?: PatchSetNum): psn is PatchSetNumber {
+export function isNumber(psn: PatchSetNum): psn is PatchSetNumber {
   return typeof psn === 'number';
 }