1. add `@extends` for all polymer elements 2. add `@override` for all lifecycle methods 3. fix a wrong type in gr-auth Change-Id: Id9dea76b169197084d2e0b5f267459cdc4aaec3e
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * @license
 | 
						|
 * Copyright (C) 2017 The Android Open Source Project
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 * http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
(function() {
 | 
						|
  'use strict';
 | 
						|
 | 
						|
  const RESTORED_MESSAGE = 'Content restored from a previous edit.';
 | 
						|
  const SAVING_MESSAGE = 'Saving changes...';
 | 
						|
  const SAVED_MESSAGE = 'All changes saved';
 | 
						|
  const SAVE_FAILED_MSG = 'Failed to save changes';
 | 
						|
 | 
						|
  const STORAGE_DEBOUNCE_INTERVAL_MS = 100;
 | 
						|
 | 
						|
  /**
 | 
						|
   * @appliesMixin Gerrit.FireMixin
 | 
						|
   * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
						|
   * @appliesMixin Gerrit.PatchSetMixin
 | 
						|
   * @appliesMixin Gerrit.PathListMixin
 | 
						|
   * @extends Polymer.Element
 | 
						|
   */
 | 
						|
  class GrEditorView extends Polymer.mixinBehaviors( [
 | 
						|
    Gerrit.FireBehavior,
 | 
						|
    Gerrit.KeyboardShortcutBehavior,
 | 
						|
    Gerrit.PatchSetBehavior,
 | 
						|
    Gerrit.PathListBehavior,
 | 
						|
  ], Polymer.GestureEventListeners(
 | 
						|
      Polymer.LegacyElementMixin(
 | 
						|
          Polymer.Element))) {
 | 
						|
    static get is() { return 'gr-editor-view'; }
 | 
						|
    /**
 | 
						|
     * Fired when the title of the page should change.
 | 
						|
     *
 | 
						|
     * @event title-change
 | 
						|
     */
 | 
						|
 | 
						|
    /**
 | 
						|
     * Fired to notify the user of
 | 
						|
     *
 | 
						|
     * @event show-alert
 | 
						|
     */
 | 
						|
 | 
						|
    static get properties() {
 | 
						|
      return {
 | 
						|
      /**
 | 
						|
       * URL params passed from the router.
 | 
						|
       */
 | 
						|
        params: {
 | 
						|
          type: Object,
 | 
						|
          observer: '_paramsChanged',
 | 
						|
        },
 | 
						|
 | 
						|
        _change: Object,
 | 
						|
        _changeEditDetail: Object,
 | 
						|
        _changeNum: String,
 | 
						|
        _patchNum: String,
 | 
						|
        _path: String,
 | 
						|
        _type: String,
 | 
						|
        _content: String,
 | 
						|
        _newContent: String,
 | 
						|
        _saving: {
 | 
						|
          type: Boolean,
 | 
						|
          value: false,
 | 
						|
        },
 | 
						|
        _successfulSave: {
 | 
						|
          type: Boolean,
 | 
						|
          value: false,
 | 
						|
        },
 | 
						|
        _saveDisabled: {
 | 
						|
          type: Boolean,
 | 
						|
          value: true,
 | 
						|
          computed: '_computeSaveDisabled(_content, _newContent, _saving)',
 | 
						|
        },
 | 
						|
        _prefs: Object,
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    get keyBindings() {
 | 
						|
      return {
 | 
						|
        'ctrl+s meta+s': '_handleSaveShortcut',
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    /** @override */
 | 
						|
    created() {
 | 
						|
      super.created();
 | 
						|
      this.addEventListener('content-change',
 | 
						|
          e => this._handleContentChange(e));
 | 
						|
    }
 | 
						|
 | 
						|
    /** @override */
 | 
						|
    attached() {
 | 
						|
      super.attached();
 | 
						|
      this._getEditPrefs().then(prefs => { this._prefs = prefs; });
 | 
						|
    }
 | 
						|
 | 
						|
    get storageKey() {
 | 
						|
      return `c${this._changeNum}_ps${this._patchNum}_${this._path}`;
 | 
						|
    }
 | 
						|
 | 
						|
    _getLoggedIn() {
 | 
						|
      return this.$.restAPI.getLoggedIn();
 | 
						|
    }
 | 
						|
 | 
						|
    _getEditPrefs() {
 | 
						|
      return this.$.restAPI.getEditPreferences();
 | 
						|
    }
 | 
						|
 | 
						|
    _paramsChanged(value) {
 | 
						|
      if (value.view !== Gerrit.Nav.View.EDIT) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      this._changeNum = value.changeNum;
 | 
						|
      this._path = value.path;
 | 
						|
      this._patchNum = value.patchNum || this.EDIT_NAME;
 | 
						|
 | 
						|
      // 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.
 | 
						|
      this.async(() => {
 | 
						|
        const title = `Editing ${this.computeTruncatedPath(this._path)}`;
 | 
						|
        this.fire('title-change', {title});
 | 
						|
      });
 | 
						|
 | 
						|
      const promises = [];
 | 
						|
 | 
						|
      promises.push(this._getChangeDetail(this._changeNum));
 | 
						|
      promises.push(
 | 
						|
          this._getFileData(this._changeNum, this._path, this._patchNum));
 | 
						|
      return Promise.all(promises);
 | 
						|
    }
 | 
						|
 | 
						|
    _getChangeDetail(changeNum) {
 | 
						|
      return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => {
 | 
						|
        this._change = change;
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    _handlePathChanged(e) {
 | 
						|
      const path = e.detail;
 | 
						|
      if (path === this._path) {
 | 
						|
        return Promise.resolve();
 | 
						|
      }
 | 
						|
      return this.$.restAPI.renameFileInChangeEdit(this._changeNum,
 | 
						|
          this._path, path).then(res => {
 | 
						|
        if (!res.ok) { return; }
 | 
						|
 | 
						|
        this._successfulSave = true;
 | 
						|
        this._viewEditInChangeView();
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    _viewEditInChangeView() {
 | 
						|
      const patch = this._successfulSave ? this.EDIT_NAME : this._patchNum;
 | 
						|
      Gerrit.Nav.navigateToChange(this._change, patch, null,
 | 
						|
          patch !== this.EDIT_NAME);
 | 
						|
    }
 | 
						|
 | 
						|
    _getFileData(changeNum, path, patchNum) {
 | 
						|
      const storedContent =
 | 
						|
            this.$.storage.getEditableContentItem(this.storageKey);
 | 
						|
 | 
						|
      return this.$.restAPI.getFileContent(changeNum, path, patchNum)
 | 
						|
          .then(res => {
 | 
						|
            if (storedContent && storedContent.message &&
 | 
						|
                storedContent.message !== res.content) {
 | 
						|
              this.dispatchEvent(new CustomEvent('show-alert', {
 | 
						|
                detail: {message: RESTORED_MESSAGE},
 | 
						|
                bubbles: true,
 | 
						|
                composed: true,
 | 
						|
              }));
 | 
						|
 | 
						|
              this._newContent = storedContent.message;
 | 
						|
            } else {
 | 
						|
              this._newContent = res.content || '';
 | 
						|
            }
 | 
						|
            this._content = res.content || '';
 | 
						|
 | 
						|
            // A non-ok response may result if the file does not yet exist.
 | 
						|
            // The `type` field of the response is only valid when the file
 | 
						|
            // already exists.
 | 
						|
            if (res.ok && res.type) {
 | 
						|
              this._type = res.type;
 | 
						|
            } else {
 | 
						|
              this._type = '';
 | 
						|
            }
 | 
						|
          });
 | 
						|
    }
 | 
						|
 | 
						|
    _saveEdit() {
 | 
						|
      this._saving = true;
 | 
						|
      this._showAlert(SAVING_MESSAGE);
 | 
						|
      this.$.storage.eraseEditableContentItem(this.storageKey);
 | 
						|
      return this.$.restAPI.saveChangeEdit(this._changeNum, this._path,
 | 
						|
          this._newContent).then(res => {
 | 
						|
        this._saving = false;
 | 
						|
        this._showAlert(res.ok ? SAVED_MESSAGE : SAVE_FAILED_MSG);
 | 
						|
        if (!res.ok) { return; }
 | 
						|
 | 
						|
        this._content = this._newContent;
 | 
						|
        this._successfulSave = true;
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    _showAlert(message) {
 | 
						|
      this.dispatchEvent(new CustomEvent('show-alert', {
 | 
						|
        detail: {message},
 | 
						|
        bubbles: true,
 | 
						|
        composed: true,
 | 
						|
      }));
 | 
						|
    }
 | 
						|
 | 
						|
    _computeSaveDisabled(content, newContent, saving) {
 | 
						|
      // Polymer 2: check for undefined
 | 
						|
      if ([
 | 
						|
        content,
 | 
						|
        newContent,
 | 
						|
        saving,
 | 
						|
      ].some(arg => arg === undefined)) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
 | 
						|
      if (saving) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
      return content === newContent;
 | 
						|
    }
 | 
						|
 | 
						|
    _handleCloseTap() {
 | 
						|
      // TODO(kaspern): Add a confirm dialog if there are unsaved changes.
 | 
						|
      this._viewEditInChangeView();
 | 
						|
    }
 | 
						|
 | 
						|
    _handleContentChange(e) {
 | 
						|
      this.debounce('store', () => {
 | 
						|
        const content = e.detail.value;
 | 
						|
        if (content) {
 | 
						|
          this.set('_newContent', e.detail.value);
 | 
						|
          this.$.storage.setEditableContentItem(this.storageKey, content);
 | 
						|
        } else {
 | 
						|
          this.$.storage.eraseEditableContentItem(this.storageKey);
 | 
						|
        }
 | 
						|
      }, STORAGE_DEBOUNCE_INTERVAL_MS);
 | 
						|
    }
 | 
						|
 | 
						|
    _handleSaveShortcut(e) {
 | 
						|
      e.preventDefault();
 | 
						|
      if (!this._saveDisabled) {
 | 
						|
        this._saveEdit();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  customElements.define(GrEditorView.is, GrEditorView);
 | 
						|
})();
 |