diff --git a/packages/uhk-agent/src/services/app-update.service.ts b/packages/uhk-agent/src/services/app-update.service.ts index 85ecb3ec..548456d9 100644 --- a/packages/uhk-agent/src/services/app-update.service.ts +++ b/packages/uhk-agent/src/services/app-update.service.ts @@ -5,7 +5,7 @@ import * as settings from 'electron-settings'; import * as isDev from 'electron-is-dev'; import * as storage from 'electron-settings'; -import { IpcEvents, LogService } from 'uhk-common'; +import { AutoUpdateSettings, IpcEvents, LogService } from 'uhk-common'; import { MainServiceBase } from './main-service-base'; export class AppUpdateService extends MainServiceBase { @@ -76,14 +76,17 @@ export class AppUpdateService extends MainServiceBase { } }); - ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => { - this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process'); + ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, (event: Electron.Event, args: any[]) => { + const allowPrerelease: boolean = args[0]; + // tslint:disable-next-line:max-line-length + const logMsg = `[AppUpdateService] checkForUpdate request from renderer process. Allow prerelease: ${allowPrerelease}`; + this.logService.debug(logMsg); this.sendAutoUpdateNotification = true; - this.checkForUpdate(); + this.checkForUpdate(allowPrerelease); }); } - private checkForUpdate() { + private checkForUpdate(allowPrerelease = false): void { if (isDev) { const msg = '[AppUpdateService] Application update is not working in dev mode.'; this.logService.info(msg); @@ -106,7 +109,7 @@ export class AppUpdateService extends MainServiceBase { return; } - autoUpdater.allowPrerelease = this.allowPreRelease(); + autoUpdater.allowPrerelease = allowPrerelease; autoUpdater.checkForUpdates() .then(() => { this.logService.debug('[AppUpdateService] checkForUpdate success'); @@ -127,12 +130,6 @@ export class AppUpdateService extends MainServiceBase { return firstRunVersion !== this.app.getVersion(); } - private allowPreRelease() { - const autoUpdateSettings = this.getAutoUpdateSettings(); - - return autoUpdateSettings && autoUpdateSettings.usePreReleaseUpdate; - } - private checkForUpdateAtStartup() { const autoUpdateSettings = this.getAutoUpdateSettings(); const checkForUpdate = autoUpdateSettings && autoUpdateSettings.checkForUpdateOnStartUp; @@ -142,10 +139,10 @@ export class AppUpdateService extends MainServiceBase { return checkForUpdate; } - private getAutoUpdateSettings() { + private getAutoUpdateSettings(): AutoUpdateSettings { const value = storage.get('auto-update-settings'); if (!value) { - return {checkForUpdateOnStartUp: false, usePreReleaseUpdate: false}; + return {checkForUpdateOnStartUp: false}; } return JSON.parse(value); diff --git a/packages/uhk-web/src/app/models/auto-update-settings.ts b/packages/uhk-common/src/models/auto-update-settings.ts similarity index 69% rename from packages/uhk-web/src/app/models/auto-update-settings.ts rename to packages/uhk-common/src/models/auto-update-settings.ts index 0a40d562..351bb387 100644 --- a/packages/uhk-web/src/app/models/auto-update-settings.ts +++ b/packages/uhk-common/src/models/auto-update-settings.ts @@ -1,4 +1,4 @@ export interface AutoUpdateSettings { checkForUpdateOnStartUp: boolean; - usePreReleaseUpdate: boolean; + usePreReleaseUpdate?: boolean; } diff --git a/packages/uhk-common/src/models/index.ts b/packages/uhk-common/src/models/index.ts index e133829c..d9d4eabe 100644 --- a/packages/uhk-common/src/models/index.ts +++ b/packages/uhk-common/src/models/index.ts @@ -1,3 +1,4 @@ +export * from './auto-update-settings'; export * from './command-line-args'; export * from './notification'; export * from './ipc-response'; diff --git a/packages/uhk-web/src/app/app.component.html b/packages/uhk-web/src/app/app.component.html index bbe29074..d9d0b083 100644 --- a/packages/uhk-web/src/app/app.component.html +++ b/packages/uhk-web/src/app/app.component.html @@ -1,10 +1,15 @@ - - -
+ +
diff --git a/packages/uhk-web/src/app/app.component.ts b/packages/uhk-web/src/app/app.component.ts index 594621cb..9a6d08c0 100644 --- a/packages/uhk-web/src/app/app.component.ts +++ b/packages/uhk-web/src/app/app.component.ts @@ -12,9 +12,11 @@ import { deviceConfigurationLoaded, runningInElectron, saveToKeyboardState, - keypressCapturing + keypressCapturing, + getUpdateInfo } from './store'; import { ProgressButtonState } from './store/reducers/progress-button-state'; +import { UpdateInfo } from './models/update-info'; @Component({ selector: 'main-app', @@ -32,11 +34,22 @@ import { ProgressButtonState } from './store/reducers/progress-button-state'; style({transform: 'translateY(0)'}), animate('400ms ease-in-out', style({transform: 'translateY(100%)'})) ]) + ]), + trigger('updateAvailable', [ + transition(':enter', [ + style({transform: 'translateY(-45px)'}), + animate('500ms ease-out', style({transform: 'translateY(0)'})) + ]), + transition(':leave', [ + style({transform: 'translateY(0)'}), + animate('500ms ease-out', style({transform: 'translateY(-45px)'})) ]) + ]) ] }) export class MainAppComponent implements OnDestroy { - showUpdateAvailable$: Observable; + showUpdateAvailable: boolean; + updateInfo$: Observable; deviceConfigurationLoaded$: Observable; runningInElectron$: Observable; saveToKeyboardState: ProgressButtonState; @@ -44,9 +57,12 @@ export class MainAppComponent implements OnDestroy { private keypressCapturing: boolean; private saveToKeyboardStateSubscription: Subscription; private keypressCapturingSubscription: Subscription; + private showUpdateAvailableSubscription: Subscription; constructor(private store: Store) { - this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable); + this.showUpdateAvailableSubscription = store.select(getShowAppUpdateAvailable) + .subscribe(data => this.showUpdateAvailable = data); + this.updateInfo$ = store.select(getUpdateInfo); this.deviceConfigurationLoaded$ = store.select(deviceConfigurationLoaded); this.runningInElectron$ = store.select(runningInElectron); this.saveToKeyboardStateSubscription = store.select(saveToKeyboardState) @@ -58,6 +74,7 @@ export class MainAppComponent implements OnDestroy { ngOnDestroy(): void { this.saveToKeyboardStateSubscription.unsubscribe(); this.keypressCapturingSubscription.unsubscribe(); + this.showUpdateAvailableSubscription.unsubscribe(); } @HostListener('document:keydown', ['$event']) diff --git a/packages/uhk-web/src/app/components/agent/settings/settings.component.html b/packages/uhk-web/src/app/components/agent/settings/settings.component.html index 83dc6fb7..c3ba97c7 100644 --- a/packages/uhk-web/src/app/components/agent/settings/settings.component.html +++ b/packages/uhk-web/src/app/components/agent/settings/settings.component.html @@ -4,10 +4,8 @@ Settings
- + (checkForUpdate)="checkForUpdate($event)"> diff --git a/packages/uhk-web/src/app/components/agent/settings/settings.component.ts b/packages/uhk-web/src/app/components/agent/settings/settings.component.ts index 7b7a0d6d..c9bdfc0d 100644 --- a/packages/uhk-web/src/app/components/agent/settings/settings.component.ts +++ b/packages/uhk-web/src/app/components/agent/settings/settings.component.ts @@ -1,15 +1,13 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; +import { AutoUpdateSettings } from 'uhk-common'; import { AppState, getAutoUpdateSettings, getCheckingForUpdate } from '../../../store'; import { CheckForUpdateNowAction, - ToggleCheckForUpdateOnStartupAction, - TogglePreReleaseFlagAction + ToggleCheckForUpdateOnStartupAction } from '../../../store/actions/auto-update-settings'; -import { AutoUpdateSettings } from '../../../models/auto-update-settings'; -import { getVersions } from '../../../util'; @Component({ selector: 'settings', @@ -20,7 +18,6 @@ import { getVersions } from '../../../util'; } }) export class SettingsComponent { - version: string = getVersions().version; autoUpdateSettings$: Observable; checkingForUpdate$: Observable; @@ -33,11 +30,7 @@ export class SettingsComponent { this.store.dispatch(new ToggleCheckForUpdateOnStartupAction(value)); } - toogleUsePreReleaseUpdate(value: boolean) { - this.store.dispatch(new TogglePreReleaseFlagAction(value)); - } - - checkForUpdate() { - this.store.dispatch(new CheckForUpdateNowAction()); + checkForUpdate(allowPrerelease: boolean): void { + this.store.dispatch(new CheckForUpdateNowAction(allowPrerelease)); } } diff --git a/packages/uhk-web/src/app/components/auto-update-settings/auto-update-settings.html b/packages/uhk-web/src/app/components/auto-update-settings/auto-update-settings.html index 5f88f95b..11ce1435 100644 --- a/packages/uhk-web/src/app/components/auto-update-settings/auto-update-settings.html +++ b/packages/uhk-web/src/app/components/auto-update-settings/auto-update-settings.html @@ -9,21 +9,7 @@ -
- -
-
- -
-

{{version}}

-
-
- - - + diff --git a/packages/uhk-web/src/app/components/update-available/update-available.component.scss b/packages/uhk-web/src/app/components/update-available/update-available.component.scss index 317af23b..47f7a642 100644 --- a/packages/uhk-web/src/app/components/update-available/update-available.component.scss +++ b/packages/uhk-web/src/app/components/update-available/update-available.component.scss @@ -1,5 +1,19 @@ .app-update-available-wrapper { - display: flex; - justify-content: center; - margin: 0.5rem; + margin: 0; + padding-top: 5px; + padding-bottom: 5px; + + background-color: #428bca; + color: white; + text-align: center; + border-radius: 0; + border-width: 0; + + .btn-link { + color: white; + + &.underline { + text-decoration: underline; + } + } } diff --git a/packages/uhk-web/src/app/components/update-available/update-available.component.ts b/packages/uhk-web/src/app/components/update-available/update-available.component.ts index dbc454f5..91af5db2 100644 --- a/packages/uhk-web/src/app/components/update-available/update-available.component.ts +++ b/packages/uhk-web/src/app/components/update-available/update-available.component.ts @@ -1,4 +1,6 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; + +import { UpdateInfo } from '../../models/update-info'; @Component({ selector: 'app-update-available', @@ -7,6 +9,8 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angul changeDetection: ChangeDetectionStrategy.OnPush }) export class UpdateAvailableComponent { + @Input() updateInfo: UpdateInfo; + @Output() updateApp = new EventEmitter(); @Output() doNotUpdateApp = new EventEmitter(); } diff --git a/packages/uhk-web/src/app/models/update-info.ts b/packages/uhk-web/src/app/models/update-info.ts new file mode 100644 index 00000000..30796f84 --- /dev/null +++ b/packages/uhk-web/src/app/models/update-info.ts @@ -0,0 +1,4 @@ +export interface UpdateInfo { + version: string; + isPrerelease: boolean; +} diff --git a/packages/uhk-web/src/app/services/app-update-renderer.service.ts b/packages/uhk-web/src/app/services/app-update-renderer.service.ts index e482d772..78593c9b 100644 --- a/packages/uhk-web/src/app/services/app-update-renderer.service.ts +++ b/packages/uhk-web/src/app/services/app-update-renderer.service.ts @@ -32,8 +32,8 @@ export class AppUpdateRendererService { this.ipcRenderer.send(IpcEvents.autoUpdater.updateAndRestart); } - checkForUpdate() { - this.ipcRenderer.send(IpcEvents.autoUpdater.checkForUpdate); + checkForUpdate(allowPrerelease: boolean): void { + this.ipcRenderer.send(IpcEvents.autoUpdater.checkForUpdate, allowPrerelease); } private registerEvents() { @@ -57,7 +57,7 @@ export class AppUpdateRendererService { this.ipcRenderer.on(IpcEvents.autoUpdater.autoUpdateDownloaded, (event: string, arg: any) => { this.writeUpdateState(IpcEvents.autoUpdater.autoUpdateDownloaded, arg); - this.dispachStoreAction(new UpdateDownloadedAction()); + this.dispachStoreAction(new UpdateDownloadedAction(arg)); }); this.ipcRenderer.on(IpcEvents.autoUpdater.checkForUpdateNotAvailable, (event: string, arg: any) => { diff --git a/packages/uhk-web/src/app/services/datastorage-repository.service.ts b/packages/uhk-web/src/app/services/datastorage-repository.service.ts index 5df41f1e..a3a39197 100644 --- a/packages/uhk-web/src/app/services/datastorage-repository.service.ts +++ b/packages/uhk-web/src/app/services/datastorage-repository.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@angular/core'; -import { UserConfiguration } from 'uhk-common'; - -import { AutoUpdateSettings } from '../models/auto-update-settings'; +import { AutoUpdateSettings, UserConfiguration } from 'uhk-common'; @Injectable() export class DataStorageRepositoryService { diff --git a/packages/uhk-web/src/app/store/actions/app-update.action.ts b/packages/uhk-web/src/app/store/actions/app-update.action.ts index 1073dd46..01d780ce 100644 --- a/packages/uhk-web/src/app/store/actions/app-update.action.ts +++ b/packages/uhk-web/src/app/store/actions/app-update.action.ts @@ -1,5 +1,6 @@ import { Action } from '@ngrx/store'; import { type } from 'uhk-common'; +import { UpdateInfo } from '../../models/update-info'; const PREFIX = '[app-update] '; @@ -27,6 +28,9 @@ export class DoNotUpdateAppAction implements Action { export class UpdateDownloadedAction implements Action { type = ActionTypes.UPDATE_DOWNLOADED; + + constructor(public payload: UpdateInfo) { + } } export class UpdatingAction implements Action { diff --git a/packages/uhk-web/src/app/store/actions/auto-update-settings.ts b/packages/uhk-web/src/app/store/actions/auto-update-settings.ts index 58b8a61e..35626ec8 100644 --- a/packages/uhk-web/src/app/store/actions/auto-update-settings.ts +++ b/packages/uhk-web/src/app/store/actions/auto-update-settings.ts @@ -1,7 +1,6 @@ import { Action } from '@ngrx/store'; -import { type } from 'uhk-common'; -import { AutoUpdateSettings } from '../../models/auto-update-settings'; +import { AutoUpdateSettings, type } from 'uhk-common'; const PREFIX = '[app-update-config] '; @@ -26,6 +25,9 @@ export class ToggleCheckForUpdateOnStartupAction implements Action { export class CheckForUpdateNowAction implements Action { type = ActionTypes.CHECK_FOR_UPDATE_NOW; + + constructor(public payload?: boolean) { + } } export class CheckForUpdateSuccessAction implements Action { diff --git a/packages/uhk-web/src/app/store/effects/app-update.ts b/packages/uhk-web/src/app/store/effects/app-update.ts index c7ef140a..43f7165e 100644 --- a/packages/uhk-web/src/app/store/effects/app-update.ts +++ b/packages/uhk-web/src/app/store/effects/app-update.ts @@ -7,7 +7,7 @@ import { first, map, tap } from 'rxjs/operators'; import { LogService, NotificationType } from 'uhk-common'; import { ActionTypes, UpdateErrorAction } from '../actions/app-update.action'; -import { ActionTypes as AutoUpdateActionTypes } from '../actions/auto-update-settings'; +import { ActionTypes as AutoUpdateActionTypes, CheckForUpdateNowAction } from '../actions/auto-update-settings'; import { ShowNotificationAction } from '../actions/app'; import { AppUpdateRendererService } from '../../services/app-update-renderer.service'; @@ -23,12 +23,13 @@ export class AppUpdateEffect { }) ); - @Effect({ dispatch: false }) checkForUpdate$: Observable = this.actions$ - .ofType(AutoUpdateActionTypes.CHECK_FOR_UPDATE_NOW) + @Effect({ dispatch: false }) checkForUpdate$ = this.actions$ + .ofType(AutoUpdateActionTypes.CHECK_FOR_UPDATE_NOW) .pipe( - tap(() => { + map(action => action.payload), + tap((allowPrerelease: boolean) => { this.logService.debug('[AppUpdateEffect] call checkForUpdate'); - this.appUpdateRendererService.checkForUpdate(); + this.appUpdateRendererService.checkForUpdate(allowPrerelease); }) ); diff --git a/packages/uhk-web/src/app/store/effects/auto-update-settings.ts b/packages/uhk-web/src/app/store/effects/auto-update-settings.ts index ccaf5d0d..88868954 100644 --- a/packages/uhk-web/src/app/store/effects/auto-update-settings.ts +++ b/packages/uhk-web/src/app/store/effects/auto-update-settings.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Observable'; import { Action, Store } from '@ngrx/store'; import { map, startWith, switchMap, withLatestFrom } from 'rxjs/operators'; -import { NotificationType } from 'uhk-common'; +import { AutoUpdateSettings, NotificationType } from 'uhk-common'; import { ActionTypes, @@ -16,7 +16,6 @@ import { import { DataStorageRepositoryService } from '../../services/datastorage-repository.service'; import { AppState, getAutoUpdateSettings } from '../index'; import { initialState } from '../reducers/auto-update-settings'; -import { AutoUpdateSettings } from '../../models/auto-update-settings'; import { ShowNotificationAction } from '../actions/app'; @Injectable() diff --git a/packages/uhk-web/src/app/store/index.ts b/packages/uhk-web/src/app/store/index.ts index ddf49696..a69d0ed7 100644 --- a/packages/uhk-web/src/app/store/index.ts +++ b/packages/uhk-web/src/app/store/index.ts @@ -64,6 +64,7 @@ export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindow export const appUpdateState = (state: AppState) => state.appUpdate; export const getShowAppUpdateAvailable = createSelector(appUpdateState, fromAppUpdate.getShowAppUpdateAvailable); +export const getUpdateInfo = createSelector(appUpdateState, fromAppUpdate.getUpdateInfo); export const appUpdateSettingsState = (state: AppState) => state.autoUpdateSettings; export const getAutoUpdateSettings = createSelector(appUpdateSettingsState, autoUpdateSettings.getUpdateSettings); diff --git a/packages/uhk-web/src/app/store/reducers/app-update.reducer.ts b/packages/uhk-web/src/app/store/reducers/app-update.reducer.ts index 937fc786..503e87d4 100644 --- a/packages/uhk-web/src/app/store/reducers/app-update.reducer.ts +++ b/packages/uhk-web/src/app/store/reducers/app-update.reducer.ts @@ -1,39 +1,48 @@ -import { Actions, ActionTypes } from '../actions/app-update.action'; +import { Actions, ActionTypes, UpdateDownloadedAction } from '../actions/app-update.action'; +import { UpdateInfo } from '../../models/update-info'; export interface State { updateAvailable: boolean; updateDownloaded: boolean; doNotUpdateApp: boolean; + updateInfo: UpdateInfo; } export const initialState: State = { updateAvailable: false, updateDownloaded: false, - doNotUpdateApp: false + doNotUpdateApp: false, + updateInfo: { + isPrerelease: false, + version: '' + } }; export function reducer(state = initialState, action: Actions) { switch (action.type) { - case ActionTypes.UPDATE_AVAILABLE: { - const newState = Object.assign({}, state); - newState.updateAvailable = true; - return newState; - } + case ActionTypes.UPDATE_AVAILABLE: + return { + ...state, + updateAvailable: true + }; - case ActionTypes.UPDATE_DOWNLOADED: { - const newState = Object.assign({}, state); - newState.updateDownloaded = true; - return newState; - } + case ActionTypes.UPDATE_DOWNLOADED: + return { + ...state, + updateDownloaded: true, + updateInfo: (action as UpdateDownloadedAction).payload + }; + + case ActionTypes.DO_NOT_UPDATE_APP: + return { + ...state, + doNotUpdateApp: true + }; - case ActionTypes.DO_NOT_UPDATE_APP: { - const newState = Object.assign({}, state); - newState.doNotUpdateApp = true; - return newState; - } default: return state; } } export const getShowAppUpdateAvailable = (state: State) => state.updateDownloaded && !state.doNotUpdateApp; +export const getUpdateInfo = (state: State) => state.updateInfo; diff --git a/packages/uhk-web/src/app/store/reducers/auto-update-settings.ts b/packages/uhk-web/src/app/store/reducers/auto-update-settings.ts index eec38f31..55e0a73b 100644 --- a/packages/uhk-web/src/app/store/reducers/auto-update-settings.ts +++ b/packages/uhk-web/src/app/store/reducers/auto-update-settings.ts @@ -1,7 +1,8 @@ import { Action } from '@ngrx/store'; +import { AutoUpdateSettings } from 'uhk-common'; + import { ActionTypes } from '../actions/auto-update-settings'; import { ActionTypes as UpdateActions } from '../actions/app-update.action'; -import { AutoUpdateSettings } from '../../models/auto-update-settings'; export interface State extends AutoUpdateSettings { checkingForUpdate: boolean; diff --git a/packages/uhk-web/src/renderer/services/electron-datastorage-repository.service.ts b/packages/uhk-web/src/renderer/services/electron-datastorage-repository.service.ts index f2b5a440..48ea47b3 100644 --- a/packages/uhk-web/src/renderer/services/electron-datastorage-repository.service.ts +++ b/packages/uhk-web/src/renderer/services/electron-datastorage-repository.service.ts @@ -1,8 +1,7 @@ import * as storage from 'electron-settings'; -import { UserConfiguration } from 'uhk-common'; +import { AutoUpdateSettings, UserConfiguration } from 'uhk-common'; import { DataStorageRepositoryService } from '../../app/services/datastorage-repository.service'; -import { AutoUpdateSettings } from '../../app/models/auto-update-settings'; export class ElectronDataStorageRepositoryService implements DataStorageRepositoryService { static getValue(key: string): any { diff --git a/packages/uhk-web/src/styles.scss b/packages/uhk-web/src/styles.scss index 6022a804..7c2a4f1c 100644 --- a/packages/uhk-web/src/styles.scss +++ b/packages/uhk-web/src/styles.scss @@ -4,6 +4,7 @@ $zindex-navbar-fixed: 1030; @import '~spacing-bootstrap-3/spacing'; @import './styles/variables'; +@import './styles/common'; @import '~angular-notifier/styles'; @import '~font-awesome/scss/font-awesome'; @import './styles/tooltip'; @@ -44,6 +45,9 @@ html, body { .main-content { height: 100%; + top: 0; + + @include updatePanelVisible(); } .main-page-content { @@ -52,6 +56,15 @@ html, body { min-height: 100%; } +app-update-available { + position: fixed; + display: block; + left: 0; + top: 0; + width: 100%; + z-index: 200; +} + .nav-pills > li > a { cursor: pointer; } diff --git a/packages/uhk-web/src/styles/_variables.scss b/packages/uhk-web/src/styles/_variables.scss index 177bc65c..6f73d075 100644 --- a/packages/uhk-web/src/styles/_variables.scss +++ b/packages/uhk-web/src/styles/_variables.scss @@ -12,3 +12,6 @@ $tooltip-border-color: $dark-grey; $tooltip-background-color: #fff; $tooltip-inner-color: #000; $tooltip-arrow-border-width: $tooltip-arrow-width + $tooltip-border-width; + +$update-notification-height: 35px; +$main-content-top-margin-on-update: 35px + 10px; diff --git a/packages/uhk-web/src/styles/common.scss b/packages/uhk-web/src/styles/common.scss index 80754c34..096ba19b 100644 --- a/packages/uhk-web/src/styles/common.scss +++ b/packages/uhk-web/src/styles/common.scss @@ -1,3 +1,5 @@ +@import './variables'; + %btn-link { padding: 7px 0; text-decoration: none; @@ -8,3 +10,15 @@ outline: none; } } + +@mixin updatePanelVisible() { + transition-timing-function: ease-out; + transition: 0.5s; + margin-top: 0; + + &.update-panel-visible { + transition-timing-function: ease-in; + transition: 0.5s; + margin-top: $main-content-top-margin-on-update; + } +}