diff --git a/assets/svgs/ClashBot-HomePage.svg b/assets/svgs/ClashBot-HomePage.svg new file mode 100644 index 0000000..9401f14 --- /dev/null +++ b/assets/svgs/ClashBot-HomePage.svg @@ -0,0 +1 @@ +AAA0RGp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAADQeanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46dXVpZDpmZDIwODgzMS01YTgxLTRiM2MtYmEwNi00NTA5ODZlZTNmZGIAAAABtGp1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAANdqdW1iAAAAJmp1bWRjYm9yABEAEIAAAKoAOJtxA2MycGEuYWN0aW9ucwAAAACpY2JvcqFnYWN0aW9uc4GjZmFjdGlvbmtjMnBhLmVkaXRlZG1zb2Z0d2FyZUFnZW50bUFkb2JlIEZpcmVmbHlxZGlnaXRhbFNvdXJjZVR5cGV4U2h0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvY29tcG9zaXRlV2l0aFRyYWluZWRBbGdvcml0aG1pY01lZGlhAAAArGp1bWIAAAAoanVtZGNib3IAEQAQgAAAqgA4m3EDYzJwYS5oYXNoLmRhdGEAAAAAfGNib3KlamV4Y2x1c2lvbnOBomVzdGFydBjhZmxlbmd0aBlFsGRuYW1lbmp1bWJmIG1hbmlmZXN0Y2FsZ2ZzaGEyNTZkaGFzaFggyMc6F5cV/0m3EYFBFXDxvH3RiPC0jhJDBYou9EyQDNVjcGFkSQAAAAAAAAAAAAAAAgtqdW1iAAAAJGp1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0AAAAB32Nib3KoaGRjOnRpdGxlb0dlbmVyYXRlZCBJbWFnZWlkYzpmb3JtYXRtaW1hZ2Uvc3ZnK3htbGppbnN0YW5jZUlEeCx4bXA6aWlkOjMwZTQ4ZGE0LTI1MTItNDE2MS05M2MyLWYzMDkzYzEwMmE0Mm9jbGFpbV9nZW5lcmF0b3J4NkFkb2JlX0lsbHVzdHJhdG9yLzI5LjMgYWRvYmVfYzJwYS8wLjcuNiBjMnBhLXJzLzAuMjUuMnRjbGFpbV9nZW5lcmF0b3JfaW5mb4G/ZG5hbWVxQWRvYmUgSWxsdXN0cmF0b3JndmVyc2lvbmQyOS4z/2lzaWduYXR1cmV4GXNlbGYjanVtYmY9YzJwYS5zaWduYXR1cmVqYXNzZXJ0aW9uc4KiY3VybHg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DjBP8h1w5RmXOBD+lo3aQ5vLnY3BhZFkTgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZZAQAB6Q1bOZcwlG4DkrvjTNn6MMTewhS/ZENkuuX7VcrlcJpAC7SH54T9snJDFEbmcvALvSTpFMkJPI6bEjUZuoNu1tDLs1jiHkDB3WIbOXbLwa/40lo5FPahItmjCLhOg5XqOOn9KDHIX6KLZYfRYSoe3bBzhdzxUG6K7UyZgORuZAKovTExDDsFnxOwnULVK0BXmEg0shZAG+bj/V7O7xV1JFxpjWmL+jOzn+N9YcpELLj2/4RUcSM3I21nNSj+ZILW49PY/KDoruC387ya7bwH5LD6dvA+/GKFLLq962ztzPZZgfbyFhGS4x34Dgn0Yq0t88pKUr+dOgO56/rRjdf7 \ No newline at end of file diff --git a/assets/images/BotIcon.webp b/images/BotIcon.webp similarity index 100% rename from assets/images/BotIcon.webp rename to images/BotIcon.webp diff --git a/assets/images/JGIcon.webp b/images/JGIcon.webp similarity index 100% rename from assets/images/JGIcon.webp rename to images/JGIcon.webp diff --git a/assets/images/MidIcon.webp b/images/MidIcon.webp similarity index 100% rename from assets/images/MidIcon.webp rename to images/MidIcon.webp diff --git a/assets/images/SuppIcon.webp b/images/SuppIcon.webp similarity index 100% rename from assets/images/SuppIcon.webp rename to images/SuppIcon.webp diff --git a/assets/images/TopIcon.webp b/images/TopIcon.webp similarity index 100% rename from assets/images/TopIcon.webp rename to images/TopIcon.webp diff --git a/lib/globals/credentials.dart b/lib/globals/credentials.dart index dad01de..080c944 100644 --- a/lib/globals/credentials.dart +++ b/lib/globals/credentials.dart @@ -1,7 +1,9 @@ import 'dart:io'; + abstract class Credentials { - static const APP_DISCORD_OAUTH_CLIENT_ID = "837629412328734740"; - static const APP_DISCORD_OAUTH_REDIRECT_URI = "http://localhost:4200/login"; - static const SCOPE = ['identify', 'guilds']; - static final appUserCredentialsFile = new File("~/.myapp/credentials.json"); -} \ No newline at end of file + // static const APP_DISCORD_OAUTH_CLIENT_ID = "837629412328734740"; + static const APP_DISCORD_OAUTH_CLIENT_ID = "839586949748228156"; + static const APP_DISCORD_OAUTH_REDIRECT_URI = "http://localhost:4200/login"; + static const SCOPE = ['identify', 'guilds']; + static final appUserCredentialsFile = new File("~/.myapp/credentials.json"); +} diff --git a/lib/globals/global_settings.dart b/lib/globals/global_settings.dart index ffe2ae6..6d622eb 100644 --- a/lib/globals/global_settings.dart +++ b/lib/globals/global_settings.dart @@ -1,5 +1,4 @@ import 'package:clashbot_flutter/clients/discord_client.dart'; -import 'package:clashbot_flutter/clients/mock_discord_client.dart'; import 'package:clashbot_flutter/globals/credentials.dart'; import 'package:oauth2_client/oauth2_helper.dart'; diff --git a/lib/main.dart b/lib/main.dart index c28acf3..d43c203 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,8 +3,10 @@ import 'dart:developer' as developer; import 'package:clash_bot_api/api.dart'; import 'package:clashbot_flutter/core/config/env.dart'; import 'package:clashbot_flutter/globals/color_schemes.dart'; +import 'package:clashbot_flutter/models/clash_team.dart'; import 'package:clashbot_flutter/models/model_first_time.dart'; import 'package:clashbot_flutter/pages/home/page/home_v2.dart'; +import 'package:clashbot_flutter/pages/home/page/widgets/team_card.dart'; import 'package:clashbot_flutter/pages/intro/welcome_page.dart'; import 'package:clashbot_flutter/routes.dart'; import 'package:clashbot_flutter/services/clashbot_service.dart'; @@ -15,7 +17,10 @@ import 'package:clashbot_flutter/services/discord_service_mock_impl.dart'; import 'package:clashbot_flutter/services/riot_resources_service.dart'; import 'package:clashbot_flutter/services/riot_resources_service_impl.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; +import 'package:clashbot_flutter/stores/riot_champion.store.dart'; import 'package:clashbot_flutter/stores/v2-stores/clash.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/error_handler.store.dart'; import 'package:clashbot_flutter/styles.dart'; import 'package:clashbot_flutter/utils/reusable_widgets.dart'; import 'package:flutter/material.dart'; @@ -26,6 +31,7 @@ import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; import 'package:provider/provider.dart'; import 'package:validators/validators.dart'; +import 'package:storybook_flutter/storybook_flutter.dart'; import 'generated/git_info.dart'; import 'globals/global_settings.dart'; @@ -128,25 +134,33 @@ class _MyAppState extends State { create: (_) => RiotResourceServiceImpl()), Provider( create: (_) => ClashBotEventsService()), - ProxyProvider4( - update: (_, discordService, clashBotService, riotResourceService, - clashBotEventService, __) => - ApplicationDetailsStore(discordService, clashBotService, - riotResourceService, clashBotEventService)), - ProxyProvider2( - update: (_, applicationDetailsStore, clashBotService, __) => - ClashStore(clashBotService, applicationDetailsStore)) + Provider(create: (_) => ErrorHandlerStore()), + ProxyProvider2( + update: (_, clashBotService, errorHandlerStore, __) => + ClashStore(clashBotService, errorHandlerStore)), + ProxyProvider2( + update: (_, discordService, errorHandlerStore, __) => + DiscordDetailsStore(discordService, errorHandlerStore)), + ProxyProvider2( + update: (_, riotResourceService, errorHandlerStore, __) => + RiotChampionStore(riotResourceService, errorHandlerStore)), + // Depends on ClashStore, DiscordDetailsStore, RiotChampionStore, ErrorHandlerStore + ProxyProvider4( + update: (_, clashStore, discordDetailsStore, riotChampionStore, + errorHandlerStore, __) => + ApplicationDetailsStore(clashStore, discordDetailsStore, + riotChampionStore, errorHandlerStore)), ], child: Consumer2(builder: (context, ApplicationDetailsStore appStore, ModelFirstTime modelFirstTime, child) { - if (modelFirstTime.visited) { - appStore.loadUserDetails(); + if (modelFirstTime.visited && !appStore.isLoggedIn) { + appStore.refreshDiscordUser(); } - return Observer(builder: (_) { - return const MainApp(); - }); + return const MainApp(); })); } } @@ -270,28 +284,27 @@ class MainContainer extends StatelessWidget { @override Widget build(BuildContext context) { - final appStore = context.read(); + final discordDetailsStore = context.read(); + final errorStore = context.read(); autorun((_) { - if ('' != appStore.error) { + if ('' != errorStore.errorMessage) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( - backgroundColor: Colors.redAccent, content: Text(appStore.error))); - appStore.error = ''; + backgroundColor: Colors.redAccent, + content: Text(errorStore.errorMessage))); + errorStore.errorMessage = ''; } - appStore.discordDetailsStore.discordIdToName.forEach((key, value) { - developer.log("$key => $value"); - }); }); return Consumer( builder: (context, ModelTheme themeNotifier, consChild) { return Scaffold( appBar: AppBar(title: const Text('ClashBot 2.0'), actions: [ - Observer( - builder: (_) => Badge.count( - count: appStore.unreadNotifications.length, - alignment: AlignmentDirectional.bottomStart, - isLabelVisible: appStore.unreadNotifications.isNotEmpty, - child: const ClashBotNotificationsWidget(), - )), + // Observer( + // builder: (_) => Badge.count( + // count: appStore.unreadNotifications.length, + // alignment: AlignmentDirectional.bottomStart, + // isLabelVisible: appStore.unreadNotifications.isNotEmpty, + // child: const ClashBotNotificationsWidget(), + // )), Column( mainAxisSize: MainAxisSize.min, // spacing: 1.0, @@ -322,11 +335,11 @@ class MainContainer extends StatelessWidget { : themeNotifier.isDark = true; }), Observer(builder: (_) { - if (appStore.isLoggedIn) { + if (discordDetailsStore.userHasLoggedIn) { return PopupMenuButton( icon: CircleAvatar( - backgroundImage: NetworkImage(appStore - .discordDetailsStore.discordUser.avatarURL)) + backgroundImage: NetworkImage( + discordDetailsStore.discordUser.avatarURL)) .animate() .shake(duration: const Duration(seconds: 1)), offset: const Offset(0, 50), @@ -354,39 +367,39 @@ class MainContainer extends StatelessWidget { } } -class ClashBotNotificationsWidget extends StatelessWidget { - const ClashBotNotificationsWidget({super.key}); +// class ClashBotNotificationsWidget extends StatelessWidget { +// const ClashBotNotificationsWidget({super.key}); - @override - Widget build(BuildContext context) { - final appStore = context.read(); - return Observer(builder: (_) { - return PopupMenuButton( - icon: const Icon(Icons.add_alert), - tooltip: 'Notifications', - position: PopupMenuPosition.under, - itemBuilder: (BuildContext context) { - return appStore.sortedNotifications.map((notification) { - var discordGuild = appStore - .discordDetailsStore.discordGuildMap[notification.serverId]; - return PopupMenuItem( - value: notification.uuid, - enabled: !notification.read, - padding: const EdgeInsets.all(2.0), - child: ClashBotNotificationBody( - serverName: discordGuild?.name ?? '', - message: notification.message, - causedBy: notification.causedBy, - timestamp: notification.timestamp, - iconUrl: discordGuild?.iconURL ?? '', - )); - }).toList(); - }, - onSelected: (value) => appStore.readNotification(value), - ); - }); - } -} +// @override +// Widget build(BuildContext context) { +// final discordDetailsStore = context.read(); +// return Observer(builder: (_) { +// return PopupMenuButton( +// icon: const Icon(Icons.add_alert), +// tooltip: 'Notifications', +// position: PopupMenuPosition.under, +// itemBuilder: (BuildContext context) { +// return appStore.sortedNotifications.map((notification) { +// var discordGuild = appStore +// .discordDetailsStore.discordGuildMap[notification.serverId]; +// return PopupMenuItem( +// value: notification.uuid, +// enabled: !notification.read, +// padding: const EdgeInsets.all(2.0), +// child: ClashBotNotificationBody( +// serverName: discordGuild?.name ?? '', +// message: notification.message, +// causedBy: notification.causedBy, +// timestamp: notification.timestamp, +// iconUrl: discordGuild?.iconURL ?? '', +// )); +// }).toList(); +// }, +// onSelected: (value) => appStore.readNotification(value), +// ); +// }); +// } +// } class ClashBotNotificationBody extends StatelessWidget { ClashBotNotificationBody({ @@ -474,6 +487,7 @@ class DrawerHeaderContentWidget extends StatelessWidget { @override Widget build(BuildContext context) { final applicationDetailsStore = context.read(); + final discordDetailsStore = context.read(); return Column( children: [ Observer( @@ -482,13 +496,12 @@ class DrawerHeaderContentWidget extends StatelessWidget { return Row(children: [ ListTile( leading: CircleAvatar( - backgroundImage: NetworkImage(applicationDetailsStore - .discordDetailsStore.discordUser.avatarURL), + backgroundImage: + NetworkImage(discordDetailsStore.discordUser.avatarURL), radius: 20, ), title: Text( - applicationDetailsStore - .discordDetailsStore.discordUser.username, + discordDetailsStore.discordUser.username, style: const TextStyle( color: Colors.white, fontSize: 24, @@ -500,7 +513,7 @@ class DrawerHeaderContentWidget extends StatelessWidget { children: [ ListTile( leading: const Icon(Icons.discord), - title: Text(applicationDetailsStore.id, + title: Text(discordDetailsStore.discordUser.id, style: const TextStyle( color: Colors.white, fontSize: 14, diff --git a/lib/models/clashbot_user.dart b/lib/models/clashbot_user.dart index a97ce82..a0dd9a6 100644 --- a/lib/models/clashbot_user.dart +++ b/lib/models/clashbot_user.dart @@ -46,4 +46,15 @@ class ClashBotUser { /// The list of preferred Discord Servers for the player to filter by. List preferredServers; + + ClashBotUser copy() { + return ClashBotUser( + discordId: discordId, + role: role, + champions: champions, + subscriptions: subscriptions, + serverId: serverId, + selectedServers: selectedServers, + preferredServers: preferredServers); + } } diff --git a/lib/models/discord_user.dart b/lib/models/discord_user.dart index 124bff4..a1bee53 100644 --- a/lib/models/discord_user.dart +++ b/lib/models/discord_user.dart @@ -24,7 +24,8 @@ class DiscordUser { late String discriminator; String get userDiscr => "$username#$discriminator"; - String get avatarURL => "${AppGlobalSettings.CND_DISCORD_AVATAR_URL}$id/$avatar.png"; + String get avatarURL => + "${AppGlobalSettings.CND_DISCORD_AVATAR_URL}$id/$avatar.png"; DiscordUser( this.id, @@ -90,4 +91,8 @@ class DiscordUser { avatar.hashCode ^ discriminator.hashCode; } + + DiscordUser copy() { + return DiscordUser(id, username, avatar, discriminator); + } } diff --git a/lib/pages/home/page/home_v2.dart b/lib/pages/home/page/home_v2.dart index 3afb802..4dfca58 100644 --- a/lib/pages/home/page/home_v2.dart +++ b/lib/pages/home/page/home_v2.dart @@ -6,18 +6,20 @@ import 'package:clashbot_flutter/models/discord_guild.dart'; import 'package:clashbot_flutter/pages/home/page/widgets/calendar_widget.dart'; import 'package:clashbot_flutter/pages/home/page/widgets/events_widget.dart'; import 'package:clashbot_flutter/pages/home/page/widgets/server_chip_list.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/stores/v2-stores/clash.store.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; -class Event { +class HomeEvent { final DateTime date; final String title; final String description; final ClashTeam team; - Event({ + HomeEvent({ required this.date, required this.title, required this.description, @@ -87,16 +89,75 @@ class _HomeV2State extends State { @override Widget build(BuildContext context) { + ClashStore clashStore = context.read(); + DiscordDetailsStore discordDetailsStore = + context.read(); return Scaffold( - appBar: AppBar( - title: const Text('Clash Tournaments'), + body: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 500) { + return Row( + children: [ + Flexible( + flex: 1, + child: Flex(direction: Axis.vertical, children: [ + const ServerChipList(), + CalendarWidget( + focusedDay: _focusedDay, + selectedDay: _selectedDay, + clashStore: clashStore, + discordDetailsStore: discordDetailsStore), + Expanded( + child: Card.filled( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.blueGrey + : Colors.blueAccent, + margin: const EdgeInsets.all(16.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: SvgPicture.asset( + 'svgs/ClashBot-HomePage.svg', + semanticsLabel: 'Clash Bot Home Page', + width: 100, + height: 600, + ), + ), + ), + ), + ]), + ), + const Flexible( + flex: 2, + child: EventsListWidget(), + ), + ], + ); + } else { + return Column( + children: [ + ServerChipList(), + CalendarWidget( + focusedDay: _focusedDay, + selectedDay: _selectedDay, + clashStore: clashStore, + discordDetailsStore: discordDetailsStore), + EventsListWidget(), + ], + ); + } + }, ), - body: Column( - children: [ - const ServerChipList(), - CalendarWidget(focusedDay: _focusedDay, selectedDay: _selectedDay), - Expanded(child: EventsListWidget()), - ], + floatingActionButton: Observer( + builder: (_) => clashStore.canCreateTeam + ? FloatingActionButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: const Text('Creating team coming soon.'), + )); + }, + child: const Icon(Icons.add), + ) + : Container(), ), ); } diff --git a/lib/pages/home/page/widgets/calendar_widget.dart b/lib/pages/home/page/widgets/calendar_widget.dart index 0de5e98..9619844 100644 --- a/lib/pages/home/page/widgets/calendar_widget.dart +++ b/lib/pages/home/page/widgets/calendar_widget.dart @@ -1,22 +1,29 @@ -import 'package:clashbot_flutter/models/clash_team.dart'; -import 'package:clashbot_flutter/models/clash_tournament.dart'; import 'package:clashbot_flutter/pages/home/page/home_v2.dart'; -import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/pages/shimmer_loading_page.dart'; import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/stores/v2-stores/clash.store.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; import 'package:table_calendar/table_calendar.dart'; -import 'dart:developer' as developer; +/// This widget requires the following providers: +/// +/// - ClashStore +/// - DiscordDetailsStore class CalendarWidget extends StatefulWidget { final DateTime focusedDay; final DateTime? selectedDay; + // Define all required providers + final ClashStore clashStore; + final DiscordDetailsStore discordDetailsStore; const CalendarWidget( - {super.key, required this.focusedDay, required this.selectedDay}); + {super.key, + required this.focusedDay, + required this.selectedDay, + required this.clashStore, + required this.discordDetailsStore}); @override _CalendarWidgetState createState() => _CalendarWidgetState(); @@ -53,35 +60,82 @@ class _CalendarWidgetState extends State { @override Widget build(BuildContext context) { bool isDarkMode = Theme.of(context).brightness == Brightness.dark; - ApplicationDetailsStore appStore = context.read(); - ClashStore clashStore = context.read(); - return SizedBox( - width: 1000.0, - child: Container( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - CalendarHeader( - focusedDay: _focusedDay, - onMonthChanged: onMonthChanged, - ), - CalendarBody( - focusedDay: _focusedDay, - hoveredDay: _hoveredDay, - isDarkMode: isDarkMode, - appStore: appStore, - clashStore: clashStore, - onDaySelected: onDaySelected, - onHoveredDayChanged: (day) { - setState(() { - _hoveredDay = day; - }); - }, - ), - ], - ), - ), - ); + return Observer( + builder: (_) => widget.clashStore.isRefreshingData + ? SizedBox( + width: 1000.0, child: LoadingCalendar(focusedDay: _focusedDay)) + : SizedBox( + width: 1000.0, + child: Container( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + CalendarHeader( + focusedDay: _focusedDay, + onMonthChanged: onMonthChanged, + ), + CalendarBody( + focusedDay: _focusedDay, + hoveredDay: _hoveredDay, + isDarkMode: isDarkMode, + discordDetailsStore: widget.discordDetailsStore, + clashStore: widget.clashStore, + onDaySelected: onDaySelected, + onHoveredDayChanged: (day) { + setState(() { + _hoveredDay = day; + }); + }, + ), + ], + ), + ), + )); + } +} + +class LoadingCalendar extends StatelessWidget { + const LoadingCalendar({ + super.key, + required DateTime focusedDay, + }) : _focusedDay = focusedDay; + + final DateTime _focusedDay; + + @override + Widget build(BuildContext context) { + return ShimmyShimmer(child: LayoutBuilder(builder: (context, constraints) { + return Column( + children: [ + Center( + child: Container( + width: constraints.maxWidth < 500 ? 200.0 : 400.0, + height: 50.0, + child: Card(), + )), + GridView.builder( + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 7, + childAspectRatio: constraints.maxWidth < 500 ? 1.0 : 1.5, + ), + itemCount: + DateTime(_focusedDay.year, _focusedDay.month + 1, 0).day + + DateTime(_focusedDay.year, _focusedDay.month, 1).weekday - + 1, + itemBuilder: (context, index) { + return Container( + margin: EdgeInsets.all(4.0), + child: const SizedBox( + width: 20.0, + height: 20.0, + child: Card(), + ), + ); + }), + ], + ); + })); } } @@ -125,7 +179,7 @@ class CalendarBody extends StatelessWidget { final DateTime focusedDay; final DateTime? hoveredDay; final bool isDarkMode; - final ApplicationDetailsStore appStore; + final DiscordDetailsStore discordDetailsStore; final ClashStore clashStore; final Function onDaySelected; final ValueChanged onHoveredDayChanged; @@ -135,7 +189,7 @@ class CalendarBody extends StatelessWidget { required this.focusedDay, required this.hoveredDay, required this.isDarkMode, - required this.appStore, + required this.discordDetailsStore, required this.clashStore, required this.onDaySelected, required this.onHoveredDayChanged, @@ -165,7 +219,7 @@ class CalendarBody extends StatelessWidget { DateTime currentDay = DateTime(focusedDay.year, focusedDay.month, day); return Observer(builder: (_) { - List dayEvents = clashStore.events + List dayEvents = clashStore.events .where((event) => isSameDay(event.date, currentDay)) .toList(); return MouseRegion( @@ -222,12 +276,18 @@ class CalendarBody extends StatelessWidget { padding: const EdgeInsets.only(left: 2.0), child: Observer( builder: (_) => CircleAvatar( - backgroundImage: NetworkImage( - appStore - .discordDetailsStore - .discordGuildMap[event - .team.serverId]! - .iconURL), + backgroundImage: discordDetailsStore + .discordGuildMap[ + event.team + .serverId] != + null + ? NetworkImage( + discordDetailsStore + .discordGuildMap[ + event.team + .serverId]! + .iconURL) + : null, radius: 8.0, )), ); diff --git a/lib/pages/home/page/widgets/events_widget.dart b/lib/pages/home/page/widgets/events_widget.dart index 8a20f46..6dfb9d1 100644 --- a/lib/pages/home/page/widgets/events_widget.dart +++ b/lib/pages/home/page/widgets/events_widget.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'dart:developer' as developer; class EventsListWidget extends StatelessWidget { const EventsListWidget({super.key}); @@ -17,7 +16,6 @@ class EventsListWidget extends StatelessWidget { return Observer(builder: (_) { var events = clashStore.tournamentsToTeamsFilteredToADayIfActive.entries.toList(); - developer.log("Events: $events"); return SingleChildScrollView( child: ListView.builder( shrinkWrap: true, diff --git a/lib/pages/home/page/widgets/server_chip_list.dart b/lib/pages/home/page/widgets/server_chip_list.dart index c3e1aaf..8f025df 100644 --- a/lib/pages/home/page/widgets/server_chip_list.dart +++ b/lib/pages/home/page/widgets/server_chip_list.dart @@ -1,5 +1,6 @@ import 'package:clashbot_flutter/models/discord_guild.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/stores/v2-stores/clash.store.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -12,6 +13,8 @@ class ServerChipList extends StatelessWidget { @override Widget build(BuildContext context) { ApplicationDetailsStore appStore = context.read(); + DiscordDetailsStore discordDetailsStore = + context.read(); ClashStore clashStore = context.read(); return Padding( padding: const EdgeInsets.all(8.0), @@ -19,25 +22,39 @@ class ServerChipList extends StatelessWidget { scrollDirection: Axis.horizontal, child: Observer( builder: (_) { + developer.log("Preferred servers: ${appStore.preferredServers}"); return Row( children: appStore.preferredServers.map((serverId) { bool isSelected = clashStore.selectedServers.contains(serverId); - DiscordGuild discordGuild = - appStore.discordDetailsStore.discordGuildMap[serverId]!; return Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: FilterChip( avatar: CircleAvatar( - backgroundImage: NetworkImage(discordGuild.iconURL), + backgroundImage: discordDetailsStore + .discordGuildMap[serverId]?.iconURL != + null + ? NetworkImage(discordDetailsStore + .discordGuildMap[serverId]!.iconURL) + : null, foregroundColor: isSelected ? Colors.white : Colors.black, ), - label: Text(discordGuild.name ?? 'Unknown'), + label: Text( + discordDetailsStore.discordGuildMap[serverId]?.name ?? + 'Unknown'), selected: isSelected, onSelected: (selected) { if (selected) { - clashStore.addSelectedServer(discordGuild.id); + clashStore.addSelectedServer(discordDetailsStore + .discordGuildMap[serverId] != + null + ? discordDetailsStore.discordGuildMap[serverId]!.id + : ""); } else { - clashStore.removeSelectedServer(discordGuild.id); + clashStore.removeSelectedServer(discordDetailsStore + .discordGuildMap[serverId] != + null + ? discordDetailsStore.discordGuildMap[serverId]!.id + : ""); } }, ), diff --git a/lib/pages/home/page/widgets/team_card.dart b/lib/pages/home/page/widgets/team_card.dart index a1c2978..c457cd6 100644 --- a/lib/pages/home/page/widgets/team_card.dart +++ b/lib/pages/home/page/widgets/team_card.dart @@ -1,6 +1,5 @@ import 'package:clash_bot_api/api.dart'; import 'package:clashbot_flutter/models/clash_team.dart'; -import 'package:clashbot_flutter/pages/home/page/home_v2.dart'; import 'package:clashbot_flutter/snackbars/join_team_snackbar.dart'; import 'package:clashbot_flutter/snackbars/remove_team_snackbar.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; @@ -9,6 +8,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:provider/provider.dart'; +import 'dart:developer' as developer; + class TeamCard extends StatelessWidget { TeamCard({ super.key, @@ -28,6 +29,8 @@ class TeamCard extends StatelessWidget { Widget build(BuildContext context) { ApplicationDetailsStore applicationDetailsStore = context.read(); + DiscordDetailsStore discordDetailsStore = + context.read(); return Card( surfaceTintColor: Theme.of(context).brightness == Brightness.dark ? const Color.fromARGB( @@ -35,48 +38,53 @@ class TeamCard extends StatelessWidget { : const Color.fromARGB( 255, 38, 0, 255), // Original color for light mode child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 330), + constraints: const BoxConstraints(maxHeight: 325, maxWidth: 300), child: Padding( padding: const EdgeInsets.all(8.0), - child: Column( + child: Flex( + direction: Axis.vertical, + spacing: 10, children: [ - Row( + Flex( + direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - children: [ - Observer(builder: (_) { - return CircleAvatar( - backgroundImage: NetworkImage(applicationDetailsStore - .discordDetailsStore - .discordGuildMap[team.serverId]! - .iconURL), - ); - }), - SizedBox(width: 5), - Text( - team.name.length > 20 - ? '${team.name.substring(0, 20)}...' - : team.name, - style: Theme.of(context).textTheme.titleLarge, - ), - ], + Observer(builder: (_) { + return CircleAvatar( + backgroundImage: discordDetailsStore + .discordGuildMap[team.serverId]?.iconURL != + null + ? NetworkImage(discordDetailsStore + .discordGuildMap[team.serverId]!.iconURL) + : null, + ); + }), + Text( + team.name.length > 20 + ? '${team.name.substring(0, 20)}...' + : team.name, + style: Theme.of(context).textTheme.titleLarge, ), + const IconButton.filledTonal( + tooltip: 'Coming soon...', + icon: Icon(Icons.fullscreen), + onPressed: null) ], ), - Column( - children: [ - Flex( - direction: Axis.vertical, - spacing: 4, - children: Role.values.map((role) { - return RoleChip( - image: roleToImage[role] ?? 'Unknown', - role: role, - player: team.members[role], - teamId: team.id); - }).toList()) - ], + Expanded( + child: Flex( + direction: Axis.vertical, + spacing: 4, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: Role.values.map((role) { + return RoleChip( + image: roleToImage[role] ?? 'Unknown', + role: role, + player: team.members[role], + teamId: team.id, + isUser: team.members[role]?.id == + applicationDetailsStore.clashBotUser.discordId); + }).toList()), ), ], ), @@ -92,23 +100,84 @@ class RoleChip extends StatelessWidget { required this.image, required this.role, this.player, - required this.teamId}); + required this.teamId, + required this.isUser}); final String image; final Role role; final PlayerDetails? player; final String teamId; + final bool isUser; @override Widget build(BuildContext context) { return player != null - ? RoleFilledWidget( - role: role, player: player, teamId: teamId, image: image) + ? (isUser + ? UsersFilledButton( + role: role, player: player, teamId: teamId, image: image) + : RoleFilledWidget( + role: role, player: player, teamId: teamId, image: image)) : UnfilledRoleWidget( role: role, player: player, teamId: teamId, image: image); } } +class UsersFilledButton extends StatelessWidget { + const UsersFilledButton({ + super.key, + required this.role, + required this.player, + required this.teamId, + required this.image, + }); + + final Role role; + final PlayerDetails? player; + final String teamId; + final String image; + + @override + Widget build(BuildContext context) { + return FilledButton.tonal( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + removeFromTeam(role, player!.id, player!.name, teamId)); + }, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).colorScheme.inversePrimary), + ), + child: Flex( + direction: Axis.horizontal, + spacing: 5, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 1, + child: SizedBox( + width: 15, + height: 15, + child: Image(image: AssetImage(image))), + ), + Expanded( + flex: 3, + child: Center( + child: Text( + player?.name ?? 'Unknown', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + Flexible( + flex: 1, + child: Container( + width: 20, + )) + ]), + ); + } +} + class UnfilledRoleWidget extends StatelessWidget { const UnfilledRoleWidget({ super.key, @@ -136,10 +205,7 @@ class UnfilledRoleWidget extends StatelessWidget { padding: const EdgeInsets.all(4.0), child: Row(spacing: 5, children: [ SizedBox( - width: 15, - height: 15, - child: Image(image: AssetImage(image))), - // Text(role.toString()) + width: 15, height: 15, child: Image(image: AssetImage(image))) ]), )), ); @@ -162,23 +228,30 @@ class RoleFilledWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return FilledButton( - onPressed: () { - ScaffoldMessenger.of(context).showSnackBar( - removeFromTeam(role, player!.id, player!.name, teamId)); - }, - child: SizedBox( - width: 150, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: Row(spacing: 5, children: [ - SizedBox( - width: 15, - height: 15, - child: Image(image: AssetImage(image))), - Text(player?.name ?? 'Unknown') - ]), + return Flex( + direction: Axis.horizontal, + spacing: 5, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 1, + child: SizedBox( + width: 15, height: 15, child: Image(image: AssetImage(image))), + ), + Expanded( + flex: 3, + child: Center( + child: Text( + player?.name ?? 'Unknown', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), ), - )); + Flexible( + flex: 1, + child: Container( + width: 20, + )) + ]); } } diff --git a/lib/pages/intro/step_pages/login_to_discord.dart b/lib/pages/intro/step_pages/login_to_discord.dart index 7877548..45f922b 100644 --- a/lib/pages/intro/step_pages/login_to_discord.dart +++ b/lib/pages/intro/step_pages/login_to_discord.dart @@ -1,5 +1,6 @@ import 'package:animated_text_kit/animated_text_kit.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/styles.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -16,27 +17,29 @@ class LoginToDiscordWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final appStore = context.read(); - var dispose = - when((_) => appStore.discordDetailsStore.detailsLoaded == true, () { + final discordDetailsStore = context.read(); + var dispose = when( + (_) => + discordDetailsStore.userHasLoggedIn == true && + discordDetailsStore.discordGuilds.isNotEmpty, () { callback(true); }); return Observer(builder: (_) { - return !appStore.discordDetailsStore.detailsLoaded + return !discordDetailsStore.userHasLoggedIn ? const AskToLogin() : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( - backgroundImage: NetworkImage( - appStore.discordDetailsStore.discordUser.avatarURL), + backgroundImage: + NetworkImage(discordDetailsStore.discordUser.avatarURL), radius: 50, ), const SizedBox(height: 10), AnimatedTextKit( animatedTexts: [ TypewriterAnimatedText( - 'Welcome ${appStore.discordDetailsStore.discordUser.username}!', + 'Welcome ${discordDetailsStore.discordUser.username}!', textStyle: subHeaderStyle, speed: const Duration(milliseconds: 150), ), @@ -59,14 +62,14 @@ class AskToLogin extends StatelessWidget { @override Widget build(BuildContext context) { - final appStore = context.read(); + final discordDetailsStore = context.read(); return Observer(builder: (_) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("Lets log you in to Discord!", style: subHeaderStyle), const SizedBox(height: 10), - appStore.discordDetailsStore.status == "NOT_LOADED" + !discordDetailsStore.loadingData ? const LoginToDiscord() : ElevatedButton.icon( onPressed: null, @@ -86,10 +89,10 @@ class LoginToDiscord extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, appStore, child) => ElevatedButton( + return Consumer( + builder: (context, discordDetailsStore, child) => ElevatedButton( onPressed: () { - appStore.discordDetailsStore.loadEverything(); + discordDetailsStore.fetchCurrentUserDetails(); }, child: const Text("Login to Discord"))); } diff --git a/lib/pages/intro/step_pages/select_servers.dart b/lib/pages/intro/step_pages/select_servers.dart index 513ebe9..acfe651 100644 --- a/lib/pages/intro/step_pages/select_servers.dart +++ b/lib/pages/intro/step_pages/select_servers.dart @@ -1,7 +1,5 @@ -import 'dart:developer' as developer; - import 'package:clashbot_flutter/models/discord_guild.dart'; -import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/stores/short-lived/selected_server_form.store.dart'; import 'package:clashbot_flutter/styles.dart'; import 'package:flutter/material.dart'; @@ -20,13 +18,11 @@ class ServerFormWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final applicationDetailsStore = context.read(); - applicationDetailsStore.discordDetailsStore.loadEverything(); + final discordDetailsStore = context.read(); return Observer( builder: (_) => ServerFormContent( - guilds: applicationDetailsStore.discordDetailsStore.discordGuilds, - guildMap: - applicationDetailsStore.discordDetailsStore.discordGuildMap, + guilds: discordDetailsStore.discordGuilds, + guildMap: discordDetailsStore.discordGuildMap, selectedServerFormStore: selectedServerFormStore, )); } @@ -104,23 +100,12 @@ class _ServerChoiceChipState extends State { onSelected: !widget.selectedServerFormStore.maxServersReached ? (bool selected) { setState(() { - developer.log("Selected: $selected"); - developer.log( - "Max Servers Reached: ${widget.selectedServerFormStore.maxServersReached}"); - developer.log("widget.guild.id: ${widget.guild.id}"); - developer.log( - "listOfSelectedServers: ${widget.selectedServerFormStore.listOfSelectedServers}"); - developer.log( - "Does it contain? ${widget.selectedServerFormStore.listOfSelectedServers.contains(widget.guild.id)}"); if (selected && !widget.selectedServerFormStore.maxServersReached) { - developer.log("Adding server to list ${widget.guild.id}..."); widget.selectedServerFormStore.addServer(widget.guild.id); } else if (!selected && widget.selectedServerFormStore.listOfSelectedServers .contains(widget.guild.id)) { - developer - .log("Removing server from list ${widget.guild.id}..."); widget.selectedServerFormStore.removeServer(widget.guild.id); } }); diff --git a/lib/pages/intro/welcome_page.dart b/lib/pages/intro/welcome_page.dart index 83759eb..3c9b900 100644 --- a/lib/pages/intro/welcome_page.dart +++ b/lib/pages/intro/welcome_page.dart @@ -7,6 +7,7 @@ import 'package:clashbot_flutter/pages/intro/step_pages/select_servers.dart'; import 'package:clashbot_flutter/pages/intro/step_pages/welcome.dart'; import 'package:clashbot_flutter/pages/intro/step_pages/what_is_clash.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/stores/short-lived/selected_server_form.store.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -131,10 +132,13 @@ class EndWelcomePageSessionButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer2(builder: (context, - ModelFirstTime modelFirstTime, - ApplicationDetailsStore appStore, - consChild) { + return Consumer3( + builder: (context, + ModelFirstTime modelFirstTime, + ApplicationDetailsStore appStore, + DiscordDetailsStore discordDetailsStore, + consChild) { return Observer(builder: (context) { return ElevatedButton.icon( onPressed: !selectedServerFormStore.ableToSubmit @@ -142,13 +146,11 @@ class EndWelcomePageSessionButton extends StatelessWidget { : () { selectedServerFormStore.submittingForm(); appStore - .createUser( - selectedServerFormStore.listOfSelectedServers.first - .toString(), + .createUser(discordDetailsStore.discordUser.id, selectedServerFormStore.listOfSelectedServers) .then((value) { modelFirstTime.visited = true; - context.goNamed('teams'); + context.goNamed('home'); }).catchError((error) { selectedServerFormStore.callFailed = true; selectedServerFormStore.submitting = false; diff --git a/lib/services/clashbot_events_service.dart b/lib/services/clashbot_events_service.dart index 4a6ad32..99fd16b 100644 --- a/lib/services/clashbot_events_service.dart +++ b/lib/services/clashbot_events_service.dart @@ -25,12 +25,8 @@ class ClashBotEventsService { onConnect: (p0) => onConnect(), connectionTimeout: const Duration(seconds: 20), beforeConnect: () async { - developer.log('waiting to connect...'); await Future.delayed(Duration(milliseconds: 200)); - developer.log('connecting...'); }, - onDebugMessage: (p0) => developer.log("Debug $p0"), - onStompError: (p0) => developer.log("Error $p0"), onWebSocketError: (error) => onError(error), ), ); @@ -40,7 +36,6 @@ class ClashBotEventsService { void setupSubscription(String loggedInUserId, String serverId, Function notifyUser, DiscordDetailsStore detailsStore) { - developer.log("Subscribing to $serverId..."); if (null != stompClient && stompClient!.connected) { openConnections.putIfAbsent( serverId, @@ -48,7 +43,6 @@ class ClashBotEventsService { destination: '/topic/server/$serverId', callback: (frame) { Event? result = Event.fromJson(json.decode(frame.body!)); - developer.log("Event recieved $result"); var discordId = result?.causedBy ?? ''; var username = detailsStore.discordIdToName[discordId]; if (isNull(username)) { @@ -57,7 +51,6 @@ class ClashBotEventsService { } switch (result!.teamEvent.eventType) { case EventType.CREATED: - developer.log("${result.teamEvent.eventType} triggered."); String message = ''; if (null != result.teamEvent.team) { // clashPlayerStore.updateClashTeam( @@ -106,7 +99,6 @@ class ClashBotEventsService { DateTime.now())); break; case EventType.JOINED: - developer.log("${result.teamEvent.eventType} triggered."); String message = ''; if (null != result.teamEvent.team) { // clashPlayerStore.updateClashTeam( @@ -131,7 +123,6 @@ class ClashBotEventsService { DateTime.now())); break; case EventType.REMOVED: - developer.log("${result.teamEvent.eventType} triggered."); String message = ''; if (null != result.teamEvent.team) { // clashPlayerStore.updateClashTeam( @@ -156,7 +147,6 @@ class ClashBotEventsService { DateTime.now())); break; case EventType.DELETED: - developer.log("Delete Event triggered."); String message = ''; if (null != result.teamEvent.team) { // clashPlayerStore.removeClashTeams( @@ -180,7 +170,6 @@ class ClashBotEventsService { DateTime.now())); break; default: - developer.log("Unknown event type occurred."); throw Error(message: "Unknown event type occurred."); } })); @@ -191,7 +180,6 @@ class ClashBotEventsService { var unsubscribe = openConnections[serverId]; if (null != unsubscribe) { unsubscribe(unsubscribeHeaders: {}); - developer.log("Unsubscribed from $serverId."); openConnections.remove(serverId); } } diff --git a/lib/services/discord_service_impl.dart b/lib/services/discord_service_impl.dart index 80c3704..de560fd 100644 --- a/lib/services/discord_service_impl.dart +++ b/lib/services/discord_service_impl.dart @@ -21,43 +21,48 @@ class DiscordServiceImpl implements DiscordService { @override Future fetchCurrentUserDetails() { return retry( - () => oAuth2Helper - .get( - AppGlobalSettings.apiEndpointURL + - AppGlobalSettings.API_DISCORD_GET_CURRENT_USER, - headers: baseHeaders) - .then((response) { - if (response.statusCode != 200) { - throw ApiException(response.statusCode, response.reasonPhrase); - } - return response; - }).timeout(const Duration(seconds: 5)), - retryIf: (e) => - e is SocketException || - e is TimeoutException || - (e is ApiException && e.code == HttpStatus.tooManyRequests)).then( - (value) => DiscordUser.fromJson(value.body)); + () => oAuth2Helper + .get( + AppGlobalSettings.apiEndpointURL + + AppGlobalSettings.API_DISCORD_GET_CURRENT_USER, + headers: baseHeaders) + .then((response) { + if (response.statusCode != 200) { + throw ApiException( + response.statusCode, response.reasonPhrase); + } + return response; + }).timeout(const Duration(seconds: 5)), + retryIf: (e) => + e is SocketException || + e is TimeoutException || + (e is ApiException && e.code == HttpStatus.tooManyRequests), + delayFactor: const Duration(seconds: 2)) + .then((value) => DiscordUser.fromJson(value.body)); } @override Future> fetchUserGuilds() { return retry( - () => oAuth2Helper - .get( - AppGlobalSettings.apiEndpointURL + - AppGlobalSettings.API_DISCORD_GET_CURRENT_USER_GUILDS, - headers: baseHeaders) - .then((response) { - if (response.statusCode != 200) { - throw ApiException(response.statusCode, response.reasonPhrase); - } - return response; - }).timeout(const Duration(seconds: 5)), - retryIf: (e) => - e is SocketException || - e is TimeoutException || - (e is ApiException && e.code == HttpStatus.tooManyRequests)).then( - (value) { + () => oAuth2Helper + .get( + AppGlobalSettings.apiEndpointURL + + AppGlobalSettings + .API_DISCORD_GET_CURRENT_USER_GUILDS, + headers: baseHeaders) + .then((response) { + if (response.statusCode != 200) { + throw ApiException( + response.statusCode, response.reasonPhrase); + } + return response; + }).timeout(const Duration(seconds: 5)), + retryIf: (e) => + e is SocketException || + e is TimeoutException || + (e is ApiException && e.code == HttpStatus.tooManyRequests), + delayFactor: const Duration(seconds: 2)) + .then((value) { Iterable l = jsonDecode(value.body); return List.from(l.map((model) { return DiscordGuild.fromMap(model); @@ -73,22 +78,24 @@ class DiscordServiceImpl implements DiscordService { @override Future fetchUserDetails(String discordId) { return retry( - () => oAuth2Helper - .get( - AppGlobalSettings.apiEndpointURL + - AppGlobalSettings.API_DISCORD_GET_USER + - discordId, - headers: baseHeaders) - .then((response) { - if (response.statusCode != 200) { - throw ApiException(response.statusCode, response.reasonPhrase); - } - return response; - }).timeout(const Duration(seconds: 5)), - retryIf: (e) => - e is SocketException || - e is TimeoutException || - (e is ApiException && e.code == HttpStatus.tooManyRequests)).then( - (value) => DiscordUser.fromJson(value.body)); + () => oAuth2Helper + .get( + AppGlobalSettings.apiEndpointURL + + AppGlobalSettings.API_DISCORD_GET_USER + + discordId, + headers: baseHeaders) + .then((response) { + if (response.statusCode != 200) { + throw ApiException( + response.statusCode, response.reasonPhrase); + } + return response; + }).timeout(const Duration(seconds: 5)), + retryIf: (e) => + e is SocketException || + e is TimeoutException || + (e is ApiException && e.code == HttpStatus.tooManyRequests), + delayFactor: const Duration(seconds: 2)) + .then((value) => DiscordUser.fromJson(value.body)); } } diff --git a/lib/stores/application_details.store.dart b/lib/stores/application_details.store.dart index ea05840..e41e3ab 100644 --- a/lib/stores/application_details.store.dart +++ b/lib/stores/application_details.store.dart @@ -1,64 +1,56 @@ -import 'dart:convert'; -import 'dart:math'; - import 'package:clash_bot_api/api.dart'; -import 'package:clashbot_flutter/globals/global_settings.dart'; import 'package:clashbot_flutter/models/clash_notification.dart'; -import 'package:clashbot_flutter/models/clash_tournament.dart'; import 'package:clashbot_flutter/models/clashbot_user.dart'; -import 'package:clashbot_flutter/models/discord_guild.dart'; -import 'package:clashbot_flutter/models/discord_user.dart'; -import 'package:clashbot_flutter/models/model_first_time.dart'; -import 'package:clashbot_flutter/services/clashbot_events_service.dart'; import 'package:clashbot_flutter/services/clashbot_service.dart'; -import 'package:clashbot_flutter/services/discord_service.dart'; -import 'package:clashbot_flutter/services/riot_resources_service.dart'; import 'package:clashbot_flutter/stores/discord_details.store.dart'; import 'package:clashbot_flutter/stores/riot_champion.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/clash.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/error_handler.store.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/widgets.dart'; -import 'package:http/http.dart'; import 'package:mobx/mobx.dart'; -import 'package:oauth2_client/oauth2_helper.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'dart:developer' as developer; part 'application_details.store.g.dart'; -class ApplicationDetailsStore extends _ApplicationDetailsStore - with _$ApplicationDetailsStore { - ApplicationDetailsStore( - DiscordService discordService, - ClashBotService clashBotService, - RiotResourcesService riotResourcesService, - ClashBotEventsService clashBotEventsService) - : super(discordService, clashBotService, riotResourcesService, - clashBotEventsService) { - discordDetailsStore = DiscordDetailsStore(_discordService, this); - riotChampionStore = RiotChampionStore(_riotResourcesService, this); - } -} +class ApplicationDetailsStore = _ApplicationDetailsStore + with _$ApplicationDetailsStore; abstract class _ApplicationDetailsStore with Store { - late DiscordDetailsStore discordDetailsStore; - final DiscordService _discordService; - final ClashBotService _clashBotService; - late RiotChampionStore riotChampionStore; - final RiotResourcesService _riotResourcesService; - final ClashBotEventsService _clashBotEventsService; - _ApplicationDetailsStore(this._discordService, this._clashBotService, - this._riotResourcesService, this._clashBotEventsService) { - reaction((_) => id, (_) { - refreshClashBotUser(); - refreshSelectedServers(); + late DiscordDetailsStore _discordDetailsStore; + late RiotChampionStore _riotChampionStore; + final ClashStore _clashStore; + final ErrorHandlerStore _errorHandlerStore; + _ApplicationDetailsStore(this._clashStore, this._discordDetailsStore, + this._riotChampionStore, this._errorHandlerStore) { + reaction((_) => _discordDetailsStore.discordUser, (_) { + developer.log("reaction: _discordDetailsStore.discordUser"); + if (_discordDetailsStore.discordUser.id != '0') { + developer.log("Refreshing clash bot user"); + _clashStore.refreshClashBotUser(_discordDetailsStore.discordUser.id); + _clashStore.refreshSelectedServers(); + _discordDetailsStore.fetchUserGuilds(); + } + }); + reaction((_) => _clashStore.clashBotUser, (_) { + if (_clashStore.clashBotUser.discordId != '0' && + !_clashStore.refreshingUser) { + _clashStore + .refreshClashTournaments(_clashStore.clashBotUser.discordId!); + _clashStore.refreshClashTeams(_clashStore.clashBotUser.discordId!, + _clashStore.clashBotUser.selectedServers); + } }); } @observable - String id = '0'; + ClashBotUser clashBotUser = ClashBotUser(); @observable - ClashBotUser clashBotUser = ClashBotUser(); + ObservableList notifications = ObservableList(); + + @computed + bool get isLoggedIn => + _discordDetailsStore.userHasLoggedIn && clashBotUser.discordId != '0'; // Create map of String to subscription @computed @@ -69,32 +61,13 @@ abstract class _ApplicationDetailsStore with Store { value: (e) => e as Subscription, )); - @observable - ObservableList preferredServers = ObservableList(); - @computed ObservableList get sortedSelectedServers => ObservableList.of(clashBotUser.selectedServers.sorted()); - @action - Future refreshSelectedServers() async { - preferredServers.clear(); - var userDetails = await _clashBotService.getPlayer(id); - developer.log(userDetails.selectedServers.toString()); - preferredServers = ObservableList.of(userDetails.selectedServers); - developer.log("Preferred: " + preferredServers.toString()); - } - - @action - Future refreshClashBotUser() async { - clashBotUser = await _clashBotService.getPlayer(id); - } - - @observable - String error = ''; - - @observable - ObservableList notifications = ObservableList(); + @computed + ObservableList get preferredServers => + ObservableList.of(_clashStore.selectedServers); @computed List get sortedNotifications => @@ -104,13 +77,9 @@ abstract class _ApplicationDetailsStore with Store { List get unreadNotifications => notifications.where((notification) => !notification.read).toList(); - @computed - bool get isLoggedIn => - discordDetailsStore.detailsLoaded && clashBotUser.discordId != '0'; - @action - void triggerError(String errorMessage) { - error = errorMessage; + void refreshDiscordUser() { + _discordDetailsStore.fetchCurrentUserDetails(); } @action @@ -139,42 +108,15 @@ abstract class _ApplicationDetailsStore with Store { } @action - Future loadUserDetails() async { - try { - await Future.wait([ - discordDetailsStore.loadEverything(), - riotChampionStore.refreshChampionData() - ]); - // _clashBotEventsService.connectClient(() { - // for (var serverId in loggedInClashUser.preferredServers) { - // _clashBotEventsService.setupSubscription( - // id, serverId, notifyUser, loggedInClashUser, discordDetailsStore); - // } - // }, (dynamic error) { - // developer.log("Websocket connection failure.", error: error); - // error = "Failed to connect to Server events."; - // }); - } catch (error) { - developer.log("Failed to load user.", error: error); - this.error = 'Failed to load :(, please try again later.'; - } - } - - @action - Future createUser( - String defaultServerId, List selectedServersToUse) async { + Future createUser(String id, List preferredServers) async { try { - clashBotUser = await _clashBotService.createPlayer( - id, discordDetailsStore.discordUser.username, defaultServerId); - var updatedSelectedServers = await _clashBotService.createSelectedServers( - id, selectedServersToUse); - clashBotUser.selectedServers = updatedSelectedServers; - preferredServers.clear(); - preferredServers.addAll(updatedSelectedServers); - return clashBotUser; + _clashStore.createPlayer(id, _discordDetailsStore.discordUser.username, + preferredServers.first); + _clashStore.createSelectedServers(id, preferredServers); + _clashStore.refreshClashBotUser(id); } on Exception catch (issue) { - error = 'Failed to create new Clash Bot User, please try again.'; - developer.log('Failed to create new Clash Bot User', error: issue); + _errorHandlerStore.errorMessage = + 'Failed to create new Clash Bot User, please try again.'; rethrow; } } diff --git a/lib/stores/discord_details.store.dart b/lib/stores/discord_details.store.dart index f712732..434f75a 100644 --- a/lib/stores/discord_details.store.dart +++ b/lib/stores/discord_details.store.dart @@ -3,24 +3,17 @@ import 'dart:async'; import 'package:clashbot_flutter/models/discord_guild.dart'; import 'package:clashbot_flutter/models/discord_user.dart'; import 'package:clashbot_flutter/services/discord_service.dart'; +import 'package:clashbot_flutter/stores/v2-stores/error_handler.store.dart'; import 'package:mobx/mobx.dart'; -import 'application_details.store.dart'; part 'discord_details.store.g.dart'; class DiscordDetailsStore = _DiscordDetailsStore with _$DiscordDetailsStore; abstract class _DiscordDetailsStore with Store { final DiscordService _discordService; - final ApplicationDetailsStore _applicationDetailsStore; - _DiscordDetailsStore(this._discordService, this._applicationDetailsStore) { - reaction( - (_) => - _applicationDetailsStore.id != '0' || - _applicationDetailsStore.id.isNotEmpty, (_) { - loadEverything(); - }); - } + final ErrorHandlerStore _errorHandlerStore; + _DiscordDetailsStore(this._discordService, this._errorHandlerStore); @observable DiscordUser discordUser = DiscordUser('0', 'Not Logged In', 'N/A', ''); @@ -32,10 +25,13 @@ abstract class _DiscordDetailsStore with Store { ObservableMap discordIdToName = ObservableMap(); @observable - String status = 'NOT_LOADED'; + ObservableList callsInProgress = ObservableList(); + + @computed + bool get loadingData => callsInProgress.isNotEmpty; @computed - bool get detailsLoaded => discordUser.id != '0'; + bool get userHasLoggedIn => discordUser.id != '0'; @computed bool get guildDetailsLoaded => discordGuildMap.isNotEmpty; @@ -45,60 +41,45 @@ abstract class _DiscordDetailsStore with Store { {for (var guild in discordGuilds) guild.id: guild}; @action - Future fetchUserDetails(String discordId) async { + Future fetchUserDetails(String discordId) async { + callsInProgress.add('fetchUserDetails'); var foundUser; try { foundUser = await _discordService.fetchUserDetails(discordId); discordIdToName.putIfAbsent(discordId, () => foundUser.username); - } on Exception catch (error) {} - return foundUser; + } on Exception catch (error) { + _errorHandlerStore.errorMessage = + 'Failed to fetch Discord User details due to ${error.toString()}'; + } + callsInProgress.remove('fetchUserDetails'); } @action Future fetchCurrentUserDetails() async { - status = 'LOADING'; + callsInProgress.add('fetchCurrentUserDetails'); final future = _discordService.fetchCurrentUserDetails(); try { DiscordUser updatedUser = await future; - discordUser = updatedUser; - _applicationDetailsStore.id = discordUser.id; + discordUser = updatedUser.copy(); discordIdToName.putIfAbsent(updatedUser.id, () => updatedUser.username); - status = 'LOADED'; } on Exception catch (error) { - _applicationDetailsStore.error = + _errorHandlerStore.errorMessage = 'Failed to fetch Discord User details due to ${error.toString()}'; - status = 'NOT LOADED'; } + callsInProgress.remove('fetchCurrentUserDetails'); } @action Future fetchUserGuilds() async { - status = 'LOADING'; + callsInProgress.add('fetchUserGuilds'); final future = _discordService.fetchUserGuilds(); try { List guilds = await future; discordGuilds.clear(); discordGuilds.addAll(guilds); - status = 'LOADED'; - } on Exception catch (error) { - _applicationDetailsStore.error = error.toString(); - status = 'NOT LOADED'; - } - } - - @action - Future loadEverything() async { - status = 'LOADING'; - try { - // await _discordService.loginToDiscord(); - await Future.wait([fetchCurrentUserDetails(), fetchUserGuilds()]); - - _applicationDetailsStore.id = discordUser.id; - - status = 'LOADED'; } on Exception catch (error) { - _applicationDetailsStore.error = error.toString(); - status = 'NOT LOADED'; + _errorHandlerStore.errorMessage = error.toString(); } + callsInProgress.remove('fetchUserGuilds'); } } diff --git a/lib/stores/discordoauth2.store.dart b/lib/stores/discordoauth2.store.dart index d3a541a..cce3731 100644 --- a/lib/stores/discordoauth2.store.dart +++ b/lib/stores/discordoauth2.store.dart @@ -19,10 +19,9 @@ part 'discordoauth2.store.g.dart'; class DiscordOAuth2Store = _DiscordOAuth2Store with _$DiscordOAuth2Store; abstract class _DiscordOAuth2Store with Store { - @observable DiscordUser currentUser = new DiscordUser('0', 'Not Logged In', 'N/A', ''); - + @observable List usersGuilds = []; @@ -42,46 +41,50 @@ abstract class _DiscordOAuth2Store with Store { @action void setOAuth2Helper(OAuth2Helper oAuth2Helper) { - developer.log("SettingOAuth2Helper..."); this.oAuth2Helper = oAuth2Helper; } @observable - ObservableFuture discordUserFuture = ObservableFuture.value(new DiscordUser('0', 'Not Logged In', 'N/A', '')); + ObservableFuture discordUserFuture = + ObservableFuture.value(new DiscordUser('0', 'Not Logged In', 'N/A', '')); @observable - ObservableFuture> discordGuildsFuture = ObservableFuture.value([]); + ObservableFuture> discordGuildsFuture = + ObservableFuture.value([]); @computed bool get userDetailsReady => - discordUserFuture != ObservableFuture.value(new DiscordUser('0', 'Not Logged In', 'N/A', '')) && + discordUserFuture != + ObservableFuture.value( + new DiscordUser('0', 'Not Logged In', 'N/A', '')) && discordUserFuture.status == FutureStatus.fulfilled; @action Future fetchCurrentUserDetails() async { final future = oAuth2Helper?.get( - AppGlobalSettings.apiEndpointURL + AppGlobalSettings.API_DISCORD_GET_CURRENT_USER, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - ).then((value) => DiscordUser.fromJson(value.body)); + AppGlobalSettings.apiEndpointURL + + AppGlobalSettings.API_DISCORD_GET_CURRENT_USER, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }).then((value) => DiscordUser.fromJson(value.body)); if (future != null) { discordUserFuture = ObservableFuture(future); return currentUser = await future; } - return ObservableFuture.value(new DiscordUser('0', 'Not Logged In', 'N/A', '')); + return ObservableFuture.value( + new DiscordUser('0', 'Not Logged In', 'N/A', '')); } @action Future> fetchUserGuilds() async { final future = oAuth2Helper?.get( - AppGlobalSettings.apiEndpointURL + AppGlobalSettings.API_DISCORD_GET_CURRENT_USER_GUILDS, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - ).then((value) { + AppGlobalSettings.apiEndpointURL + + AppGlobalSettings.API_DISCORD_GET_CURRENT_USER_GUILDS, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }).then((value) { Iterable l = jsonDecode(value.body); return List.from(l.map((model) { return DiscordGuild.fromMap(model); @@ -121,4 +124,4 @@ abstract class _DiscordOAuth2Store with Store { void setUserOAuth2ReponseURL(String url) { userOAuth2ReponseURL = url; } -} \ No newline at end of file +} diff --git a/lib/stores/riot_champion.store.dart b/lib/stores/riot_champion.store.dart index 0a3c69b..bcef7c3 100644 --- a/lib/stores/riot_champion.store.dart +++ b/lib/stores/riot_champion.store.dart @@ -1,25 +1,16 @@ import 'package:clashbot_flutter/models/lol_champion.dart'; import 'package:clashbot_flutter/services/riot_resources_service.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/error_handler.store.dart'; import 'package:mobx/mobx.dart'; part 'riot_champion.store.g.dart'; -class RiotChampionStore extends _RiotChampionStoreBase - with _$RiotChampionStore { - RiotChampionStore(RiotResourcesService riotResourcesService, - ApplicationDetailsStore applicationDetailsStore) - : super(riotResourcesService, applicationDetailsStore); -} +class RiotChampionStore = _RiotChampionStore with _$RiotChampionStore; -abstract class _RiotChampionStoreBase with Store { - ApplicationDetailsStore _applicationDetailsStore; +abstract class _RiotChampionStore with Store { RiotResourcesService _riotResourcesService; - _RiotChampionStoreBase( - this._riotResourcesService, this._applicationDetailsStore) { - reaction((_) => _applicationDetailsStore.id, (_) { - refreshChampionData(); - }); - } + ErrorHandlerStore _errorHandlerStore; + _RiotChampionStore(this._riotResourcesService, this._errorHandlerStore); @observable LoLChampionsData lChampionsData = LoLChampionsData(); diff --git a/lib/stores/v2-stores/clash.store.dart b/lib/stores/v2-stores/clash.store.dart index dda0800..fd1b961 100644 --- a/lib/stores/v2-stores/clash.store.dart +++ b/lib/stores/v2-stores/clash.store.dart @@ -1,11 +1,11 @@ +import 'package:clash_bot_api/api.dart'; import 'package:clashbot_flutter/models/clash_team.dart'; import 'package:clashbot_flutter/models/clash_tournament.dart'; -import 'package:clashbot_flutter/models/discord_guild.dart'; -import 'package:clashbot_flutter/models/user.dart'; +import 'package:clashbot_flutter/models/clashbot_user.dart'; import 'package:clashbot_flutter/pages/home/page/home_v2.dart'; import 'package:clashbot_flutter/services/clashbot_service.dart'; import 'package:clashbot_flutter/stores/application_details.store.dart'; -import 'package:clashbot_flutter/stores/discord_details.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/error_handler.store.dart'; import 'package:mobx/mobx.dart'; import 'dart:developer' as developer; @@ -17,25 +17,73 @@ class ClashStore = _ClashStore with _$ClashStore; abstract class _ClashStore with Store { final ClashBotService _clashService; - final ApplicationDetailsStore _applicationDetailsStore; + final ErrorHandlerStore _errorhandlerStore; - _ClashStore(this._clashService, this._applicationDetailsStore) { - reaction((_) => _applicationDetailsStore.id, (_) { - refreshClashTournaments(); - }); - reaction((_) => _applicationDetailsStore.preferredServers, (_) { - resetSelectedServers(); - refreshClashTeams(); - }); - } + _ClashStore(this._clashService, this._errorhandlerStore); + + // Constants for network calls + static const String refreshClashBotUserCall = "refreshClashBotUser"; + static const String refreshClashTournamentsCall = "refreshClashTournaments"; + static const String refreshClashTeamsCall = "refreshClashTeams"; + + @observable + ClashBotUser clashBotUser = ClashBotUser(); @observable ObservableList selectedServers = ObservableList(); + @observable + bool filterByDay = false; + + @observable + DateTime filterDate = DateTime.now(); + + @observable + ObservableList tournaments = ObservableList(); + + @observable + ObservableList clashTeams = ObservableList(); + + @observable + bool refreshingUser = false; + + @observable + ObservableList callsInProgress = ObservableList(); + + @computed + bool get isRefreshingData => + callsInProgress.contains(_ClashStore.refreshClashBotUserCall) || + callsInProgress.contains(_ClashStore.refreshClashTournamentsCall) || + callsInProgress.contains(_ClashStore.refreshClashTeamsCall); + + @action + void addCallInProgress(String call) { + callsInProgress.add(call); + } + + @action + void removeCallInProgress(String call) { + callsInProgress.remove(call); + } + @action - void resetSelectedServers() { - selectedServers = - ObservableList.of(_applicationDetailsStore.preferredServers); + void clearCallsInProgress() { + callsInProgress.clear(); + } + + @action + Future refreshClashBotUser(String id) async { + refreshingUser = true; + callsInProgress.add(_ClashStore.refreshClashBotUserCall); + clashBotUser = await _clashService.getPlayer(id); + setSelectedServer(clashBotUser.selectedServers); + callsInProgress.remove(_ClashStore.refreshClashBotUserCall); + refreshingUser = false; + } + + @action + void refreshSelectedServers() { + selectedServers = ObservableList.of(clashBotUser.selectedServers); } @action @@ -45,10 +93,7 @@ abstract class _ClashStore with Store { @action void addSelectedServer(String server) { - if (_applicationDetailsStore.preferredServers.contains(server) && - !selectedServers.contains(server)) { - selectedServers.add(server); - } + selectedServers.add(server); } @action @@ -56,8 +101,8 @@ abstract class _ClashStore with Store { selectedServers.remove(server); } - @observable - bool filterByDay = false; + @computed + bool get canCreateTeam => filterByDay; @action void turnOnDayFilter() { @@ -69,17 +114,11 @@ abstract class _ClashStore with Store { filterByDay = false; } - @observable - DateTime filterDate = DateTime.now(); - @action void setFilterDate(DateTime date) { filterDate = date; } - @observable - ObservableList tournaments = ObservableList(); - @computed Map> get tournamentsByNameAndDay { Map> tournamentsByNameAndDay = {}; @@ -94,9 +133,6 @@ abstract class _ClashStore with Store { return tournamentsByNameAndDay; } - @observable - ObservableList clashTeams = ObservableList(); - @computed Map> get tournamentsToTeams { Map> tournamentsToTeams = {}; @@ -115,25 +151,24 @@ abstract class _ClashStore with Store { get tournamentsToTeamsFilteredToADayIfActive { Map> ogTeams = tournamentsToTeams; if (filterByDay) { - developer.log("Filtering by day"); - ogTeams = ogTeams.map((key, value) { + var teamy = ogTeams.map((key, value) { return MapEntry( key, value.where((team) { return isSameDay(key.startTime, filterDate); }).toList()); }); + return teamy; } - developer.log("Not filtering by day"); return ogTeams; } @computed - ObservableList get events { - List events = []; + ObservableList get events { + List events = []; for (var entry in tournamentsToTeams.entries) { for (var team in entry.value) { - events.add(Event( + events.add(HomeEvent( date: entry.key.startTime, title: '${entry.key.tournamentName} ${entry.key.tournamentDay}', description: "An event", @@ -150,16 +185,46 @@ abstract class _ClashStore with Store { } @action - Future refreshClashTournaments() async { - tournaments = ObservableList.of( - await _clashService.retrieveTournaments(_applicationDetailsStore.id)); + Future refreshClashTournaments(String id) async { + callsInProgress.add(_ClashStore.refreshClashTournamentsCall); + tournaments = + ObservableList.of(await _clashService.retrieveTournaments(id)); + callsInProgress.remove(_ClashStore.refreshClashTournamentsCall); } @action - Future refreshClashTeams() async { - var futureClashTeams = await _clashService.getClashTeams( - _applicationDetailsStore.id, _applicationDetailsStore.preferredServers); + Future refreshClashTeams( + String id, List preferredServers) async { + callsInProgress.add(_ClashStore.refreshClashTeamsCall); + var futureClashTeams = + await _clashService.getClashTeams(id, preferredServers); clashTeams = ObservableList.of(futureClashTeams); + callsInProgress.remove(_ClashStore.refreshClashTeamsCall); + } + + @action + Future createTeam(String id, String name, Role role, + ClashTournament clashTournament, String serverId) async { + await _clashService.createClashTeam( + id, + name, + role, + clashTournament.tournamentName, + clashTournament.tournamentDay, + serverId); + refreshClashTeams(id, selectedServers); + } + + @action + Future createPlayer( + String id, String username, String preferredServers) async { + await _clashService.createPlayer(id, username, preferredServers); + } + + @action + Future createSelectedServers( + String id, List preferredServers) async { + await _clashService.createSelectedServers(id, preferredServers); } DateTime roundUpToEndOfDay(DateTime date) { diff --git a/lib/stores/v2-stores/error_handler.store.dart b/lib/stores/v2-stores/error_handler.store.dart new file mode 100644 index 0000000..9d5caaa --- /dev/null +++ b/lib/stores/v2-stores/error_handler.store.dart @@ -0,0 +1,20 @@ +import 'package:mobx/mobx.dart'; + +part 'error_handler.store.g.dart'; + +class ErrorHandlerStore = _ErrorHandlerStore with _$ErrorHandlerStore; + +abstract class _ErrorHandlerStore with Store { + @observable + String errorMessage = ''; + + @action + void setErrorMessage(String message) { + errorMessage = message; + } + + @action + void clearErrorMessage() { + errorMessage = ''; + } +} diff --git a/lib/storybook_main.dart b/lib/storybook_main.dart new file mode 100644 index 0000000..a18fdbb --- /dev/null +++ b/lib/storybook_main.dart @@ -0,0 +1,340 @@ +import 'package:clash_bot_api/api.dart'; +import 'package:clashbot_flutter/globals/global_settings.dart'; +import 'package:clashbot_flutter/models/clash_team.dart'; +import 'package:clashbot_flutter/models/clash_tournament.dart'; +import 'package:clashbot_flutter/models/clashbot_user.dart'; +import 'package:clashbot_flutter/models/discord_guild.dart'; +import 'package:clashbot_flutter/models/discord_user.dart'; +import 'package:clashbot_flutter/pages/home/page/widgets/calendar_widget.dart'; +import 'package:clashbot_flutter/pages/home/page/widgets/team_card.dart'; +import 'package:clashbot_flutter/services/clashbot_service_impl.dart'; +import 'package:clashbot_flutter/services/discord_service_impl.dart'; +import 'package:clashbot_flutter/services/riot_resources_service_impl.dart'; +import 'package:clashbot_flutter/stores/application_details.store.dart'; +import 'package:clashbot_flutter/stores/discord_details.store.dart'; +import 'package:clashbot_flutter/stores/riot_champion.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/clash.store.dart'; +import 'package:clashbot_flutter/stores/v2-stores/error_handler.store.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:storybook_flutter/storybook_flutter.dart'; + +class MockApplicationDetailsStore extends ApplicationDetailsStore { + MockApplicationDetailsStore( + ClashBotUser mockClashBotUser, + super._clashStore, + super._discordDetailsStore, + super._riotChampionStore, + super._errorHandlerStore); + + @override + ClashBotUser get clashBotUser => ClashBotUser( + discordId: '123456789', + champions: [], + role: Role.TOP, + serverId: 'server1', + selectedServers: [ + 'server1', + ], + preferredServers: ['server1', 'server2'], + ); +} + +class MockDiscordDetailsStore extends DiscordDetailsStore { + MockDiscordDetailsStore(List guilds, DiscordUser user, + super.discordService, super._errorHandlerStore); +} + +class MockClashStore extends ClashStore { + MockClashStore( + ClashBotUser clashBotUser, + List tournaments, + List clashTeams, + super._clashService, + super._errorhandlerStore); +} + +class MockRiotChampionStore extends RiotChampionStore { + MockRiotChampionStore(super._riotResourcesService, super._errorHandlerStore); +} + +void main() { + var loggedInUserId = '123456789'; + var clashUser = ClashBotUser( + discordId: loggedInUserId, + champions: [], + role: Role.TOP, + serverId: 'server1', + selectedServers: [ + 'server1', + ], + preferredServers: ['server1', 'server2'], + ); + var guilds = [ + DiscordGuild('1', 'Mock Guild 1', '123456789', false), + DiscordGuild('2', 'Mock Guild 2', '123456789', false), + ]; + var tournaments = [ + ClashTournament('1', 'Mock Tournament 1', DateTime.now(), DateTime.now()), + ClashTournament('2', 'Mock Tournament 2', DateTime.now(), DateTime.now()), + ]; + var clashTeams = [ + ClashTeam( + '1', + 'Mock Team 1', + tournaments[0].tournamentName, + tournaments[0].tournamentDay, + { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.SUPP: PlayerDetails('5', 'Player 5', []), + }, + '123456789', + DateTime.now(), + ), + ClashTeam( + '2', + 'Mock Team 2', + tournaments[0].tournamentName, + tournaments[0].tournamentDay, + { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.SUPP: PlayerDetails('5', 'Player 5', []), + }, + '123456789', + DateTime.now(), + ), + ]; + var discordUser = DiscordUser( + loggedInUserId, 'mock_username', '123456789', "mock_discriminator"); + runApp(MultiProvider(providers: [ + Provider(create: (_) => ApiClient(basePath: "http://localhost")), + Provider(create: (_) => ErrorHandlerStore()), + ProxyProvider( + update: (_, errorHandlerStore, __) { + return MockDiscordDetailsStore(guilds, discordUser, + DiscordServiceImpl(setupOauth2Helper()), errorHandlerStore); + }), + ProxyProvider( + update: (_, errorHandlerStore, __) => MockRiotChampionStore( + RiotResourceServiceImpl(), errorHandlerStore)), + ProxyProvider2( + update: (_, errorHandlerStore, apiClient, __) => MockClashStore( + clashUser, + tournaments, + clashTeams, + ClashBotServiceImpl( + UserApi(apiClient), + TeamApi(apiClient), + ChampionsApi(apiClient), + SubscriptionApi(apiClient), + TentativeApi(apiClient), + TournamentApi(apiClient)), + errorHandlerStore)), + ProxyProvider4( + update: (_, clashStore, errorHandlerStore, discordDetailsStore, + riotChampionStore, __) => + MockApplicationDetailsStore(clashUser, clashStore, + discordDetailsStore, riotChampionStore, errorHandlerStore)), + ], child: ClashBotStorybookApp())); +} + +class ClashBotStorybookApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Storybook(initialStory: "4Filled", stories: [ + StoryCalendarWidgetWTournaments(context), + StoryCalendarWidgetWTournamentsLoading(context), + StoryTeamCard() + ]); + } + + Story StoryTeamCard() { + return Story( + name: "4Filled", + description: "A card for displaying team information", + builder: (context) { + return TeamCard( + team: ClashTeam( + '1', + 'Mock Team', + 'Tournament 1', + '1', + () { + switch (context.knobs + .text(label: '# of missing roles', initial: '0')) { + case '0': + return { + Role.TOP: PlayerDetails('123456789', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.BOT: PlayerDetails('5', 'Player 4', []), + Role.SUPP: PlayerDetails('5', 'Player 5', []), + }; + case '1': + return { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.BOT: PlayerDetails('5', 'Player 4', []), + Role.SUPP: null, + }; + case '2': + return { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.BOT: null, + Role.SUPP: null, + }; + case '3': + return { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.JG: null, + Role.BOT: null, + Role.SUPP: null, + }; + case '4': + return { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: null, + Role.MID: null, + Role.BOT: null, + Role.SUPP: null, + }; + case '5': + return { + Role.TOP: null, + Role.JG: null, + Role.MID: null, + Role.BOT: null, + Role.SUPP: null, + }; + default: + return { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.SUPP: PlayerDetails('5', 'Player 5', []), + }; + } + }(), + '123456789', + DateTime.now(), + )); + }); + } + + Story StoryCalendarWidgetWTournamentsLoading(BuildContext context) { + DiscordDetailsStore discordDetailsStore = + context.read(); + ClashStore clashStoreW5Tournies = new MockClashStore( + context.read().clashBotUser, + [ + ClashTournament('1', 'Mock Tournament 1', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('2', 'Mock Tournament 2', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('3', 'Mock Tournament 3', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('4', 'Mock Tournament 4', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('5', 'Mock Tournament 5', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ], + [ + ClashTeam( + '1', + 'Mock Team 1', + 'Mock Tournament 1', + '1', + { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.SUPP: PlayerDetails('5', 'Player 5', []), + }, + '123456789', + DateTime.now(), + ) + ], + new ClashBotServiceImpl( + new UserApi(context.read()), + new TeamApi(context.read()), + new ChampionsApi(context.read()), + new SubscriptionApi(context.read()), + new TentativeApi(context.read()), + new TournamentApi(context.read())), + context.read()); + clashStoreW5Tournies.addCallInProgress('getTournaments'); + return Story( + name: "Widgets/Calendar/loading", + description: "ClashBot's main calendar widget loading", + builder: (context) { + return CalendarWidget( + focusedDay: DateTime.now(), + selectedDay: DateTime.now(), + clashStore: clashStoreW5Tournies, + discordDetailsStore: discordDetailsStore); + }, + ); + } +} + +Story StoryCalendarWidgetWTournaments(BuildContext context) { + DiscordDetailsStore discordDetailsStore = context.read(); + ClashStore clashStoreW5Tournies = new MockClashStore( + context.read().clashBotUser, + [ + ClashTournament('1', 'Mock Tournament 1', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('2', 'Mock Tournament 2', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('3', 'Mock Tournament 3', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('4', 'Mock Tournament 4', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ClashTournament('5', 'Mock Tournament 5', DateTime.now(), + DateTime.now().add(Duration(days: 1))), + ], + [ + ClashTeam( + '1', + 'Mock Team 1', + 'Mock Tournament 1', + '1', + { + Role.TOP: PlayerDetails('1', 'Player 1', []), + Role.JG: PlayerDetails('2', 'Player 2', []), + Role.MID: PlayerDetails('3', 'Player 3', []), + Role.SUPP: PlayerDetails('5', 'Player 5', []), + }, + '460520499680641035', + DateTime.now(), + ) + ], + new ClashBotServiceImpl( + new UserApi(context.read()), + new TeamApi(context.read()), + new ChampionsApi(context.read()), + new SubscriptionApi(context.read()), + new TentativeApi(context.read()), + new TournamentApi(context.read())), + context.read()); + return Story( + name: "Widgets/Calendar/filled", + description: "ClashBot's main calendar widget filled", + builder: (context) { + return CalendarWidget( + focusedDay: DateTime.now(), + selectedDay: DateTime.now(), + clashStore: clashStoreW5Tournies, + discordDetailsStore: discordDetailsStore); + }, + ); +} diff --git a/pubspec.lock b/pubspec.lock index ccafc07..9f0472b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1020,10 +1020,10 @@ packages: dependency: "direct main" description: name: storybook_flutter - sha256: "0ef97a82741e12734af3c104ef6c913af1ed808756214d23bff2bd115e547887" + sha256: "68f07d2caf16bd34e9cfbc479d60298a6568abfac552ea1998ef724344c6cc13" url: "https://pub.dev" source: hosted - version: "0.12.0" + version: "0.14.1" stream_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fac8901..b85a4e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,7 +49,7 @@ dependencies: clash_bot_api: path: clash-bot-api animated_text_kit: ^4.2.2 - storybook_flutter: ^0.12.0 + storybook_flutter: ^0.14.1 validators: ^3.0.0 retry: ^3.1.1 stomp_dart_client: ^0.4.4 @@ -100,11 +100,12 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - assets/markdown/privacy-policy.md - - assets/images/TopIcon.webp - - assets/images/BotIcon.webp - - assets/images/MidIcon.webp - - assets/images/JGIcon.webp - - assets/images/SuppIcon.webp + - images/TopIcon.webp + - images/BotIcon.webp + - images/MidIcon.webp + - images/JGIcon.webp + - images/SuppIcon.webp + - svgs/ClashBot-HomePage.svg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware diff --git a/svgs/ClashBot-HomePage.svg b/svgs/ClashBot-HomePage.svg new file mode 100644 index 0000000..9401f14 --- /dev/null +++ b/svgs/ClashBot-HomePage.svg @@ -0,0 +1 @@ +AAA0RGp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAADQeanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46dXVpZDpmZDIwODgzMS01YTgxLTRiM2MtYmEwNi00NTA5ODZlZTNmZGIAAAABtGp1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAANdqdW1iAAAAJmp1bWRjYm9yABEAEIAAAKoAOJtxA2MycGEuYWN0aW9ucwAAAACpY2JvcqFnYWN0aW9uc4GjZmFjdGlvbmtjMnBhLmVkaXRlZG1zb2Z0d2FyZUFnZW50bUFkb2JlIEZpcmVmbHlxZGlnaXRhbFNvdXJjZVR5cGV4U2h0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvY29tcG9zaXRlV2l0aFRyYWluZWRBbGdvcml0aG1pY01lZGlhAAAArGp1bWIAAAAoanVtZGNib3IAEQAQgAAAqgA4m3EDYzJwYS5oYXNoLmRhdGEAAAAAfGNib3KlamV4Y2x1c2lvbnOBomVzdGFydBjhZmxlbmd0aBlFsGRuYW1lbmp1bWJmIG1hbmlmZXN0Y2FsZ2ZzaGEyNTZkaGFzaFggyMc6F5cV/0m3EYFBFXDxvH3RiPC0jhJDBYou9EyQDNVjcGFkSQAAAAAAAAAAAAAAAgtqdW1iAAAAJGp1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0AAAAB32Nib3KoaGRjOnRpdGxlb0dlbmVyYXRlZCBJbWFnZWlkYzpmb3JtYXRtaW1hZ2Uvc3ZnK3htbGppbnN0YW5jZUlEeCx4bXA6aWlkOjMwZTQ4ZGE0LTI1MTItNDE2MS05M2MyLWYzMDkzYzEwMmE0Mm9jbGFpbV9nZW5lcmF0b3J4NkFkb2JlX0lsbHVzdHJhdG9yLzI5LjMgYWRvYmVfYzJwYS8wLjcuNiBjMnBhLXJzLzAuMjUuMnRjbGFpbV9nZW5lcmF0b3JfaW5mb4G/ZG5hbWVxQWRvYmUgSWxsdXN0cmF0b3JndmVyc2lvbmQyOS4z/2lzaWduYXR1cmV4GXNlbGYjanVtYmY9YzJwYS5zaWduYXR1cmVqYXNzZXJ0aW9uc4KiY3VybHg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DjBP8h1w5RmXOBD+lo3aQ5vLnY3BhZFkTgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZZAQAB6Q1bOZcwlG4DkrvjTNn6MMTewhS/ZENkuuX7VcrlcJpAC7SH54T9snJDFEbmcvALvSTpFMkJPI6bEjUZuoNu1tDLs1jiHkDB3WIbOXbLwa/40lo5FPahItmjCLhOg5XqOOn9KDHIX6KLZYfRYSoe3bBzhdzxUG6K7UyZgORuZAKovTExDDsFnxOwnULVK0BXmEg0shZAG+bj/V7O7xV1JFxpjWmL+jOzn+N9YcpELLj2/4RUcSM3I21nNSj+ZILW49PY/KDoruC387ya7bwH5LD6dvA+/GKFLLq962ztzPZZgfbyFhGS4x34Dgn0Yq0t88pKUr+dOgO56/rRjdf7 \ No newline at end of file diff --git a/test/widget/clash_notification_test.dart b/test/widget/clash_notification_test.dart deleted file mode 100644 index 76c212b..0000000 --- a/test/widget/clash_notification_test.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:clashbot_flutter/globals/color_schemes.dart'; -import 'package:clashbot_flutter/main.dart'; -import 'package:clashbot_flutter/models/clash_notification.dart'; -import 'package:clashbot_flutter/models/discord_guild.dart'; -import 'package:clashbot_flutter/stores/application_details.store.dart'; -import 'package:clashbot_flutter/stores/discord_details.store.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:golden_toolkit/golden_toolkit.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:provider/provider.dart'; -import 'clash_notification_test.mocks.dart'; - -@GenerateMocks([ApplicationDetailsStore, DiscordDetailsStore]) -void main() { - testGoldens('Clash Bot Notifications - Golden', (WidgetTester tester) async { - final mockAppStore = MockApplicationDetailsStore(); - final mockDetailsStore = MockDiscordDetailsStore(); - when(mockDetailsStore.discordGuildMap) - .thenReturn(Map.of({"serverId": DiscordGuild("serverId", "Goon Squad", "", false)})); - when(mockAppStore.discordDetailsStore) - .thenReturn(mockDetailsStore); - if (const bool.fromEnvironment("LoadFont")) { - await loadAppFonts(); - } - final builder = GoldenBuilder.column() - ..addScenario( - 'Simple', - SizedBox( - width: 500, - height: 500, - child: MaterialApp( - themeMode: ThemeMode.dark, - debugShowCheckedModeBanner: false, - darkTheme: ThemeData( - brightness: Brightness.dark, - colorScheme: darkColorScheme, - useMaterial3: true, - snackBarTheme: SnackBarThemeData( - behavior: SnackBarBehavior.floating, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - )), - home: const Scaffold( - body: ClashBotNotificationsWidget(), - ), - ), - )); - await tester.pumpWidgetBuilder(Builder( - builder: (context) { - when(mockAppStore.sortedNotifications) - .thenAnswer((_) => [ - ClashNotification("uuid", false, "Has created a Team New Team", "Roidrage", "serverId", DateTime.now()), - ClashNotification("uuid", true, "message", "Some one", "serverId", DateTime.now()), - ClashNotification("uuid", false, "message", "Shiragaku", "serverId", DateTime.now()) - ]); - return MultiProvider( - providers: [ - Provider(create: (_) => mockAppStore), - ], - child: builder.build()); - } - )); - await tester.tap(find.byType(Icon)); - await screenMatchesGolden(tester, 'clash_bot_notification'); - }); -} diff --git a/test/widget/clash_notification_test.mocks.dart b/test/widget/clash_notification_test.mocks.dart deleted file mode 100644 index 9a6769d..0000000 --- a/test/widget/clash_notification_test.mocks.dart +++ /dev/null @@ -1,552 +0,0 @@ -// Mocks generated by Mockito 5.4.5 from annotations -// in clashbot_flutter/test/widget/clash_notification_test.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i11; - -import 'package:clash_bot_api/api.dart' as _i10; -import 'package:clashbot_flutter/models/clash_notification.dart' as _i9; -import 'package:clashbot_flutter/models/clashbot_user.dart' as _i4; -import 'package:clashbot_flutter/models/discord_guild.dart' as _i12; -import 'package:clashbot_flutter/models/discord_user.dart' as _i6; -import 'package:clashbot_flutter/stores/application_details.store.dart' as _i7; -import 'package:clashbot_flutter/stores/discord_details.store.dart' as _i2; -import 'package:clashbot_flutter/stores/riot_champion.store.dart' as _i3; -import 'package:mobx/mobx.dart' as _i5; -import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i8; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: deprecated_member_use -// ignore_for_file: deprecated_member_use_from_same_package -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: must_be_immutable -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeDiscordDetailsStore_0 extends _i1.SmartFake - implements _i2.DiscordDetailsStore { - _FakeDiscordDetailsStore_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRiotChampionStore_1 extends _i1.SmartFake - implements _i3.RiotChampionStore { - _FakeRiotChampionStore_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeClashBotUser_2 extends _i1.SmartFake implements _i4.ClashBotUser { - _FakeClashBotUser_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeObservableList_3 extends _i1.SmartFake - implements _i5.ObservableList { - _FakeObservableList_3( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeObservableMap_4 extends _i1.SmartFake - implements _i5.ObservableMap { - _FakeObservableMap_4( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeReactiveContext_5 extends _i1.SmartFake - implements _i5.ReactiveContext { - _FakeReactiveContext_5( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeDiscordUser_6 extends _i1.SmartFake implements _i6.DiscordUser { - _FakeDiscordUser_6( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [ApplicationDetailsStore]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockApplicationDetailsStore extends _i1.Mock - implements _i7.ApplicationDetailsStore { - MockApplicationDetailsStore() { - _i1.throwOnMissingStub(this); - } - - @override - _i2.DiscordDetailsStore get discordDetailsStore => (super.noSuchMethod( - Invocation.getter(#discordDetailsStore), - returnValue: _FakeDiscordDetailsStore_0( - this, - Invocation.getter(#discordDetailsStore), - ), - ) as _i2.DiscordDetailsStore); - - @override - set discordDetailsStore(_i2.DiscordDetailsStore? _discordDetailsStore) => - super.noSuchMethod( - Invocation.setter( - #discordDetailsStore, - _discordDetailsStore, - ), - returnValueForMissingStub: null, - ); - - @override - _i3.RiotChampionStore get riotChampionStore => (super.noSuchMethod( - Invocation.getter(#riotChampionStore), - returnValue: _FakeRiotChampionStore_1( - this, - Invocation.getter(#riotChampionStore), - ), - ) as _i3.RiotChampionStore); - - @override - set riotChampionStore(_i3.RiotChampionStore? _riotChampionStore) => - super.noSuchMethod( - Invocation.setter( - #riotChampionStore, - _riotChampionStore, - ), - returnValueForMissingStub: null, - ); - - @override - String get id => (super.noSuchMethod( - Invocation.getter(#id), - returnValue: _i8.dummyValue( - this, - Invocation.getter(#id), - ), - ) as String); - - @override - set id(String? value) => super.noSuchMethod( - Invocation.setter( - #id, - value, - ), - returnValueForMissingStub: null, - ); - - @override - _i4.ClashBotUser get clashBotUser => (super.noSuchMethod( - Invocation.getter(#clashBotUser), - returnValue: _FakeClashBotUser_2( - this, - Invocation.getter(#clashBotUser), - ), - ) as _i4.ClashBotUser); - - @override - set clashBotUser(_i4.ClashBotUser? value) => super.noSuchMethod( - Invocation.setter( - #clashBotUser, - value, - ), - returnValueForMissingStub: null, - ); - - @override - _i5.ObservableList get preferredServers => (super.noSuchMethod( - Invocation.getter(#preferredServers), - returnValue: _FakeObservableList_3( - this, - Invocation.getter(#preferredServers), - ), - ) as _i5.ObservableList); - - @override - set preferredServers(_i5.ObservableList? value) => super.noSuchMethod( - Invocation.setter( - #preferredServers, - value, - ), - returnValueForMissingStub: null, - ); - - @override - String get error => (super.noSuchMethod( - Invocation.getter(#error), - returnValue: _i8.dummyValue( - this, - Invocation.getter(#error), - ), - ) as String); - - @override - set error(String? value) => super.noSuchMethod( - Invocation.setter( - #error, - value, - ), - returnValueForMissingStub: null, - ); - - @override - _i5.ObservableList<_i9.ClashNotification> get notifications => - (super.noSuchMethod( - Invocation.getter(#notifications), - returnValue: _FakeObservableList_3<_i9.ClashNotification>( - this, - Invocation.getter(#notifications), - ), - ) as _i5.ObservableList<_i9.ClashNotification>); - - @override - set notifications(_i5.ObservableList<_i9.ClashNotification>? value) => - super.noSuchMethod( - Invocation.setter( - #notifications, - value, - ), - returnValueForMissingStub: null, - ); - - @override - _i5.ObservableMap get subscription => - (super.noSuchMethod( - Invocation.getter(#subscription), - returnValue: _FakeObservableMap_4( - this, - Invocation.getter(#subscription), - ), - ) as _i5.ObservableMap); - - @override - _i5.ObservableList get sortedSelectedServers => (super.noSuchMethod( - Invocation.getter(#sortedSelectedServers), - returnValue: _FakeObservableList_3( - this, - Invocation.getter(#sortedSelectedServers), - ), - ) as _i5.ObservableList); - - @override - List<_i9.ClashNotification> get sortedNotifications => (super.noSuchMethod( - Invocation.getter(#sortedNotifications), - returnValue: <_i9.ClashNotification>[], - ) as List<_i9.ClashNotification>); - - @override - List<_i9.ClashNotification> get unreadNotifications => (super.noSuchMethod( - Invocation.getter(#unreadNotifications), - returnValue: <_i9.ClashNotification>[], - ) as List<_i9.ClashNotification>); - - @override - bool get isLoggedIn => (super.noSuchMethod( - Invocation.getter(#isLoggedIn), - returnValue: false, - ) as bool); - - @override - _i5.ReactiveContext get context => (super.noSuchMethod( - Invocation.getter(#context), - returnValue: _FakeReactiveContext_5( - this, - Invocation.getter(#context), - ), - ) as _i5.ReactiveContext); - - @override - _i11.Future refreshSelectedServers() => (super.noSuchMethod( - Invocation.method( - #refreshSelectedServers, - [], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - - @override - _i11.Future refreshClashBotUser() => (super.noSuchMethod( - Invocation.method( - #refreshClashBotUser, - [], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - - @override - void triggerError(String? errorMessage) => super.noSuchMethod( - Invocation.method( - #triggerError, - [errorMessage], - ), - returnValueForMissingStub: null, - ); - - @override - void notifyUser(_i9.ClashNotification? clashNotification) => - super.noSuchMethod( - Invocation.method( - #notifyUser, - [clashNotification], - ), - returnValueForMissingStub: null, - ); - - @override - void readNotification(String? uuid) => super.noSuchMethod( - Invocation.method( - #readNotification, - [uuid], - ), - returnValueForMissingStub: null, - ); - - @override - void unsubscribeFromServer(String? serverId) => super.noSuchMethod( - Invocation.method( - #unsubscribeFromServer, - [serverId], - ), - returnValueForMissingStub: null, - ); - - @override - void subscribeToServer(String? serverId) => super.noSuchMethod( - Invocation.method( - #subscribeToServer, - [serverId], - ), - returnValueForMissingStub: null, - ); - - @override - _i11.Future loadUserDetails() => (super.noSuchMethod( - Invocation.method( - #loadUserDetails, - [], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - - @override - _i11.Future<_i4.ClashBotUser> createUser( - String? defaultServerId, - List? selectedServersToUse, - ) => - (super.noSuchMethod( - Invocation.method( - #createUser, - [ - defaultServerId, - selectedServersToUse, - ], - ), - returnValue: _i11.Future<_i4.ClashBotUser>.value(_FakeClashBotUser_2( - this, - Invocation.method( - #createUser, - [ - defaultServerId, - selectedServersToUse, - ], - ), - )), - ) as _i11.Future<_i4.ClashBotUser>); -} - -/// A class which mocks [DiscordDetailsStore]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockDiscordDetailsStore extends _i1.Mock - implements _i2.DiscordDetailsStore { - MockDiscordDetailsStore() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.DiscordUser get discordUser => (super.noSuchMethod( - Invocation.getter(#discordUser), - returnValue: _FakeDiscordUser_6( - this, - Invocation.getter(#discordUser), - ), - ) as _i6.DiscordUser); - - @override - set discordUser(_i6.DiscordUser? value) => super.noSuchMethod( - Invocation.setter( - #discordUser, - value, - ), - returnValueForMissingStub: null, - ); - - @override - _i5.ObservableList<_i12.DiscordGuild> get discordGuilds => - (super.noSuchMethod( - Invocation.getter(#discordGuilds), - returnValue: _FakeObservableList_3<_i12.DiscordGuild>( - this, - Invocation.getter(#discordGuilds), - ), - ) as _i5.ObservableList<_i12.DiscordGuild>); - - @override - set discordGuilds(_i5.ObservableList<_i12.DiscordGuild>? value) => - super.noSuchMethod( - Invocation.setter( - #discordGuilds, - value, - ), - returnValueForMissingStub: null, - ); - - @override - _i5.ObservableMap get discordIdToName => (super.noSuchMethod( - Invocation.getter(#discordIdToName), - returnValue: _FakeObservableMap_4( - this, - Invocation.getter(#discordIdToName), - ), - ) as _i5.ObservableMap); - - @override - set discordIdToName(_i5.ObservableMap? value) => - super.noSuchMethod( - Invocation.setter( - #discordIdToName, - value, - ), - returnValueForMissingStub: null, - ); - - @override - String get status => (super.noSuchMethod( - Invocation.getter(#status), - returnValue: _i8.dummyValue( - this, - Invocation.getter(#status), - ), - ) as String); - - @override - set status(String? value) => super.noSuchMethod( - Invocation.setter( - #status, - value, - ), - returnValueForMissingStub: null, - ); - - @override - bool get detailsLoaded => (super.noSuchMethod( - Invocation.getter(#detailsLoaded), - returnValue: false, - ) as bool); - - @override - bool get guildDetailsLoaded => (super.noSuchMethod( - Invocation.getter(#guildDetailsLoaded), - returnValue: false, - ) as bool); - - @override - Map get discordGuildMap => (super.noSuchMethod( - Invocation.getter(#discordGuildMap), - returnValue: {}, - ) as Map); - - @override - _i5.ReactiveContext get context => (super.noSuchMethod( - Invocation.getter(#context), - returnValue: _FakeReactiveContext_5( - this, - Invocation.getter(#context), - ), - ) as _i5.ReactiveContext); - - @override - _i11.Future<_i6.DiscordUser> fetchUserDetails(String? discordId) => - (super.noSuchMethod( - Invocation.method( - #fetchUserDetails, - [discordId], - ), - returnValue: _i11.Future<_i6.DiscordUser>.value(_FakeDiscordUser_6( - this, - Invocation.method( - #fetchUserDetails, - [discordId], - ), - )), - ) as _i11.Future<_i6.DiscordUser>); - - @override - _i11.Future fetchCurrentUserDetails() => (super.noSuchMethod( - Invocation.method( - #fetchCurrentUserDetails, - [], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - - @override - _i11.Future fetchUserGuilds() => (super.noSuchMethod( - Invocation.method( - #fetchUserGuilds, - [], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - - @override - _i11.Future loadEverything() => (super.noSuchMethod( - Invocation.method( - #loadEverything, - [], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); -} diff --git a/web/favicon.png b/web/favicon.png deleted file mode 100644 index 8aaa46a..0000000 Binary files a/web/favicon.png and /dev/null differ diff --git a/web/favicon.svg b/web/favicon.svg new file mode 100644 index 0000000..c3ba17b --- /dev/null +++ b/web/favicon.svg @@ -0,0 +1 @@ +AAA0RGp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAADQeanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46dXVpZDowYmQ4MWU5MS1lMTQyLTRiOTQtOWQ3OC1jYTYwMTUwNTk3Y2EAAAABtGp1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAANdqdW1iAAAAJmp1bWRjYm9yABEAEIAAAKoAOJtxA2MycGEuYWN0aW9ucwAAAACpY2JvcqFnYWN0aW9uc4GjZmFjdGlvbmtjMnBhLmVkaXRlZG1zb2Z0d2FyZUFnZW50bUFkb2JlIEZpcmVmbHlxZGlnaXRhbFNvdXJjZVR5cGV4U2h0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvY29tcG9zaXRlV2l0aFRyYWluZWRBbGdvcml0aG1pY01lZGlhAAAArGp1bWIAAAAoanVtZGNib3IAEQAQgAAAqgA4m3EDYzJwYS5oYXNoLmRhdGEAAAAAfGNib3KlamV4Y2x1c2lvbnOBomVzdGFydBjeZmxlbmd0aBlFsGRuYW1lbmp1bWJmIG1hbmlmZXN0Y2FsZ2ZzaGEyNTZkaGFzaFggL3p6VK7pKJexihJ+mGO2n46jbaiXEdyZut8JbyX9tt9jcGFkSQAAAAAAAAAAAAAAAgtqdW1iAAAAJGp1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0AAAAB32Nib3KoaGRjOnRpdGxlb0dlbmVyYXRlZCBJbWFnZWlkYzpmb3JtYXRtaW1hZ2Uvc3ZnK3htbGppbnN0YW5jZUlEeCx4bXA6aWlkOjE1Y2M0NjE3LWE4MTEtNDc0OC1hZDc1LTAxY2QyNWVlMTQwNW9jbGFpbV9nZW5lcmF0b3J4NkFkb2JlX0lsbHVzdHJhdG9yLzI5LjMgYWRvYmVfYzJwYS8wLjcuNiBjMnBhLXJzLzAuMjUuMnRjbGFpbV9nZW5lcmF0b3JfaW5mb4G/ZG5hbWVxQWRvYmUgSWxsdXN0cmF0b3JndmVyc2lvbmQyOS4z/2lzaWduYXR1cmV4GXNlbGYjanVtYmY9YzJwYS5zaWduYXR1cmVqYXNzZXJ0aW9uc4KiY3VybHgnc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5hY3Rpb25zZGhhc2hYIEppwb3/qN5BMHi+JO3M+DE6wdFklTRWcaANawazN9SvomN1cmx4KXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaGFzaC5kYXRhZGhhc2hYILir9gp9fYUtiVzV+WXUWfdq+GeFwgC3piESfAooqIbKY2FsZ2ZzaGEyNTYAADAQanVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAAC/gY2JvctKEWQzvogE4JBghglkGPTCCBjkwggQhoAMCAQICEBWN/yesI9K4JUtOYzceHZ4wDQYJKoZIhvcNAQELBQAwdTELMAkGA1UEBhMCVVMxIzAhBgNVBAoTGkFkb2JlIFN5c3RlbXMgSW5jb3Jwb3JhdGVkMR0wGwYDVQQLExRBZG9iZSBUcnVzdCBTZXJ2aWNlczEiMCAGA1UEAxMZQWRvYmUgUHJvZHVjdCBTZXJ2aWNlcyBHMzAeFw0yNDEwMTUwMDAwMDBaFw0yNTEwMTUyMzU5NTlaMIGrMRMwEQYDVQQDDApBZG9iZSBDMlBBMSgwJgYDVQQLDB9Db250ZW50IEF1dGhlbnRpY2l0eSBJbml0aWF0aXZlMRMwEQYDVQQKDApBZG9iZSBJbmMuMREwDwYDVQQHDAhTYW4gSm9zZTETMBEGA1UECAwKQ2FsaWZvcm5pYTELMAkGA1UEBhMCVVMxIDAeBgkqhkiG9w0BCQEWEWNhaS1vcHNAYWRvYmUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwxDBgdB9PXLpMXPw5oNgYkFWDPP1aSfth9TZPINOtOQlhp1v4h+hMxZWFjkZ3RJRuoSBrsSSYBaEfiDMKisi/sOxuFHKBV//l1rv3SrjrixANXIlqjGdIYydaMaFa/5ovFz/m4+SUz0ccYzqw+vSAzuRySGnpgm8Gmj+SEJcL/GIHzqU9bUy3NsizY2oY28yj32rbkOqeADSM51OqIJKloEBFFexzMunzpU+K2sLqheoR8FJMaR0fGXa/gqRzhkiBFhwUhLPS9s6+TCnz09UZMlXbdG/iFKj3UPFUDjqh0wtFgcz24DrUlaWeiltKHouymBHuirzvmOG0VtSPepxOQIDAQABo4IBjDCCAYgwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwHgYDVR0lBBcwFQYJKoZIhvcvAQEMBggrBgEFBQcDBDCBjgYDVR0gBIGGMIGDMIGABgkqhkiG9y8BAgMwczBxBggrBgEFBQcCAjBlDGNZb3UgYXJlIG5vdCBwZXJtaXR0ZWQgdG8gdXNlIHRoaXMgTGljZW5zZSBDZXJ0aWZpY2F0ZSBleGNlcHQgYXMgcGVybWl0dGVkIGJ5IHRoZSBsaWNlbnNlIGFncmVlbWVudC4wXQYDVR0fBFYwVDBSoFCgToZMaHR0cDovL3BraS1jcmwuc3ltYXV0aC5jb20vY2FfN2E1YzNhMGM3MzExNzQwNmFkZDE5MzEyYmMxYmMyM2YvTGF0ZXN0Q1JMLmNybDA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9wa2ktb2NzcC5zeW1hdXRoLmNvbTAfBgNVHSMEGDAWgBRXKXoyTcz+5DVOwB8kc85zU6vfajANBgkqhkiG9w0BAQsFAAOCAgEAqrl6FLQ+c9LYaf0igyTgErNL9XmmieiT3ohKFevJ3BN7kWkZD1znbVw3XnX5tgQaKq+AiSCldNxYEKqU+Rq9Lr26GGglBSA0s/Ds2kw+2LlnTmojAHCH3CvVRbhGHHrashmnfgwmF1TSkaWk7NxEhbt9wQoiEMkLSQeM4S4Cu+176FzEdy+zzkDkRWqeSOQO/qG2WRto/vIq30ECf6v6FtazJI1CWIqhBK5oJioSNbLsCVJhar3Uca9D11ujeZW4k2jPCsnzriTmFqVftd3k2SXUQg7wQcQcXfKBItWZt8ztn0IYoZzJa1x9l9dvXKAJvNsDoz4uDerS8z9rzLsEQvzL1yPbTp4+/6mTTAWsoaDtmkZgm3X+sffPX3XzMFmfzIHiYfUja5nKK2bs4P71tit/1U/FD2xdlpzZSupRqCMGz+UTeXI8IrN5ZR/+F8rSsmRAStjnggz/wDucwcDlJbY4/RKq3BrAi4LamLMIfwo/dbL55TDlIOd7HCfmSgabc1WO0Kji3LW/VZnP9VG8WiUG+WqtN1OQIAZmFUOWdXQGLag+I1OaZ1BXJDNsJiXcg2TGNBSEPo44Akfn9MzFGKyB5UurMN4NH0qlamhrhKiV0+b73Fjx330P/frxBmzx5NpAQhNKksx+F4z1S8Ay1o2TBIkkeAFbDzy8f/FxuytZBqUwggahMIIEiaADAgECAhAMqLZUe4nm0gaJdc2Lm4niMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVTMSMwIQYDVQQKExpBZG9iZSBTeXN0ZW1zIEluY29ycG9yYXRlZDEdMBsGA1UECxMUQWRvYmUgVHJ1c3QgU2VydmljZXMxGTAXBgNVBAMTEEFkb2JlIFJvb3QgQ0EgRzIwHhcNMTYxMTI5MDAwMDAwWhcNNDExMTI4MjM1OTU5WjB1MQswCQYDVQQGEwJVUzEjMCEGA1UEChMaQWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxHTAbBgNVBAsTFEFkb2JlIFRydXN0IFNlcnZpY2VzMSIwIAYDVQQDExlBZG9iZSBQcm9kdWN0IFNlcnZpY2VzIEczMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtx8uvb0Js1xIbP4Mg65sAepReCWkgD6Jp7GyiGTa9ol2gfn5HfOV/HiYjZiOz+TuHFU+DXNad86xEqgVeGVMlvIHGe/EHcKBxvEDXdlTXB5zIEkfl0/SGn7J6vTX8MNybfSi95eQDUOZ9fjCaq+PBFjS5ZfeNmzi/yR+MsA0jKKoWarSRCFFFBpUFQWfAgLyXOyxOnXQOQudjxNj6Wu0X0IB13+IH11WcKcWEWXM4j4jh6hLy29Cd3EoVG3oxcVenMF/EMgD2tXjx4NUbTNB1/g9+MR6Nw5Mhp5k/g3atNExAxhtugC+T3SDShSEJfs2quiiRUHtX3RhOcK1s1OJgT5s2s9xGy5/uxVpcAIaK2KiDJXW3xxN8nXPmk1NSVu/mxtfapr4TvSJbhrU7UA3qhQY9n4On2sbH1X1Tw+7LTek8KCA5ZDghOERPiIp/Jt893qov1bE5rJkagcVg0Wqjh89NhCaBA8VyRt3ovlGyCKdNV2UL3bn5vdFsTk7qqmp9makz1/SuVXYxIf6L6+8RXOatXWaPkmucuLE1TPOeP7S1N5JToFCs80l2D2EtxoQXGCR48K/cTUR5zV/fQ+hdIOzoo0nFn77Y8Ydd2k7/x9BE78pmoeMnw6VXYfXCuWEgj6p7jpbLoxQMoWMCVzlg72WVNhJFlSw4aD8fc6ezeECAwEAAaOCATQwggEwMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5hZG9iZS5jb20vYWRvYmVyb290ZzIuY3JsMA4GA1UdDwEB/wQEAwIBBjAUBgNVHSUEDTALBgkqhkiG9y8BAQcwVwYDVR0gBFAwTjBMBgkqhkiG9y8BAgMwPzA9BggrBgEFBQcCARYxaHR0cHM6Ly93d3cuYWRvYmUuY29tL21pc2MvcGtpL3Byb2Rfc3ZjZV9jcHMuaHRtbDAkBgNVHREEHTAbpBkwFzEVMBMGA1UEAxMMU1lNQy00MDk2LTMzMB0GA1UdDgQWBBRXKXoyTcz+5DVOwB8kc85zU6vfajAfBgNVHSMEGDAWgBSmHOFtVCRMqI9Icr9uqYzV5Owx1DANBgkqhkiG9w0BAQsFAAOCAgEAcc7lB4ym3C3cyOA7ZV4AkoGV65UgJK+faThdyXzxuNqlTQBlOyXBGFyevlm33BsGO1mDJfozuyLyT2+7IVxWFvW5yYMV+5S1NeChMXIZnCzWNXnuiIQSdmPD82TEVCkneQpFET4NDwSxo8/ykfw6Hx8fhuKz0wjhjkWMXmK3dNZXIuYVcbynHLyJOzA+vWU3sH2T0jPtFp7FN39GZne4YG0aVMlnHhtHhxaXVCiv2RVoR4w1QtvKHQpzfPObR53Cl74iLStGVFKPwCLYRSpYRF7J6vVS/XxW4LzvN2b6VEKOcvJmN3LhpxFRl3YYzW+dwnwtbuHW6WJlmjffbLm1MxLFGlG95aCz31X8wzqYNsvb9+5AXcv8Ll69tLXmO1OtsY/3wILNUEp4VLZTE3wqm3n8hMnClZiiKyZCS7L4E0mClbx+BRSMH3eVo6jgve41/fK3FQM4QCNIkpGs7FjjLy+ptC+JyyWqcfvORrFV/GOgB5hD+G5ghJcIpeigD/lHsCRYsOa5sFdqREhwIWLmSWtNwfLZdJ3dkCc7yRpm3gal6qRfTkYpxTNxxKyvKbkaJDoxR9vtWrC3iNrQd9VvxC3TXtuzoHbqumeqgcAqefWF9u6snQ4Q9FkXzeuJArNuSvPIhgBjVtggH0w0vm/lmCQYiC/Y12GeCxfgYlL33buiZnNpZ1RzdKFpdHN0VG9rZW5zgaFjdmFsWQ44MIIONDADAgEAMIIOKwYJKoZIhvcNAQcCoIIOHDCCDhgCAQMxDzANBglghkgBZQMEAgEFADCBggYLKoZIhvcNAQkQAQSgcwRxMG8CAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCBANAP1YTQwUd8zmEdsxkuSU2r0TBAQ4QAlQacwTynLRgIQf8rWX6uAnopb4ZLt3JswgRgPMjAyNTAzMjIxNzIzMjBaAgkAuyJETcWLg4SgggvAMIIFCjCCAvKgAwIBAgIQDAsvx3p4z7rtZVZUwZokAzANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MTEyMDAwMDAwMFoXDTM2MDIxOTIzNTk1OVowWzELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MTkwNwYDVQQDEzBFQ0MyNTZfU0hBMjU2X1RpbWVzdGFtcF9SZXNwb25kZXJfQWRvYmVfTk9WXzIwMjQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATQ/B6F+IGYpiqQQLxOfqkUmeTmSRWZzxSCtwM82siW/SbXazktRyEWmwIVs+8PJjhV9C4fUJ23IGRxsfzJM8leo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBTs0RxvfOegTeQxP4cACNSt1H02uzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCi7MEg8nkvLauLI7cAj21DgnMErh0mntCt4c4tsW9yJQZdZv1n8E1dueayb6IiZ8mYambImrTeuVKwGqUSITZTiVhtFRP3zRD9DpFk+Ex4P010IStH/eD1lgK6bVfaY0gvzcIRQP3CwIzqBZAE81c5QINjPs81cvJLOFKd/cX7zOhoQvrziNDy15UNT5fuURe2fioANQsRNYOmVXAdg2TK7OktYD+EH/D8gWr7nHQhRJMuD54GNjiZnNPnYXz6F3j7Bu2aVlirvQGAAsrW27Lqhg9ksW5+aL+g9/lyqRoMrWLSy4KDQaztPB+PKskecO1R7dbJbw7UBFVl+GbGaUc4x6HvVLNNL5hHjiLrf9A4zxe52e9ZqpSU7kDu7dsRXvm+uLLMXjHFSx/j0stIcxnQHwOL4A5RRuUH1Xw1wz2CiGvIHNcoYrkqfkb6TsKJU7ntDqNKFKZ349sBErTdXwVoId4zS/cV/A5rO1Kw5aNO/zUTCtQkbcMg88XGWqImaYmIHhIvHaE1nRdPWCa0QhrxvioeP45p4/zqd/JrVbNsoEuEBSRIPB3+ViLaoFlimZRUePzwKYvyTrd6g72mVtF4Prbbvy1kqCUmsZDMFqn33DR0N8Qzqkzir3bufNyI5k95Rq3NXcbfNYDx9qZ8gjCu4NHtSxAdJKswzb9gi6jyFTCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPwxggG3MIIBswIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAwLL8d6eM+67WVWVMGaJAMwDQYJYIZIAWUDBAIBBQCggdEwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAzMjIxNzIzMjBaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFPcTAlRkG5zO0lEtYsW/nymEjPz4MC8GCSqGSIb3DQEJBDEiBCALLrsWoeL8jeq/97z0uGspCKNBZhCvmJrOtFaEzD5R9zA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCC5eiZoHRjpuXxjPvhIOWRVdZeW2lBDRCyPjM3lJ+AAqTAKBggqhkjOPQQDAgRGMEQCIB23PJVi5taToQSk+T+U4Rdab3ikHfijer3RsvJCfJ35AiA81uUg8qNYGaY/QAXftAVRzwMWOJ27eHPacZcGJYG6aGNwYWRZE4UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZZAQAQd/4/FathWFaeuS84tLSFPBOfUiRr/QOH8c0zur1CVuiOMDs6x4ih4SSk4SeM1YGLCOVAIpaqoWdm3uwr7H8LQ6pvY0ilfq5qN1la88BVdUxHQ0fzCNsz+Uy3kAAub1u+U5HjvG27DOR3D0HZQg2jM0mmTvojCZF2zZhNRET6x9Z2kSHrMK/ZI67a2wG7Q6efgoGz3ypCcQx5IhSKDF8qIdIewZCTVugE8Dm22zwGRS+SSRtnl/0PXiIP5XIKXy64Pnxs3qEopGVnfp6uc9/JCAkLwsoO+MT9ik50BZ9IZkBmr9T7ugddWglaHrhnf+1d4GydRJ/YOmn62DLkURvP \ No newline at end of file diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png index b749bfe..f828f30 100644 Binary files a/web/icons/Icon-192.png and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png index 88cfd48..7edf04f 100644 Binary files a/web/icons/Icon-512.png and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d7..0000000 Binary files a/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c566..0000000 Binary files a/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/web/index.html b/web/index.html index 7b3698e..cad678d 100644 --- a/web/index.html +++ b/web/index.html @@ -26,10 +26,10 @@ - + - + Clash Bot 2.0 diff --git a/web/manifest.json b/web/manifest.json index 51d517d..35fdb72 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -14,23 +14,23 @@ ], "icons": [ { - "src": "icons/Icon-192.png", + "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "icons/Icon-512.png", + "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" }, { - "src": "icons/Icon-maskable-192.png", + "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { - "src": "icons/Icon-maskable-512.png", + "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable"