Skip to content

Compass App: Activities screen, error handling and logs #2371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 2, 2024

Conversation

miquelbeltran
Copy link
Member

@miquelbeltran miquelbeltran commented Jul 29, 2024

This PR introduces the Activities screen, handling of errors in view models and commands, and logs using the dart logging package.

Activities

  • The screen loads a list of activities, split in daytime and evening activities, and the user can select them.
  • Server adds the endpoint /destination/<id>/activitity which was missing before.

Screencast provided:

Screencast.from.2024-07-29.16-29-02.webm

Error handling

UI Error handling:

In the screencast you can see a SnackBar appearing, since the "Confirm" button is not yet implemented.
The saveActivities Command returns an error Result.error(), then the error state is exposed by the Command and consumed by the listener in the ActivityScreen, which displays a SnackBar and consumes the state.

Functionality is similar to the one found in UI events - Consuming events can trigger state updates from the Android architecture guide, as the command state is "consumed" and cleared.

The Snackbar also includes an action to "try again". Tapping on it calls to the failed Command execute() so users can run the action again.

For example, here the saveActivities command failed, so error is true. Then we call to clearResult() to remove the failed status, and show a SnackBar, with the SnackBarAction that runs saveActivities again when tapped.

    if (widget.viewModel.saveActivities.error) {
      widget.viewModel.saveActivities.clearResult();
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: const Text('Error while saving activities'),
          action: SnackBarAction(
            label: "Try again",
            onPressed: widget.viewModel.saveActivities.execute,
          ),
        ),
      );
    }

Since commands expose running, error and completed, it is easy to implement loading and error indicator widgets:

Screencast.from.2024-07-29.16-55-42.webm

As side node, we can easily simulate that state by adding these lines in any of the repository implementations:

    await Future.delayed(Durations.extralong1);
    return Result.error(Exception('ERROR!'));

In-code error handling:

The project introduces the logging package.

In the entry point main_development.dart the log level is configured. Then in code, a Logger is creaded in each View Model with the name of the class. Then the log calls are used depending on the Result response, some finer traces are also added.

By default, they are printed to the IDE debug console, for example:

[SearchFormViewModel] Continents (7) loaded
[SearchFormViewModel] ItineraryConfig loaded
[SearchFormViewModel] Selected continent: Asia
[SearchFormViewModel] Selected date range: 2024-07-30 00:00:00.000 - 2024-08-08 00:00:00.000
[SearchFormViewModel] Set guests number: 1
[SearchFormViewModel] ItineraryConfig saved

Other changes

  • The json files containing destinations and activities are moved into the app/assets/ folders, and the server is querying those files instead of their own copy. This is done to avoid file duplication but we can make a copy of those assets files for the server if we decide to.

TODO Next

  • I will implement the "book a trip" screen which would complete the main application flow, which should introduce a more complex "component/use case" outside a view model.

Pre-launch Checklist

  • I read the Flutter Style Guide recently, and have followed its advice.
  • I signed the CLA.
  • I read the Contributors Guide.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-devrel channel on Discord.

…2359)

This PR introduces two new subprojects:

- `compass_server` under `compass_app/server`.
- `compass_model` under `compass_app/model`.

**`compass_server`**

- Dart server implemented using `shelf`.
- Created with the `dart create -t server-shelf` template.
- Implements two REST endpoints:
  - `GET /continent`: Returns the list of `Continent` as JSON.
  - `GET /destination`: Returns the list of `Destination` as JSON.
- Generated Docker files have been removed.
- Implemented tests.
- TODO: Implement some basic auth.

**`compass_model`**

- Dart package to share data model classes between the `server` and
`app`.
- Contains the data model classes (`Continent`, `Destination`).
- Generated JSON from/To methods and data classes using `freezed`.
- The sole purpose of this package is to host the data model. Other
shared code should go in a different package.

**Other changes**

- Created an API Client to connect to the local dart server.
- Created "remote" repositories, which also implement a basic in-memory
caching strategy.
- Created two dependency configurations, one with local repositories and
one with remote repos.
- Created two application main targets to select configuration:
- `lib/main_development.dart` which starts the app with the "local" data
configuration.
- `lib/main_staging.dart` which starts the app with the "remove" (local
dart server) configuration.
  - `lib/main.dart` still works as default entry point.
- Implemented tests for remote repositories.

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-devrel
channel on [Discord].

<!-- Links -->
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[CLA]: https://cla.developers.google.com/
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
[Contributors Guide]:
https://github.com/flutter/samples/blob/main/CONTRIBUTING.md

WIP

implement activity repository local

wip activity repository

updates and iOS stuff

implement navigation logic to activities

implement tests

fixing navigation with query parameters

refactor search queries

cleanup and comments

cleanup

add ItineraryConfig, refactor SearchScreen to use it

fix tests

refactor with Commands

refactor commands

add progress indicator to commands

simplify command

command tests

cleanup

simplify activity repository

cleanup

cleanup

cleanup

fix comment

should be Command0

refactor assets, server, and add activities endpoint

fix assets refactor

wip activities screen

add error state to commands

Added comments

handle errors in Command

add comments

WIP error indicator

widget lifecycle

wip activities and error handling

Add random errors to repositories to simulate recovering from errors

implement activites

WIP tests

create test activities screen

finish Activities UI
@miquelbeltran miquelbeltran marked this pull request as ready for review July 29, 2024 15:15
List<Activity> get daytimeActivities => _daytimeActivities;

/// List of evening [Activity] per destination.
List<Activity> get eveningActivities => _eveningActivities;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit / nonblocking comment:
I've recently learned about UnmodifiableListView, and I think this is the perfect place to use it :)

  /// List of daytime [Activity] per destination.
  UnmodifiableListView<Activity> get daytimeActivities => UnmodifiableListView(_daytimeActivities);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will give it a try!

Copy link
Contributor

@ericwindmill ericwindmill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One higher level consideration for the future: Should String's that are displayed in UI be put in a class somewhere as constants?

All my comments are nits, and otherwise it looks good to me.

@miquelbeltran
Copy link
Member Author

One higher level consideration for the future: Should String's that are displayed in UI be put in a class somewhere as constants?

All my comments are nits, and otherwise it looks good to me.

Agreed, I will do that for the next PR

@miquelbeltran
Copy link
Member Author

I have created a class Dimens in ui.core.theme.

This is a sealed class that has two implementations, one for mobile and one for desktop.

The method Dimens.of(context) allows code access either of the two depending on the screen size using MediaQuery.sizeOf(context).

These dimensions are now used to define the common vertical and horizontal spacing of the app between UI components, but also to screen borders, so the desktop application has a larger margin around it:

Screenshot 2024-07-30 at 17 22 40 Screenshot 2024-07-30 at 17 22 46

@miquelbeltran
Copy link
Member Author

One higher level consideration for the future: Should String's that are displayed in UI be put in a class somewhere as constants?

I have created an AppLocalization class under ui/core/localization/ which works with a LocalizationsDelegate. It implements a hardcoded map of strings similar to what we can find in the Flutter docs, just that this is only in English.

Maybe I missed a String or two, but works as a base for future localizations if we want to ;)

@ericwindmill
Copy link
Contributor

LGTM, feel free to merge at your leisure!

@miquelbeltran miquelbeltran merged commit 0305894 into compass-app Aug 2, 2024
1 check passed
@miquelbeltran miquelbeltran deleted the mb-compass_app-activitites-2 branch August 2, 2024 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants