refactor(GUI): replace the orange alert ribbon with a modal (#689)

As a way to simplify the design and make more use of available
components rather than creating specialised ones, we replaced the
`.alert-ribbon` component, which consisted of an orange alert appearing
at the top of the screen for when a validation failed, the drive ran out
of space, or other issues, with a modal.

This change allowed us to remove the "warning"-related colors from the
theme palette.

Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
Juan Cruz Viotti 2016-09-07 12:40:51 -07:00 committed by GitHub
parent ce1c1d724d
commit 951b8de9fc
14 changed files with 205 additions and 188 deletions

View File

@ -4460,25 +4460,25 @@ a.thumbnail:focus,
a.thumbnail.active {
border-color: #ddd; }
.alert, .alert-ribbon {
padding: 13px;
.alert {
padding: 15px;
margin-bottom: 18px;
border: 1px solid transparent;
border-radius: 4px; }
.alert h4, .alert-ribbon h4 {
.alert h4 {
margin-top: 0;
color: inherit; }
.alert .alert-link, .alert-ribbon .alert-link {
.alert .alert-link {
font-weight: bold; }
.alert > p, .alert-ribbon > p,
.alert > ul, .alert-ribbon > ul {
.alert > p,
.alert > ul {
margin-bottom: 0; }
.alert > p + p, .alert-ribbon > p + p {
.alert > p + p {
margin-top: 5px; }
.alert-dismissable,
.alert-dismissible {
padding-right: 33px; }
padding-right: 35px; }
.alert-dismissable .close,
.alert-dismissible .close {
position: relative;
@ -6153,16 +6153,6 @@ body {
background-color: #2d78d2;
color: #fff; }
.button-warning,
.button-warning:focus {
background-color: #e99852;
color: #fff;
outline: none; }
.button-warning:hover {
background-color: #e37d25;
color: #fff; }
.button-danger,
.button-danger:focus {
background-color: #d9534f;
@ -6209,53 +6199,6 @@ body {
background-color: #d9534f;
border-color: #d9534f; }
/*
* Copyright 2016 Resin.io
*
* 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.
*/
.alert-ribbon {
background-color: #e99852;
color: #fff;
border-color: #f7dbc3;
width: 60%;
position: fixed;
text-align: center;
left: 0;
right: 0;
margin: 0 auto;
border: 0;
border-radius: 2px;
border-top-left-radius: 0;
border-top-right-radius: 0;
top: -100%;
transition: top .5s; }
.alert-ribbon > .glyphicon:first-child, .alert-ribbon > .tick:first-child {
margin-right: 5px; }
.alert-ribbon > .glyphicon:last-child, .alert-ribbon > .tick:last-child {
margin-left: 5px; }
.alert-ribbon .button-link {
padding: 0;
font-size: inherit;
vertical-align: baseline;
border-radius: 0;
border-bottom: 1px solid;
border-color: #fff;
color: #fff; }
.alert-ribbon--open {
top: 0; }
/*
* Copyright 2016 Resin.io
*
@ -6489,6 +6432,24 @@ body {
background-color: #f2f2f2;
word-wrap: break-word; }
/*
* Copyright 2016 Resin.io
*
* 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.
*/
.modal-flash-error-modal .modal-title .glyphicon, .modal-flash-error-modal .modal-title .tick {
color: #d9534f; }
/*
* Copyright 2016 Resin.io
*

View File

@ -0,0 +1,52 @@
/*
* Copyright 2016 Resin.io
*
* 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.
*/
'use strict';
module.exports = function(
$uibModalInstance,
SelectionStateModel,
FlashStateModel,
AnalyticsService,
flashErrorData
) {
/**
* @summary Flash error data
* @property
* @public
*/
this.data = flashErrorData;
/**
* @summary Retry flash process
* @function
* @public
*
* @example
* FlashErrorModalController.retry();
*/
this.retry = () => {
SelectionStateModel.clear({
preserveImage: true
});
FlashStateModel.resetState();
AnalyticsService.logEvent('Restart after failure');
$uibModalInstance.dismiss();
};
};

View File

@ -0,0 +1,35 @@
/*
* Copyright 2016 Resin.io
*
* 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.
*/
'use strict';
/**
* @module Etcher.Components.FlashErrorModal
*/
const angular = require('angular');
const MODULE_NAME = 'Etcher.Components.FlashErrorModal';
const FlashErrorModal = angular.module(MODULE_NAME, [
require('../modal/modal'),
require('../../models/selection-state'),
require('../../models/flash-state'),
require('../../modules/analytics')
]);
FlashErrorModal.controller('FlashErrorModalController', require('./controllers/flash-error-modal'));
FlashErrorModal.service('FlashErrorModalService', require('./services/flash-error-modal'));
module.exports = MODULE_NAME;

View File

@ -0,0 +1,47 @@
/*
* Copyright 2016 Resin.io
*
* 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.
*/
'use strict';
const _ = require('lodash');
module.exports = function(ModalService) {
/**
* @summary Open the flash error modal
* @function
* @public
*
* @param {String} message - flash error message
* @returns {Promise}
*
* @example
* FlashErrorModalService.show('The drive is not large enough!');
*/
this.show = (message) => {
return ModalService.open({
template: './components/flash-error-modal/templates/flash-error-modal.tpl.html',
controller: 'FlashErrorModalController as modal',
size: 'flash-error-modal',
resolve: {
flashErrorData: _.constant({
message: message
})
}
}).result;
};
};

View File

@ -0,0 +1,19 @@
/*
* Copyright 2016 Resin.io
*
* 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.
*/
.modal-flash-error-modal .modal-title .glyphicon {
color: $palette-theme-danger-background;
}

View File

@ -0,0 +1,17 @@
<div class="modal-header">
<h4 class="modal-title">
<span class="glyphicon glyphicon-exclamation-sign"></span>
<span>An error ocurred!</span>
</h4>
<button class="close" ng-click="modal.retry()">&times;</button>
</div>
<div class="modal-body">
<div class="modal-text">{{ ::modal.data.message }}</div>
</div>
<div class="modal-footer">
<button class="button button-danger button-block"
ng-click="modal.retry()">Retry</button>
</div>

View File

@ -23,6 +23,7 @@ module.exports = function(
DriveScannerService,
ImageWriterService,
AnalyticsService,
FlashErrorModalService,
ErrorService,
OSNotificationService,
OSWindowProgressService
@ -72,10 +73,14 @@ module.exports = function(
OSNotificationService.send('Oops!', 'Looks like your flash has failed');
if (error.code === 'EVALIDATION') {
FlashErrorModalService.show('Your removable drive may be corrupted. Try inserting a different one and try again.');
AnalyticsService.logEvent('Validation error');
} else if (error.code === 'ENOSPC') {
FlashErrorModalService.show('Not enough space on the drive. Please insert larger one and try again.');
} else {
AnalyticsService.logEvent('Flash error');
FlashErrorModalService.show('Oops, seems something went wrong.');
ErrorService.reportException(error);
AnalyticsService.logEvent('Flash error');
}
})

View File

@ -21,7 +21,6 @@ module.exports = function(
DrivesModel,
FlashStateModel,
SettingsModel,
AnalyticsService,
TooltipModalService,
OSOpenExternalService
) {
@ -34,23 +33,6 @@ module.exports = function(
this.external = OSOpenExternalService;
this.tooltipModal = TooltipModalService;
/**
* @summary Restart after failure
* @function
* @public
*
* @example
* MainController.restartAfterFailure();
*/
this.restartAfterFailure = () => {
SelectionStateModel.clear({
preserveImage: true
});
FlashStateModel.resetState();
AnalyticsService.logEvent('Restart after failure');
};
/**
* @summary Determine if the drive step should be disabled
* @function

View File

@ -33,6 +33,7 @@ const MainPage = angular.module(MODULE_NAME, [
require('../../components/drive-selector/drive-selector'),
require('../../components/tooltip-modal/tooltip-modal'),
require('../../components/flash-error-modal/flash-error-modal'),
require('../../components/progress-button/progress-button'),
require('../../os/window-progress/window-progress'),

View File

@ -1,28 +1,4 @@
<div class="page-main row around-xs">
<div class="alert-ribbon" ng-class="{
'alert-ribbon--open': main.state.getLastFlashErrorCode()
}" ng-switch="main.state.getLastFlashErrorCode()">
<span class="glyphicon glyphicon-warning-sign"></span>
<span ng-switch-when="ENOSPC">
Not enough space on the drive.<br>
Please insert larger one and
<button class="button button-link" ng-click="main.restartAfterFailure()">try again</button>
</span>
<span ng-switch-when="EVALIDATION">
Your removable drive may be corrupted.
<br>Try inserting a different one and
<button class="button button-link" ng-click="main.restartAfterFailure()">press "retry"</button>
</span>
<span ng-switch-default>
Oops, seems something went wrong.
Click <button class="button button-link" ng-click="main.restartAfterFailure()">here</button> to retry
</span>
</div>
<div class="col-xs" ng-controller="ImageSelectionController as image">
<div class="box text-center" os-dropzone="image.selectImage($file)">
<svg-icon class="center-block" path="{{ main.selection.getImageLogo() || '../../../assets/image.svg' }}"></svg-icon>
@ -114,16 +90,11 @@
percentage="main.state.getFlashState().percentage"
striped="{{ main.state.getFlashState().type == 'check' }}"
ng-attr-active="{{ main.state.isFlashing() }}"
ng-show="!main.state.getLastFlashErrorCode()"
ng-click="flash.flashImageToDrive(main.selection.getImagePath(), main.selection.getDrive())"
ng-disabled="main.shouldFlashStepBeDisabled()">
ng-disabled="main.shouldFlashStepBeDisabled() || main.state.getLastFlashErrorCode()">
<span ng-bind="flash.getProgressButtonLabel()"></span>
</progress-button>
<button class="button button-warning button-brick" ng-show="main.state.getLastFlashErrorCode()" ng-click="main.restartAfterFailure()">
<span class="glyphicon glyphicon-repeat"></span> Retry
</button>
<p class="step-footer step-footer-split" ng-show="main.state.getFlashState().speed && main.state.getFlashState().percentage != 100">
<span>ETA: {{ main.state.getFlashState().eta | secondsToDate | amDateFormat:'m[m]ss[s]' }}</span>
<span ng-bind="main.state.getFlashState().speed.toFixed(2) + ' MB/s'"></span>

View File

@ -1,66 +0,0 @@
/*
* Copyright 2016 Resin.io
*
* 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.
*/
.alert-ribbon {
@extend .alert;
background-color: $palette-theme-warning-background;
color: $palette-theme-warning-foreground;
border-color: lighten($palette-theme-warning-background, 25%);
width: 60%;
position: fixed;
text-align: center;
left: 0;
right: 0;
margin: 0 auto;
border: 0;
border-radius: 2px;
border-top-left-radius: 0;
border-top-right-radius: 0;
// Animate appearance
top: -100%;
transition: top .5s;
// Align alert icons a bit better
> .glyphicon {
&:first-child {
margin-right: 5px;
}
&:last-child {
margin-left: 5px;
}
}
.button-link {
padding: 0;
font-size: inherit;
vertical-align: baseline;
border-radius: 0;
border-bottom: 1px solid;
border-color: $palette-theme-warning-foreground;
color: $palette-theme-warning-foreground;
}
}
.alert-ribbon--open {
top: 0;
}

View File

@ -66,10 +66,6 @@ $button-types-styles: (
bg: $palette-theme-primary-background,
color: $palette-theme-primary-foreground
),
warning: (
bg: $palette-theme-warning-background,
color: $palette-theme-warning-foreground
),
danger: (
bg: $palette-theme-danger-background,
color: $palette-theme-danger-foreground

View File

@ -18,7 +18,6 @@ $icon-font-path: "../../node_modules/bootstrap-sass/assets/fonts/bootstrap/";
$font-size-base: 13px;
$cursor-disabled: initial;
$link-hover-decoration: none;
$alert-padding: 13px;
$btn-min-width: 170px;
$link-color: #ddd;
@ -31,12 +30,12 @@ $link-color: #ddd;
@import "./components/caption";
@import "./components/button";
@import "./components/tick";
@import "./components/alert-ribbon";
@import "../components/modal/styles/modal";
@import "../components/update-notifier/styles/update-notifier";
@import "../components/progress-button/styles/progress-button";
@import "../components/drive-selector/styles/drive-selector";
@import "../components/tooltip-modal/styles/tooltip-modal";
@import "../components/flash-error-modal/styles/flash-error-modal";
@import "../pages/main/styles/main";
@import "../pages/settings/styles/settings";
@import "../pages/finish/styles/finish";

View File

@ -29,8 +29,6 @@ $palette-theme-default-background: #ececec;
$palette-theme-default-foreground: #b3b3b3;
$palette-theme-primary-background: #5793db;
$palette-theme-primary-foreground: #fff;
$palette-theme-warning-background: #e99852;
$palette-theme-warning-foreground: #fff;
$palette-theme-danger-background: #d9534f;
$palette-theme-danger-foreground: #fff;
$palette-theme-success-background: #5fb835;