From 26c07c1709e42f617109adedfd3b606de8a54b16 Mon Sep 17 00:00:00 2001 From: Zhanara <91150590+SourbaevaJanaraJ@users.noreply.github.com> Date: Thu, 6 Jul 2023 21:54:28 +0600 Subject: [PATCH] [Bazaar] Create UI of a single business page (#1303) * created Single Business, its stores, widgets * added svg * navigation from BazaarMain to SingleBusinessView * added dic to mapButton, deleted comments from BusinessDetailPage, sorted imports * fixed model, store and navigation * added translation, added xtra parameters in model and widgets * did fixes by PR comments * deleted unused assets * fixes by PR comments --- app/lib/l10n/arb/app_de.arb | 2 + app/lib/l10n/arb/app_en.arb | 2 + app/lib/l10n/arb/app_fr.arb | 2 + app/lib/l10n/arb/app_ru.arb | 2 + app/lib/models/bazaar/businesses.dart | 2 +- app/lib/models/bazaar/single_business.dart | 82 +++++++++++ app/lib/models/bazaar/single_business.g.dart | 57 ++++++++ .../bazaar/0_main/bazaar_main.dart | 6 +- .../logic/businesses_store.dart | 2 +- .../logic/businesses_store.g.dart | 0 .../view/businesses_view.dart | 8 +- .../businesses/widgets/businesses_card.dart | 86 +++++++++++ .../widgets/dropdown_widget.dart | 2 +- .../widgets/empty_businesses.dart | 0 .../logic/single_business_store.dart | 54 +++++++ .../logic/single_business_store.g.dart | 130 +++++++++++++++++ .../views/single_business_view.dart | 39 +++++ .../business_detail_address_widget.dart | 45 ++++++ .../widgets/business_detail_text_widget.dart | 40 +++++ .../single_business/widgets/map_button.dart | 32 ++++ .../widgets/single_business_detail.dart | 138 ++++++++++++++++++ .../new_bazaar/widgets/businesses_card.dart | 70 --------- app/pubspec.yaml | 1 + .../store/bazaar/businesses_store_test.dart | 4 +- 24 files changed, 724 insertions(+), 82 deletions(-) create mode 100644 app/lib/models/bazaar/single_business.dart create mode 100644 app/lib/models/bazaar/single_business.g.dart rename app/lib/page-encointer/new_bazaar/{ => businesses}/logic/businesses_store.dart (92%) rename app/lib/page-encointer/new_bazaar/{ => businesses}/logic/businesses_store.g.dart (100%) rename app/lib/page-encointer/new_bazaar/{ => businesses}/view/businesses_view.dart (80%) create mode 100644 app/lib/page-encointer/new_bazaar/businesses/widgets/businesses_card.dart rename app/lib/page-encointer/new_bazaar/{ => businesses}/widgets/dropdown_widget.dart (96%) rename app/lib/page-encointer/new_bazaar/{ => businesses}/widgets/empty_businesses.dart (100%) create mode 100644 app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.dart create mode 100644 app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.g.dart create mode 100644 app/lib/page-encointer/new_bazaar/single_business/views/single_business_view.dart create mode 100644 app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_address_widget.dart create mode 100644 app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_text_widget.dart create mode 100644 app/lib/page-encointer/new_bazaar/single_business/widgets/map_button.dart create mode 100644 app/lib/page-encointer/new_bazaar/single_business/widgets/single_business_detail.dart delete mode 100644 app/lib/page-encointer/new_bazaar/widgets/businesses_card.dart diff --git a/app/lib/l10n/arb/app_de.arb b/app/lib/l10n/arb/app_de.arb index a1971f3a5..e616b42f1 100644 --- a/app/lib/l10n/arb/app_de.arb +++ b/app/lib/l10n/arb/app_de.arb @@ -191,6 +191,7 @@ "lang": "Sprache", "lastVisited": "Zuletzt besucht", "leuZurichFAQ": "leu.zuerich FAQ", + "like":"Gefällt mir", "list": "Kontoauswahl", "loading": "Lädt...", "localizedReason": "Authentifizierung notwendig um auf dein Konto zuzugreifen", @@ -207,6 +208,7 @@ "meetupNotificationOneHourBeforeTitle": "1 Stunde übrig", "menu": "Menu", "mnemonic": "Mnemonik", + "moreInfo": "Mehr Infos:", "newbieContent": "Du hast dich als Newbie ohne bisherige Reputation registriert. Es ist nicht garantiert, dass du in diesem Cycle zugewiesen wirst, falls es viele Newbies hat. Bitte überprüfe deinen Zuweisungsstatus am Tag vor dem Cycle, um zu erfahren, ob und wo deine Versammlung stattfinden wird.", "newbieTitle": "Als Newbie Registriert - Dein Platz ist noch nicht sicher.", "next": "Weiter", diff --git a/app/lib/l10n/arb/app_en.arb b/app/lib/l10n/arb/app_en.arb index 559c2f58b..120d3bc41 100644 --- a/app/lib/l10n/arb/app_en.arb +++ b/app/lib/l10n/arb/app_en.arb @@ -191,6 +191,7 @@ "lang": "Language", "lastVisited": "Last visited", "leuZurichFAQ": "leu.zuerich FAQ", + "like":"Like", "list": "Account Select", "loading": "Loading...", "localizedReason": "Authenticate to access your account.", @@ -207,6 +208,7 @@ "meetupNotificationOneHourBeforeTitle": "1 hour left", "menu": "Menu", "mnemonic": "Mnemonic", + "moreInfo": "More Info:", "newbieContent": "You registered as a newbie without previous reputation. We can't guarantee that you will be assigned to a gathering this cycle if there are many newbies. Please check your assignment status on the day before the cycle to learn if and where your gathering will take place.", "newbieTitle": "Registered as newbie - your seat is not definite", "next": "Next", diff --git a/app/lib/l10n/arb/app_fr.arb b/app/lib/l10n/arb/app_fr.arb index a8c1366bc..2a6c92060 100644 --- a/app/lib/l10n/arb/app_fr.arb +++ b/app/lib/l10n/arb/app_fr.arb @@ -191,6 +191,7 @@ "lang": "Langue", "lastVisited": "Dernière visite", "leuZurichFAQ": "leu.zuerich FAQ", + "like": "J'aime", "list": "Sélection de compte", "loading": "Chargement…", "localizedReason": "Authentification nécessaire pour accéder à ton compte", @@ -207,6 +208,7 @@ "meetupNotificationOneHourBeforeTitle": "Encore 1 heure", "menu": "Menu", "mnemonic": "Mnémonique", + "moreInfo": "Plus d'informations:", "newbieContent": "Tu t'es enregistré en tant que Novice Il n'est pas garanti que tu sois dans ce cycle Si le nombre de Novices est élevé, tu ne seras pas assigné. S'il te plaît, vérifie ton statut d'assignation le jour précédant le cycle pour savoir pour savoir si et où ta réunion aura lieu.", "newbieTitle": "Enregistré comme Novice - Ta place n'est pas encore garantie", "next": "Continuer", diff --git a/app/lib/l10n/arb/app_ru.arb b/app/lib/l10n/arb/app_ru.arb index 537885699..75a81042f 100644 --- a/app/lib/l10n/arb/app_ru.arb +++ b/app/lib/l10n/arb/app_ru.arb @@ -191,6 +191,7 @@ "lang": "Язык", "lastVisited": "Последнее посещение", "leuZurichFAQ": "ЧЗВ leu.zuerich", + "like": "Нравится", "list": "Выбрать аккаунт", "loading": "Загружается...", "localizedReason": "Аутентифицируйтесь для доступа к вашей учетной записи", @@ -207,6 +208,7 @@ "meetupNotificationOneHourBeforeTitle": "Остался 1 час", "menu": "Меню", "mnemonic": "Мнемоническая фраза", + "moreInfo": "Дополнительная информация:", "newbieContent": "Вы зарегистрировались в качестве новичка без репутации. Нет гарантии, что Вас назначат на этот цикл, если в нем будет участвовать большое количество новичков. Пожалуйста, проверьте статус вашего назначения за день до цикла чтобы узнать, состоится ли ваше собрание и где оно будет проходить.", "newbieTitle": "Зарегистрирован в качестве Новичка - место не гарантировано.", "next": "Следующий", diff --git a/app/lib/models/bazaar/businesses.dart b/app/lib/models/bazaar/businesses.dart index 0b861d8f5..fc5357b79 100644 --- a/app/lib/models/bazaar/businesses.dart +++ b/app/lib/models/bazaar/businesses.dart @@ -1,4 +1,4 @@ -import 'package:encointer_wallet/page-encointer/new_bazaar/widgets/dropdown_widget.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; diff --git a/app/lib/models/bazaar/single_business.dart b/app/lib/models/bazaar/single_business.dart new file mode 100644 index 000000000..fc4498f0c --- /dev/null +++ b/app/lib/models/bazaar/single_business.dart @@ -0,0 +1,82 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'single_business.g.dart'; + +@JsonSerializable() +class SingleBusiness { + const SingleBusiness({ + required this.name, + required this.description, + required this.category, + required this.address, + required this.zipcode, + required this.addressDescription, + required this.telephone, + required this.email, + required this.longitude, + required this.latitude, + required this.openingHours1, + required this.openingHours2, + required this.logo, + required this.photo, + required this.offer, + required this.offerName1, + required this.offerName2, + required this.moreInfo, + this.status, + this.isLiked = false, + this.isLikedPersonally = false, + this.countLikes = 0, + }); + factory SingleBusiness.fromJson(Map json) => _$SingleBusinessFromJson(json); + Map toJson() => _$SingleBusinessToJson(this); + + final String name; + final String description; + final String category; + final String address; + final String zipcode; + final String addressDescription; + final String telephone; + final String email; + final double longitude; + final double latitude; + final String openingHours1; + final String openingHours2; + final String logo; + final String photo; + final String offer; + final String offerName1; + final String offerName2; + final String moreInfo; + final String? status; + final bool isLiked; + final bool isLikedPersonally; + final int countLikes; +} + +const singleBusinessMockData = { + 'name': 'Hatha Lisa', + 'description': + 'Nutze deine Leu, um deinem Körper und Geist etwas Gutes zu tun. Besuche eine Yogastunde im Kreis 4 oder Kreis 5 mit Lisa Stähli, einer Hatha-Yoga-Lehrerin mit über 4 Jahren Unterrichtserfahrung. Die Klassen sind für alle Niveaus geeignet, werden auf Englisch unterrichtet und bieten sowohl eine Herausforderung als auch die Möglichkeit, sein Gleichgewicht zu finden.\n\nErfahre mehr oder kontaktiere uns:\nhttps://hathalisa.com/', + 'category': 'Body & Soul', + 'addressDescription': 'Yoga Studio Zürich', + 'address': 'Zwinglistrasse, 8', + 'zipcode': '8004, Zürich', + 'telephone': '+41 123 456 789', + 'email': 'info@hathalisa.com', + 'longitude': 8.515962660312653, + 'latitude': 47.390349148891545, + 'openingHours1': 'Tuesdays 07:30-08:30', + 'openingHours2': 'Wednesday 12:15-13:20', + 'logo': 'QmUH7W2eAWTfHRYYV1YitZaz54sTjEwv6udjZjh7Tg47Xv', + 'photo': 'https://github.com/SourbaevaJanaraJ/lock_screen/blob/master/assets/hatha_lisa_single_b.png?raw=true', + 'offer': 'Offer for Leu', + 'offerName1': 'Single course LEU 25', + 'offerName2': '10-course subscription LEU 200', + 'moreInfo': 'With Leu since 01 January 2023', + 'status': 'Neu bei Leu', + 'isLiked': false, + 'isLikedPersonally': false, + 'countLikes': 0 +}; diff --git a/app/lib/models/bazaar/single_business.g.dart b/app/lib/models/bazaar/single_business.g.dart new file mode 100644 index 000000000..f6ce8e078 --- /dev/null +++ b/app/lib/models/bazaar/single_business.g.dart @@ -0,0 +1,57 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'single_business.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SingleBusiness _$SingleBusinessFromJson(Map json) => SingleBusiness( + name: json['name'] as String, + description: json['description'] as String, + category: json['category'] as String, + address: json['address'] as String, + zipcode: json['zipcode'] as String, + addressDescription: json['addressDescription'] as String, + telephone: json['telephone'] as String, + email: json['email'] as String, + longitude: (json['longitude'] as num).toDouble(), + latitude: (json['latitude'] as num).toDouble(), + openingHours1: json['openingHours1'] as String, + openingHours2: json['openingHours2'] as String, + logo: json['logo'] as String, + photo: json['photo'] as String, + offer: json['offer'] as String, + offerName1: json['offerName1'] as String, + offerName2: json['offerName2'] as String, + moreInfo: json['moreInfo'] as String, + status: json['status'] as String?, + isLiked: json['isLiked'] as bool? ?? false, + isLikedPersonally: json['isLikedPersonally'] as bool? ?? false, + countLikes: json['countLikes'] as int? ?? 0, + ); + +Map _$SingleBusinessToJson(SingleBusiness instance) => { + 'name': instance.name, + 'description': instance.description, + 'category': instance.category, + 'address': instance.address, + 'zipcode': instance.zipcode, + 'addressDescription': instance.addressDescription, + 'telephone': instance.telephone, + 'email': instance.email, + 'longitude': instance.longitude, + 'latitude': instance.latitude, + 'openingHours1': instance.openingHours1, + 'openingHours2': instance.openingHours2, + 'logo': instance.logo, + 'photo': instance.photo, + 'offer': instance.offer, + 'offerName1': instance.offerName1, + 'offerName2': instance.offerName2, + 'moreInfo': instance.moreInfo, + 'status': instance.status, + 'isLiked': instance.isLiked, + 'isLikedPersonally': instance.isLikedPersonally, + 'countLikes': instance.countLikes, + }; diff --git a/app/lib/page-encointer/bazaar/0_main/bazaar_main.dart b/app/lib/page-encointer/bazaar/0_main/bazaar_main.dart index bdcb16e5c..8f9267a80 100644 --- a/app/lib/page-encointer/bazaar/0_main/bazaar_main.dart +++ b/app/lib/page-encointer/bazaar/0_main/bazaar_main.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/logic/businesses_store.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/view/businesses_view.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart'; import 'package:encointer_wallet/theme/custom/extension/theme_extension.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/logic/businesses_store.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/view/businesses_view.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/widgets/dropdown_widget.dart'; import 'package:encointer_wallet/l10n/l10.dart'; class BazaarPage extends StatelessWidget { diff --git a/app/lib/page-encointer/new_bazaar/logic/businesses_store.dart b/app/lib/page-encointer/new_bazaar/businesses/logic/businesses_store.dart similarity index 92% rename from app/lib/page-encointer/new_bazaar/logic/businesses_store.dart rename to app/lib/page-encointer/new_bazaar/businesses/logic/businesses_store.dart index 40b420efe..cf6836954 100644 --- a/app/lib/page-encointer/new_bazaar/logic/businesses_store.dart +++ b/app/lib/page-encointer/new_bazaar/businesses/logic/businesses_store.dart @@ -1,6 +1,6 @@ import 'package:mobx/mobx.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/widgets/dropdown_widget.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart'; import 'package:encointer_wallet/utils/fetch_status.dart'; import 'package:encointer_wallet/models/bazaar/businesses.dart'; diff --git a/app/lib/page-encointer/new_bazaar/logic/businesses_store.g.dart b/app/lib/page-encointer/new_bazaar/businesses/logic/businesses_store.g.dart similarity index 100% rename from app/lib/page-encointer/new_bazaar/logic/businesses_store.g.dart rename to app/lib/page-encointer/new_bazaar/businesses/logic/businesses_store.g.dart diff --git a/app/lib/page-encointer/new_bazaar/view/businesses_view.dart b/app/lib/page-encointer/new_bazaar/businesses/view/businesses_view.dart similarity index 80% rename from app/lib/page-encointer/new_bazaar/view/businesses_view.dart rename to app/lib/page-encointer/new_bazaar/businesses/view/businesses_view.dart index 9f2c10e90..7208b3c00 100644 --- a/app/lib/page-encointer/new_bazaar/view/businesses_view.dart +++ b/app/lib/page-encointer/new_bazaar/businesses/view/businesses_view.dart @@ -1,13 +1,13 @@ -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:provider/provider.dart'; import 'package:encointer_wallet/common/components/error/error_view.dart'; import 'package:encointer_wallet/common/components/loading/centered_activity_indicator.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/widgets/empty_businesses.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/widgets/empty_businesses.dart'; import 'package:encointer_wallet/models/bazaar/businesses.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/logic/businesses_store.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/widgets/businesses_card.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/logic/businesses_store.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/widgets/businesses_card.dart'; import 'package:encointer_wallet/utils/fetch_status.dart'; class BusinessesView extends StatelessWidget { diff --git a/app/lib/page-encointer/new_bazaar/businesses/widgets/businesses_card.dart b/app/lib/page-encointer/new_bazaar/businesses/widgets/businesses_card.dart new file mode 100644 index 000000000..eef21a855 --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/businesses/widgets/businesses_card.dart @@ -0,0 +1,86 @@ +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/logic/single_business_store.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/views/single_business_view.dart'; +import 'package:flutter/material.dart'; + +import 'package:encointer_wallet/models/bazaar/businesses.dart'; +import 'package:encointer_wallet/theme/theme.dart'; +import 'package:provider/provider.dart'; + +class BusinessesCard extends StatelessWidget { + const BusinessesCard({super.key, required this.businesses}); + + final Businesses businesses; + + @override + Widget build(BuildContext context) { + final textTheme = context.textTheme; + return InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Provider( + create: (context) => SingleBusinessStore(businesses)..getSingleBusiness(), + child: const SingleBusinessView(), + ), + ), + ); + }, + child: SizedBox( + height: 160, + child: Card( + margin: const EdgeInsets.only(top: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DecoratedBox( + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(businesses.photo), + ), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + bottomLeft: Radius.circular(20), + ), + ), + child: const SizedBox(height: double.infinity, width: 130), + ), + Expanded( + child: ListTile( + contentPadding: const EdgeInsets.fromLTRB(10, 10, 10, 5), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: Text(businesses.category.name, style: textTheme.bodySmall)), + Text( + businesses.status?.name ?? '', + style: textTheme.bodySmall!.copyWith(color: businesses.status?.textColor ?? Colors.black), + ) + ], + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 28), + Text(businesses.name, style: textTheme.labelLarge), + const SizedBox(height: 8), + Text( + businesses.description, + style: textTheme.bodyMedium, + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + const SizedBox(height: 18), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/app/lib/page-encointer/new_bazaar/widgets/dropdown_widget.dart b/app/lib/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart similarity index 96% rename from app/lib/page-encointer/new_bazaar/widgets/dropdown_widget.dart rename to app/lib/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart index d0739bdfc..4b575f85c 100644 --- a/app/lib/page-encointer/new_bazaar/widgets/dropdown_widget.dart +++ b/app/lib/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart @@ -4,7 +4,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:provider/provider.dart'; import 'package:encointer_wallet/theme/custom/colors/app_colors.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/logic/businesses_store.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/logic/businesses_store.dart'; class DropdownWidget extends StatefulWidget { const DropdownWidget({super.key}); diff --git a/app/lib/page-encointer/new_bazaar/widgets/empty_businesses.dart b/app/lib/page-encointer/new_bazaar/businesses/widgets/empty_businesses.dart similarity index 100% rename from app/lib/page-encointer/new_bazaar/widgets/empty_businesses.dart rename to app/lib/page-encointer/new_bazaar/businesses/widgets/empty_businesses.dart diff --git a/app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.dart b/app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.dart new file mode 100644 index 000000000..47f2f1fe9 --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.dart @@ -0,0 +1,54 @@ +import 'package:encointer_wallet/models/bazaar/businesses.dart'; +import 'package:mobx/mobx.dart'; + +import 'package:encointer_wallet/models/bazaar/single_business.dart'; +import 'package:encointer_wallet/utils/fetch_status.dart'; + +part 'single_business_store.g.dart'; + +// ignore: library_private_types_in_public_api +class SingleBusinessStore = _SingleBusinessStoreBase with _$SingleBusinessStore; + +abstract class _SingleBusinessStoreBase with Store { + _SingleBusinessStoreBase(this.businesses, + {bool isLiked1 = false, bool isLikedPersonally1 = false, int countLikes1 = 0}) + : isLiked = isLiked1, + isLikedPersonally = isLikedPersonally1, + countLikes = countLikes1; + final Businesses businesses; + + @observable + late bool isLiked; + + @observable + late bool isLikedPersonally; + + @observable + late int countLikes; + + @observable + SingleBusiness? singleBusiness; + + @observable + FetchStatus fetchStatus = FetchStatus.loading; + + @action + Future getSingleBusiness() async { + fetchStatus = FetchStatus.loading; + await Future.delayed(const Duration(seconds: 1)); + final items = SingleBusiness.fromJson(singleBusinessMockData); + singleBusiness = items; + fetchStatus = FetchStatus.success; + } + + @action + void toggleLikes() { + isLiked = !isLiked; + isLiked ? countLikes++ : countLikes--; + } + + @action + void toggleOwnLikes() { + isLikedPersonally = !isLikedPersonally; + } +} diff --git a/app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.g.dart b/app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.g.dart new file mode 100644 index 000000000..3e0ad0d7b --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/logic/single_business_store.g.dart @@ -0,0 +1,130 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'single_business_store.dart'; + +// ************************************************************************** +// StoreGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers + +mixin _$SingleBusinessStore on _SingleBusinessStoreBase, Store { + late final _$isLikedAtom = Atom(name: '_SingleBusinessStoreBase.isLiked', context: context); + + @override + bool get isLiked { + _$isLikedAtom.reportRead(); + return super.isLiked; + } + + @override + set isLiked(bool value) { + _$isLikedAtom.reportWrite(value, super.isLiked, () { + super.isLiked = value; + }); + } + + late final _$isLikedPersonallyAtom = Atom(name: '_SingleBusinessStoreBase.isLikedPersonally', context: context); + + @override + bool get isLikedPersonally { + _$isLikedPersonallyAtom.reportRead(); + return super.isLikedPersonally; + } + + @override + set isLikedPersonally(bool value) { + _$isLikedPersonallyAtom.reportWrite(value, super.isLikedPersonally, () { + super.isLikedPersonally = value; + }); + } + + late final _$countLikesAtom = Atom(name: '_SingleBusinessStoreBase.countLikes', context: context); + + @override + int get countLikes { + _$countLikesAtom.reportRead(); + return super.countLikes; + } + + @override + set countLikes(int value) { + _$countLikesAtom.reportWrite(value, super.countLikes, () { + super.countLikes = value; + }); + } + + late final _$singleBusinessAtom = Atom(name: '_SingleBusinessStoreBase.singleBusiness', context: context); + + @override + SingleBusiness? get singleBusiness { + _$singleBusinessAtom.reportRead(); + return super.singleBusiness; + } + + @override + set singleBusiness(SingleBusiness? value) { + _$singleBusinessAtom.reportWrite(value, super.singleBusiness, () { + super.singleBusiness = value; + }); + } + + late final _$fetchStatusAtom = Atom(name: '_SingleBusinessStoreBase.fetchStatus', context: context); + + @override + FetchStatus get fetchStatus { + _$fetchStatusAtom.reportRead(); + return super.fetchStatus; + } + + @override + set fetchStatus(FetchStatus value) { + _$fetchStatusAtom.reportWrite(value, super.fetchStatus, () { + super.fetchStatus = value; + }); + } + + late final _$getSingleBusinessAsyncAction = + AsyncAction('_SingleBusinessStoreBase.getSingleBusiness', context: context); + + @override + Future getSingleBusiness() { + return _$getSingleBusinessAsyncAction.run(() => super.getSingleBusiness()); + } + + late final _$_SingleBusinessStoreBaseActionController = + ActionController(name: '_SingleBusinessStoreBase', context: context); + + @override + void toggleLikes() { + final _$actionInfo = + _$_SingleBusinessStoreBaseActionController.startAction(name: '_SingleBusinessStoreBase.toggleLikes'); + try { + return super.toggleLikes(); + } finally { + _$_SingleBusinessStoreBaseActionController.endAction(_$actionInfo); + } + } + + @override + void toggleOwnLikes() { + final _$actionInfo = + _$_SingleBusinessStoreBaseActionController.startAction(name: '_SingleBusinessStoreBase.toggleOwnLikes'); + try { + return super.toggleOwnLikes(); + } finally { + _$_SingleBusinessStoreBaseActionController.endAction(_$actionInfo); + } + } + + @override + String toString() { + return ''' +isLiked: ${isLiked}, +isLikedPersonally: ${isLikedPersonally}, +countLikes: ${countLikes}, +singleBusiness: ${singleBusiness}, +fetchStatus: ${fetchStatus} + '''; + } +} diff --git a/app/lib/page-encointer/new_bazaar/single_business/views/single_business_view.dart b/app/lib/page-encointer/new_bazaar/single_business/views/single_business_view.dart new file mode 100644 index 000000000..5db3d64b0 --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/views/single_business_view.dart @@ -0,0 +1,39 @@ +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; + +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/widgets/single_business_detail.dart'; +import 'package:encointer_wallet/common/components/error/error_view.dart'; +import 'package:encointer_wallet/common/components/loading/centered_activity_indicator.dart'; + +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/logic/single_business_store.dart'; +import 'package:encointer_wallet/utils/fetch_status.dart'; + +class SingleBusinessView extends StatelessWidget { + const SingleBusinessView({super.key}); + + @override + Widget build(BuildContext context) { + final store = context.watch(); + return Scaffold( + appBar: AppBar( + title: Observer(builder: (_) { + return switch (store.fetchStatus) { + FetchStatus.success => Text(store.singleBusiness!.name.toUpperCase()), + _ => const SizedBox(), + }; + }), + ), + body: Observer(builder: (_) { + switch (store.fetchStatus) { + case FetchStatus.loading: + return const CenteredActivityIndicator(); + case FetchStatus.success: + return SingleBusinessDetail(singleBusiness: store.singleBusiness!); + case FetchStatus.error: + return const ErrorView(); + } + }), + ); + } +} diff --git a/app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_address_widget.dart b/app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_address_widget.dart new file mode 100644 index 000000000..692b9b83e --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_address_widget.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:encointer_wallet/theme/custom/extension/theme_extension.dart'; + +class BusinessDetailAddressWidget extends StatelessWidget { + const BusinessDetailAddressWidget({ + required this.text, + required this.description, + required this.address, + required this.zipCode, + required this.email, + required this.phoneNum, + super.key, + }); + final String text; + final String description; + final String address; + final String zipCode; + final String email; + final String phoneNum; + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.topLeft, + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '$text\n', + style: context.textTheme.titleLarge!.copyWith(color: context.colorScheme.primary, fontSize: 18), + ), + TextSpan( + text: '$description\n', + style: context.textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.bold), + ), + TextSpan(text: '$address\n', style: context.textTheme.bodyMedium!.copyWith(height: 1.3)), + TextSpan(text: '$zipCode\n', style: context.textTheme.bodyMedium!.copyWith(height: 1.3)), + TextSpan(text: '$email\n', style: context.textTheme.bodyMedium!.copyWith(height: 1.3)), + TextSpan(text: phoneNum, style: context.textTheme.bodyMedium!.copyWith(height: 1.5)), + ], + ), + ), + ); + } +} diff --git a/app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_text_widget.dart b/app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_text_widget.dart new file mode 100644 index 000000000..52b4afd7b --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/widgets/business_detail_text_widget.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +import 'package:encointer_wallet/theme/custom/extension/theme_extension.dart'; + +class BusinessDetailTextWidget extends StatelessWidget { + const BusinessDetailTextWidget({ + required this.text, + required this.text1, + required this.text2, + super.key, + }); + final String text; + final String text1; + final String text2; + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.topLeft, + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '$text\n', + style: context.textTheme.titleLarge!.copyWith(color: context.colorScheme.primary, fontSize: 18), + ), + TextSpan( + text: '$text1\n', + style: context.textTheme.bodyMedium!.copyWith(height: 1.4), + ), + TextSpan( + text: '$text2\n', + style: context.textTheme.bodyMedium!.copyWith(height: 1.4), + ), + ], + ), + ), + ); + } +} diff --git a/app/lib/page-encointer/new_bazaar/single_business/widgets/map_button.dart b/app/lib/page-encointer/new_bazaar/single_business/widgets/map_button.dart new file mode 100644 index 000000000..ef7e2d9bc --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/widgets/map_button.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:encointer_wallet/theme/custom/extension/theme_extension.dart'; +import 'package:encointer_wallet/theme/theme.dart'; +import 'package:encointer_wallet/l10n/l10.dart'; + +class MapButton extends StatelessWidget { + const MapButton({ + required this.onPressed, + super.key, + }); + + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.centerLeft, + child: ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: context.colorScheme.onSecondary, + padding: const EdgeInsets.fromLTRB(30, 12, 30, 12), + elevation: 7, + ), + child: Text( + context.l10n.openMapApplication, + style: context.textTheme.displaySmall!.copyWith(fontSize: 16), + ), + ), + ); + } +} diff --git a/app/lib/page-encointer/new_bazaar/single_business/widgets/single_business_detail.dart b/app/lib/page-encointer/new_bazaar/single_business/widgets/single_business_detail.dart new file mode 100644 index 000000000..e7d8b320b --- /dev/null +++ b/app/lib/page-encointer/new_bazaar/single_business/widgets/single_business_detail.dart @@ -0,0 +1,138 @@ +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/logic/single_business_store.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:provider/provider.dart'; + +import 'package:encointer_wallet/l10n/l10.dart'; +import 'package:encointer_wallet/gen/assets.gen.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/widgets/business_detail_text_widget.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/widgets/business_detail_address_widget.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/single_business/widgets/map_button.dart'; +import 'package:encointer_wallet/models/bazaar/single_business.dart'; +import 'package:encointer_wallet/theme/theme.dart'; +import 'package:encointer_wallet/models/location/location.dart'; +import 'package:encointer_wallet/service/launch/app_launch.dart'; + +class SingleBusinessDetail extends StatelessWidget { + const SingleBusinessDetail({ + required this.singleBusiness, + super.key, + }); + + final SingleBusiness singleBusiness; + + @override + Widget build(BuildContext context) { + final store = context.watch(); + final l10n = context.l10n; + return SingleChildScrollView( + child: Card( + child: Column( + children: [ + Image.network(singleBusiness.photo, width: double.infinity, fit: BoxFit.cover), + Padding( + padding: const EdgeInsets.fromLTRB(30, 20, 30, 60), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + singleBusiness.category, + style: context.textTheme.bodySmall, + ), + Text( + singleBusiness.status!, + style: context.textTheme.bodySmall!.copyWith(color: const Color(0xFF35B731)), + ) + ], + ), + const SizedBox(height: 8), + Observer(builder: (_) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + InkWell( + onTap: context.read().toggleOwnLikes, + child: Assets.avatars.participant00.svg( + height: 19, + colorFilter: store.isLikedPersonally + ? null + : const ColorFilter.mode(Colors.white, BlendMode.color)), + ), + const SizedBox(width: 10), + Text( + l10n.like, + style: context.textTheme.bodySmall! + .copyWith(decoration: TextDecoration.underline, fontWeight: FontWeight.bold), + ), + ], + ), + Row( + children: [ + InkWell( + onTap: context.read().toggleLikes, + child: Assets.avatars.participant00.svg( + height: 25, + colorFilter: + store.isLiked ? null : const ColorFilter.mode(Colors.white, BlendMode.color)), + ), + const SizedBox(width: 10), + Text('${store.countLikes}'), + ], + ), + ], + ); + }), + const SizedBox(height: 20), + Text( + singleBusiness.description, + style: context.textTheme.bodyMedium!.copyWith(height: 1.5, fontSize: 16), + ), + const SizedBox(height: 40), + BusinessDetailTextWidget( + text: singleBusiness.offer, + text1: singleBusiness.offerName1, + text2: singleBusiness.offerName2, + ), + BusinessDetailTextWidget( + text: l10n.openningHours, + text1: singleBusiness.openingHours1, + text2: singleBusiness.openingHours2, + ), + const SizedBox(height: 20), + BusinessDetailAddressWidget( + text: l10n.address, + description: singleBusiness.addressDescription, + address: singleBusiness.address, + zipCode: singleBusiness.zipcode, + email: singleBusiness.email, + phoneNum: singleBusiness.telephone, + ), + const SizedBox(height: 20), + MapButton( + onPressed: () { + final location = Location( + singleBusiness.latitude.toString(), + singleBusiness.longitude.toString(), + ); + AppLaunch.launchMap(location); + }, + ), + const SizedBox(height: 40), + BusinessDetailTextWidget( + text: l10n.moreInfo, + text1: singleBusiness.moreInfo, + text2: '', + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/app/lib/page-encointer/new_bazaar/widgets/businesses_card.dart b/app/lib/page-encointer/new_bazaar/widgets/businesses_card.dart deleted file mode 100644 index 2734dddc1..000000000 --- a/app/lib/page-encointer/new_bazaar/widgets/businesses_card.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:encointer_wallet/models/bazaar/businesses.dart'; -import 'package:encointer_wallet/theme/theme.dart'; - -class BusinessesCard extends StatelessWidget { - const BusinessesCard({super.key, required this.businesses}); - - final Businesses businesses; - - @override - Widget build(BuildContext context) { - final textTheme = context.textTheme; - return SizedBox( - height: 160, - child: Card( - margin: const EdgeInsets.only(top: 10), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - DecoratedBox( - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: NetworkImage(businesses.photo), - ), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), - bottomLeft: Radius.circular(20), - ), - ), - child: const SizedBox(height: double.infinity, width: 130), - ), - Expanded( - child: ListTile( - contentPadding: const EdgeInsets.fromLTRB(10, 10, 10, 5), - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded(child: Text(businesses.category.name, style: textTheme.bodySmall)), - Text( - businesses.status?.name ?? '', - style: textTheme.bodySmall!.copyWith(color: businesses.status?.textColor ?? Colors.black), - ) - ], - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 28), - Text(businesses.name, style: textTheme.labelLarge), - const SizedBox(height: 8), - Text( - businesses.description, - style: textTheme.bodyMedium, - overflow: TextOverflow.ellipsis, - maxLines: 2, - ), - const SizedBox(height: 18), - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 5e66788bc..fdbfc434e 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -134,6 +134,7 @@ flutter: - assets/avatars/ - assets/nctr_logo.svg - assets/nctr_logo_faces_only_thick.svg + # fonts: # - family: Schyler diff --git a/app/test/store/bazaar/businesses_store_test.dart b/app/test/store/bazaar/businesses_store_test.dart index 70f31da64..2eced7ebc 100644 --- a/app/test/store/bazaar/businesses_store_test.dart +++ b/app/test/store/bazaar/businesses_store_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/logic/businesses_store.dart'; -import 'package:encointer_wallet/page-encointer/new_bazaar/widgets/dropdown_widget.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/widgets/dropdown_widget.dart'; +import 'package:encointer_wallet/page-encointer/new_bazaar/businesses/logic/businesses_store.dart'; import 'package:encointer_wallet/utils/fetch_status.dart'; void main() {