The `.button` component is not the responsible of knowing the
`min-width` it should occupy in the actual pages.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
We have some global CSS rules that affect `.alert` living in
`_bootstrap.scss`, however `.alert` is only being used by our
`.alert-ribbon` component, so it makes sense to move those rules over
there.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
Move `.badge` coloring to the main page's style file, since the style we
currently hardcode on the component itself is very tied to the
particular context the badge is being instantiated in.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
`.label-danger` is defined by Bootstrap, and its coloring re-uses what
was defined in `$brand-danger`, which is currently unset.
To prevent the module from going out of sync with our new CSS palette,
we explicitly declare the colorings in the component file.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This is a big PR that extracts out a sensible set of SCSS color
variables that can be used to customise Etcher's look.
To achieve such goal, the following changes were introduced:
- Create a separate `lib/gui/scss/modules/_theme.scss` to conveniently
hold all SCSS color variables.
- Decouple button styles from Bootstrap's `.btn`.
- Stop configuring Bootstrap colors as a whole using the SCSS variables
it exposes and instead declare them directly in the modules that make
use of them.
- Normalize all modal layouts into one. We had like 3 different modal
layouts, each with their own title/body colors, etc. To simplify the
color palette, we make use of a single modal layout everywhere.
- Remove `.progress-button--primary`. `.progress-button` is now
"primary" by default.
- Be precise about `tick` foreground colors.
- Rename `.label-default` to `.label-inset`.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This PR integrates SCSS Lint, a tool that will help us keep SCSS tidier.
I've included a sensible configuration at `.scss-lint.yml`, and
documentation on how to install it on `CONTRIBUTING.md`.
The tool will run automatically as part of `npm run lint`.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
There are many SCSS rules in `lib/gui/scss/main.scss` that only apply to
the main page. In order to keep things tidy, those styles were moved to
`lib/gui/pages/main/styles/_main.scss`.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
In order to get the bmap file contents to the Etcher CLI, we were
handling extraction, writing to a temporary file, then reading again,
and all sorts of other mumbo-jumbo, without realising that
`etcher-image-stream` already has this information right where we need
it (in the CLI's writer module) and in the way we need it (as plain
text).
Re-using from there hugely simplifies things.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
Steps to reproduce:
- Insert a single drive.
- Open drive selector modal.
- Extract the drive while drive selectector modal is open.
- Check DevTools.
The solution is to use `.close()` instead of `.dismiss()`. After some
diving into the documentation and the code, `.dismiss()` is only
available from within the modal controller, however if you want to close
the modal from outside, `.close()` is the way to go.
Notice that `.close()` returns a rejected promise when being called from
the modal itself, but thats not the case from outside, which is quite
confusing, but means we can safely use `.close()` in this context.
Change-Type: patch
Changelog-Entry: Fix "`modal.dismiss` is not a function" exception.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
The current tooltip, "SHOW IN FULL", proved to be a bit confusing for
users. We're using "SHOW FULL FILE NAME", as kindly suggested by @dlech.
Fixes: https://github.com/resin-io/etcher/issues/634
Change-Type: patch
Changelog-Entry: Improve image full file name modal tooltip.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This PR integrates `.bmap` support recently added to
`etcher-image-write` into Etcher itself.
It does it in the following way:
- It adds a `--bmap` option to the Etcher CLI.
- It saves a potential `bmap` file contents to the
`SelectionStateModel`.
- In the GUI, at the time of writing, if there is a `bmap` file content
in `SelectionStateModel`, it gets written to a temporary file and such
path is passed as the `--bmap` option to the CLI.
Since validation checksums don't make sense anymore, the finish screen
doesn't show the checksum box in this case.
Change-Type: minor
Changelog-Entry: Add `.bmap` support.
Fixes: https://github.com/resin-io/etcher/issues/171
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
The main page controller contained a lot of undocumented and untested
logic. As a first step towards cleaning up the whole thing, this PR
introduces the following changes:
- Implement `ImageSelectionController`, `DriveSelectionController`, and
`FlashController` as children of `MainController`. Each of them is
used by the appropriate main page "steps", and contains logic specific
to them. The `MainController` hosts functionality that applies to the
page as a whole.
- Add JSDoc annotations fo every controller function/property.
- Unit test several controller functions.
- Simplify template logic.
The "GUI fifty-thousand foot view" section in ARCHITECTURE.md has been
removed since there is no longer a single place where you can see all
the interactions between components.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This PR fixes an uncaught error being thrown when
`OSOpenExternalService.open()` is called with an undefined value.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, the logic the controls the drive scanner and populates the
drive model lives in the main application controller.
This is not optimal because:
- The drive scanner stops populating the drives model when the
application is not on the main screen.
- An event handler subscribes to the drive scanner every time the user
navigates back to the main screen.
This PR moves the drive scanner logic to a run block in the entry point
of the application.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Error handling is currently a mess. The knowledge to correctly report an
error both to the end user and to us is scattered in many places.
This PR introduces the following changes:
- Rename `AnalyticsService.logDebug()` to `AnalyticsService.logDebug()`
to clarify better the intention of the function.
- Move `$log` decorators from an `AnalyticsService` run block to
`AnalyticsService.logDebug()`.
- Implement `AnalyticsService.logException()`, whose duty is to log an
exception to TrackJS or any related service, and log it to DevTools.
- Implement `ErrorService.reportException()`, whose duty is to report an
exception to every interested party. This means logging the error to
TrackJS, displaying it DevTools and showing a nice alert to the user.
This function is based from `handleError()` from `MainController`.
- Move global `$exceptionHandler` error handler to the entry point of
the application, and make it simply call
`ErrorService.reportException()`.
- Replace every `handleError()` call in `MainController` with
`ErrorService.reportException()`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The problem can be reproduced with the following steps:
- Start Etcher.
- Plug a single drive, and let Etcher auto-select it.
- Navigate to the settings screen.
- Go back to the main screen.
The drive is auto-removed for a very small amount of time, until
auto-selection takes care of selecting it again. This behaviour causes
no harm, but its a bit annoying.
The problem was caused by a previous fix, which auto-removed the drive
if navigating back to the main screen after a flash finished, however
the fix no longer makes a lot of sense now that we allow a drive to be
selected even when no image has been chosen.
Change-Type: patch
Changelog-Entry: Prevent selected drive from getting auto-removed when navigating back to the main screen from another screen.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we show "Finishing..." indenpendently on if we're waiting for
the flash to emit the "done" event, or we're waiting for the drive to be
unmounted.
As a way to simplify things, we move the hairy `ngShow` stack from the
main template to a nice function in the controller.
Change-Type: minor
Changelog-Entry: Show "Unmounting..." while unmounting a drive.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
There are some cases where AngularJS won't recognise that the available
drive list has changed, and incorrectly keeps asking the user to connect
a drive.
This problem was mainly witnessed in Windows, and could be reproduced
with the following steps:
- Start Etcher with no available drive.
- Select an image.
- Plug a drive.
Etcher keeps showing "Connect a drive" in the second step.
Change-Type: patch
Changelog-Entry: Fix new available drives not being recognised automatically in Windows.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
When using the `--robot` option, we write to `stdout`/`stderr` using
`WritableStream#write()`. This operation is asynchronous, however we're
not waiting for the data to be flushed before calling `process.exit()`
right after emitting the `done` event.
This causes a rance condition where sometimes `done` is never written to
the log file, and therefore the GUI remains waiting forever.
Change-Type: patch
Changelog-Entry: Fix application stuck at "Finishing".
See: https://github.com/resin-io/etcher/issues/613
See: https://github.com/resin-io/etcher/issues/609
See: https://github.com/resin-io/etcher/issues/573
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We were currently throwing a validation error if the error code was not
string when setting the flash results, however a number error code makes
sense in some cases, and its what its returned by a `ChildProcess`
object.
Change-Type: patch
See: https://github.com/resin-io/etcher/issues/609
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, the client application knows too much about how the flash
results are stored in the internal state, and relies on its structure to
perform its logic.
This PR introduces several getters to `FlashStateModel` and makes
`FlashStateModel.getFlashResults()` private, ensuring clients don't
depend on the flash results object.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
`lib/gui/app.js` contains a lot of code that should have been split long
ago. This PR extracts the "main page" logic into an actual page
component in `lib/gui/pages`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
`ImageWriterService` currently has two responsibilities. It contains
logic to start and manage a flash process, and provides an API to
interact with the current flash state.
To honour the single responsibility principle, we extract
`FlashStateModel` from `ImageWriterService`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We recently introduced a feature where a single available drive would be
auto-selected even if the user didn't select an image.
This introduces a potential scenario that brings the state to an
incoherent state:
- Start Etcher.
- Let a drive get auto-selected.
- Select an image larger than the auto-selected drive.
In this case, the drive is known to not have enough space to hold the
image, but remains auto-selected.
This PR makes sure the drive is deselected automatically in the above
scenario.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we subscribe to the store change events and manually update a
`.state` property in `ImageWriterService`. A much more elegant approach
is to provide a `.getFlashState()` function that fetches the flash state
directly from the store when needed.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, TrackJS would receive errors when running Etcher locally.
Most of the errors reported from those environments are due to
developing Etcher, and create a lot of noise in TrackJS.
With this PR, we ensure Etcher will only report errors when running
inside an `asar` archive, which means its a "packaged" Etcher version.
Change-Type: patch
Changelog-Entry: Only enable error reporting if running inside an `asar`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we intentionally prevent a drive from being auto-selected if
there was no selected image for UX purposes, however this way of
thinking has been challenged, and we didn't find a real UX problem by
doing this.
As a minor design improvement, we gray out the drive name in the main
window when an image hasn't been selected yet.
Change-Type: minor
Changelog-Entry: Perform drive auto-selection even when there is no selected image.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
When the user clicks on the backdrop of a modal, the modal promise gets
rejected, therefore triggering our application error handler.
UI Bootstrap provides a way to disable the backdrop completely, but
doesn't provide a way to allow a backdrop click to simply close the
modal rather than rejecting it, as if an issue happened.
To mitigate this issue, and still preserve the backdrop functionality,
we created `ModalService`, which abstracts the messy details of calling
`$uibModal`, and has custom logic to ignore "backdrop click" errors.
Change-Type: patch
Changelog-Entry: Fix "backdrop click" uncaught errors on modals.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Instead of storing the whole selected drive object, we barely store a
reference to the corresponding drive in the available drives array (the
reference being the drive device).
This greatly simplifies the application state in the following ways:
- The drive metadata (size, description, etc) is not duplicated in the
state, enforcing a single source of truth.
- If the selected drive stops being available (e.g: is unplugged), the
reference doesn't hold anymore, making this functionality very natural
to implement.
- Makes `SelectionStateModel.isCurrentDrive()` much more inuitive, since
we don't have to document that changes in the metadata of the drive
object, or extra keys such as `$$hashKey` don't change the result of
this function.
- Ensures the state never goes into a problematic state where we try to
to write to an unavailable drive.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Consider a directory name such as `Juan's Files`. When passing such path
as an argument to the child writer proxy script, Bash will complain
with:
> Unexpected EOF while looking for matching `'`
The solution is to manually escape quotes on the image path.
Change-Type: patch
Changelog-Entry: Escape quotes from image paths to prevent Bash errors on GNU/Linux and OS X.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The following PRs add support for a custom `_info` directory containing
metadata such an image URL, display name, logo, etc in
`etcher-image-stream`:
- https://github.com/resin-io-modules/etcher-image-stream/pull/16
- https://github.com/resin-io-modules/etcher-image-stream/pull/14
- https://github.com/resin-io-modules/etcher-image-stream/pull/13
Now that this module supports such metadata, we make use of it in the
GUI as follows:
- The file name is replaced with the display name.
- The file name links to the image URL.
- The "Select Image" logo is replaced with the image logo.
Some miscellaneous changes introduces in this PR to support the changes
described above:
- Implement `SelectionStateModel.getImageUrl()`.
- Implement `SelectionStateModel.getImageLogo()`.
- Implement `SelectionStateModel.getImageName()`.
- Ignore the "logo" image property when displaying the "Select image"
event, in order to not fill the console with SVG contents.
- Make `svg-icon` understand SVG strings as paths.
- Make `svg-icon` react to changes to the `path` attribute.
- Extract the core functionality of `openExternal` into
`OSOpenExternalService`.
- Upgrade `etcher-image-stream` to v3.0.1.
Change-Type: minor
Changelog-Entry: Support rich image extensions.
Fixes: https://github.com/resin-io/etcher/issues/470
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The current error message is a bit blurry. The new one is much more user
friendly and directly guides the user towards a possible "next action".
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We try our best to check that the images the user select are too big for
the selected drive as early as possible, but this probes to be
problematic with certain compressed formats, like bzip2, which doesn't
store any information about the uncompressed size, requiring a ~50s
intensive computation as a minimum to find it out.
For these kinds of formats, we don't perform an early check, but instead
gracefully handle the case where the drive doesn't have any more space.
This PR handles an `ENOSPC` error by displaying the alert orange ribbon,
and prompting the user to retry with a larger drive. This is a huge
improvement over the cryptic `EIO` error what was thrown before, and
over having Etcher freeze at a certain percentage point.
Change-Type: minor
Changelog-Entry: Display a nice alert ribbon if drive runs out of space.
See: https://github.com/resin-io/etcher/issues/571
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The Etcher CLI doesn't care if the drive exists or not. The user will
eventually find out since the CLI will output an `ENOENT` when trying to
actually write data, however its nicer from a UX perspective to catch
this early on.
Change-Type: minor
Changelog-Entry: Validate the existence of the passed drive.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We were currently checking if a drive was large enough for an image by checking
the image file size, completely ignoring compression. This results on a small
chance of the uncompressed image not actually fitting in the drive, and
throwing more weird errors later on.
In order to mitigate this, we use the new `.getEstimatedFinalSize()` function
from `etcher-image-stream`, which returns the uncompressed size in the case of
compressed images.
We needed to update the build script for OS X to configure NPM correctly,
otherwise `lzma-native` throws some errors due to an ABI V8 incompatibility
issue.
Fixes: #571
See: https://github.com/addaleax/lzma-native/issues/25
Change-Type: patch
Changelog-Entry: Check if drive is large enough using the final uncompressed size of the image.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This setting makes Etcher not filter non-removable drives, allowing you to
arbitrarily write to your system drives.
This is a dangerous option, therefore we present it in a separate section of
the settings page, and show an informative confirmation dialog.
Change-Type: minor
Changelog-Entry: Add an "unsafe" option to bypass drive protection.
Fixes: https://github.com/resin-io/etcher/issues/480
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We were facing quite some issues that involved debugging the writer child
process, which proved to be a very difficult task given the lack of information
we're exposing to the parent.
This is an attempt to improve that situation.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we take the image extension casing into account when
determining if the extension is a recognised one (e.g: `img`, `iso`,
etc). This causes an "Invalid image" error to be thrown when selecting
an image with an uppercase extension, like `UBUNTU.ISO`.
Change-Type: patch
Changelog-Entry: Don't throw an "Invalid image" error if the extension is not in lowercase.
Fixes: https://github.com/resin-io/etcher/issues/567
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The `$OWD` environment variable, which stands for "Original Working
Directory" is set in recent AppImageKit versions and equals the
directory from where the AppImage was run.
We set the open dialog default path to this environment variable for
consistency with other GNU/Linux applications.
Change-Type: patch,
Changelog-Entry: Set dialog default directory to the place where the AppImage was run from in GNU/Linux.
See: 1569d6f854
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The `elevator` module passes its arguments directly to
`child_process.execFile()`, which handles escaping spaces and other
weird issues by default.
Instead of passing a separate argument for every word of the writer
proxy script command, we passed the whole thing at once, which means we
didn't get the escaping features by default.
Change-Type: patch
Changelog-Entry: Fix flashing not starting when an image name contains a space.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Images may contain parenthesis. This is usually the case when you
re-download a file with a web browser, which atuaomtically appends `(N)`
to the path.
Not escaping parenthesis means that when passing the image path as an
argument to the write proxy script, bash will complain about it as a
syntax error on the command.
The fix is not necessary in Windows. I've been able to write images
containing parenthesis in that operating system without issues.
Change-Type: patch
Changelog-Entry: Fix error when writing images containing parenthesis in GNU/Linux and OS X.
Fixes: https://github.com/resin-io/etcher/issues/556
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, if we cancel elevation, we'd get a big scary validation error
about `passedValidation` not being passed to the flash results object.
This fix also requires some adjustments to `wasLastFlashSuccessful()` in
order to adapt to the cancellation scenario.
Since a cancelled elevation request means the writing never took place,
`wasLastFlashSuccessful()` returns `true` if so, without even looking at
`passedValidation`.
Change-Type: patch
Changelog-Entry: Fix error when cancelling an elevation request.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* upgrade: etcher-image-stream to v2.3.0
This version contains support for `hddimg` files.
Changelog-Entry: Add support for `hddimg` images.
Change-Type: minor
Fixes: https://github.com/resin-io/etcher/issues/549
Link: https://github.com/resin-io-modules/etcher-image-stream/blob/master/CHANGELOG.md#v230---2016-07-01
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* minifix(GUI): add a "many more" tooltip in the first step
The amount of image types we support is growing exponentially. Adding
the uncompressed extensions and the compressed ones in a tooltip gave us
room the breathe in the past, but its not enough anymore.
The current approach allows us to scale forever: we list the first three
extensions, and add a "many more" tooltip that shows all the rest.
Change-Type: patch
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Using `dismiss()` causes the promise to be rejected, with no reason in
this case. Given we made sure we were handling errors from dialogs in a
previous commit, this issue manifested itself.
The `close()` function, without arguments, makes more sense in this
case.
See: https://github.com/resin-io/etcher/pull/548
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, if either `OSDialogService.selectImage()` or
`DriveSelectorService.open()` are rejected, we completely swallow the
errors, making very hard to debug certain problems.
This PR takes care of showing the usual error dialog for those cases.
Change-Type: patch
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The code that performs an HTTP request to the S3 bucket where released
are stored and determines which is the latest available version was
extracted to a separate module called `etcher-latest-version`, mainly
for the website to be able to re-use this functionality.
See: https://github.com/resin-io-modules/etcher-latest-version
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
In certain timezones, like India's, the ETA would display very weird
numbers. The problem was that we passed a number of milliseconds to
MomentJS, which created a Date object based on it taking timezones into
consideration.
As a solution, we convert the seconds to a Date object containing the
lowest possible date values, and set its seconds to the ETA, since this
effectively represents just the number of seconds we're interested in.
Changelog-Entry: Fix incorrect ETA numbers in certain timezones.
Change-Type: patch
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>