From 2dfec45d65ee0a44166271a51f552b5f4d420adc Mon Sep 17 00:00:00 2001 From: Warakorn Sitthirit Date: Mon, 23 Sep 2024 12:07:10 +0700 Subject: [PATCH] Reapply "feat: ad settings and ad query (#229)" This reverts commit 6763f385eb90cb267b5b77eb5b7ebbf78b4cf0d9. --- android/app/build.gradle | 2 +- lib/core/route/app_route.dart | 4 + lib/core/route/app_router.dart | 18 ++++ lib/core/widget/ad_widget.dart | 102 ++++++++++++++++++ lib/core/widget/advertiser_row_widget.dart | 68 ++++++++++++ .../screen/ads/ads_list_screen.dart | 63 +++++++++++ .../screen/ads/ads_settings_screen.dart | 83 ++++++++++++++ .../screen/dashboard/dashboard_screen.dart | 34 ++++-- .../token_exchange/token_exchange_screen.dart | 3 +- 9 files changed, 364 insertions(+), 13 deletions(-) create mode 100644 lib/core/widget/ad_widget.dart create mode 100644 lib/core/widget/advertiser_row_widget.dart create mode 100644 lib/presentation/screen/ads/ads_list_screen.dart create mode 100644 lib/presentation/screen/ads/ads_settings_screen.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 6496964..4566d75 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply plugin: 'com.google.gms.google-services' android { - compileSdkVersion 33 + compileSdkVersion 34 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/lib/core/route/app_route.dart b/lib/core/route/app_route.dart index 2b352a6..ccafe6a 100644 --- a/lib/core/route/app_route.dart +++ b/lib/core/route/app_route.dart @@ -181,5 +181,9 @@ class AppRoute { static const communitNotificationSerttings = 'communitNotificationSerttings'; static const communitNotificationSerttingsRoute = '/communitNotificationSerttings'; + static const adsList = 'adsList'; + static const adsListRoute = '/adsList'; + static const adsSettings = 'adsSettings'; + static const adsSettingsRoute = '/adsSettings'; } diff --git a/lib/core/route/app_router.dart b/lib/core/route/app_router.dart index d4590f3..4ae9411 100644 --- a/lib/core/route/app_router.dart +++ b/lib/core/route/app_router.dart @@ -3,6 +3,8 @@ import 'dart:developer'; import 'package:amity_sdk/amity_sdk.dart'; import 'package:flutter_social_sample_app/core/preferences/preference_interface_impl.dart'; import 'package:flutter_social_sample_app/core/route/app_route.dart'; +import 'package:flutter_social_sample_app/presentation/screen/ads/ads_list_screen.dart'; +import 'package:flutter_social_sample_app/presentation/screen/ads/ads_settings_screen.dart'; import 'package:flutter_social_sample_app/presentation/screen/channel_create/channel_create_screen.dart'; import 'package:flutter_social_sample_app/presentation/screen/channel_list/channel_list_screen.dart'; import 'package:flutter_social_sample_app/presentation/screen/channel_profile/channel_profile_screen.dart'; @@ -497,6 +499,22 @@ class AppRouter { return const UserBlockedListScreen(); }, ), + + GoRoute( + name: AppRoute.adsList, + path: AppRoute.adsListRoute, + builder: (context, state) { + return const AdsListScreen(); + }, + ), + + GoRoute( + name: AppRoute.adsSettings, + path: AppRoute.adsSettingsRoute, + builder: (context, state) { + return const AdsSettingsScreen(); + }, + ), ], redirect: (context, state) async { if (state.location == AppRoute.loginRoute) { diff --git a/lib/core/widget/ad_widget.dart b/lib/core/widget/ad_widget.dart new file mode 100644 index 0000000..6539529 --- /dev/null +++ b/lib/core/widget/ad_widget.dart @@ -0,0 +1,102 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_social_sample_app/core/widget/advertiser_row_widget.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class AdWidget extends StatelessWidget { + final AmityAd amityAd; + + const AdWidget({Key? key, required this.amityAd}) : super(key: key); + + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( + padding: const EdgeInsets.all(16), + child: Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + AdvertiserRowWidget( + advertiserId: amityAd.advertiserId ?? '', + companyName: amityAd.advertiser?.companyName, + advertiserAvatar: amityAd.advertiser?.avatar), + Text( + 'Ad id - ${amityAd.adId}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'Target - ${amityAd.adTarget}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'Placements - ${amityAd.placements}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'Start at - ${amityAd.startAt ?? 'N/A'}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'End at - ${amityAd.endAt ?? 'forever'}', + style: themeData.textTheme.bodySmall, + ), + Text( + 'Name - ${amityAd.name}', + style: themeData.textTheme.titleMedium, + ), + Text( + 'Headline - ${amityAd.headline ?? 'N/A - Headline is not available'}', + style: themeData.textTheme.titleLarge, + ), + Text( + 'Description - ${amityAd.description ?? 'N/A - Description is not available'}', + style: themeData.textTheme.bodyMedium, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.all(4), + color: Colors.blueGrey.withOpacity(.25), + width: 130, + height: 130, + child: (amityAd.image1_1 != null) + ? Image.network( + amityAd.image1_1!.getUrl(AmityImageSize.MEDIUM), + fit: BoxFit.cover, + ) + : const Align( + alignment: Alignment.center, + child: Text( + "1:1 Ad image is not available", + textAlign: TextAlign.center, + ), + ), + ), + Container( + margin: const EdgeInsets.all(4), + color: Colors.blueGrey.withOpacity(.25), + width: 130, + height: 130, + child: (amityAd.image9_16 != null) + ? Image.network( + amityAd.image9_16!.getUrl(AmityImageSize.MEDIUM), + fit: BoxFit.fitWidth, + ) + : const Align( + alignment: Alignment.center, + child: Text( + "9:16 Ad image is not available", + textAlign: TextAlign.center, + ), + )) + ]), + ], + ), + ), + ); + } +} diff --git a/lib/core/widget/advertiser_row_widget.dart b/lib/core/widget/advertiser_row_widget.dart new file mode 100644 index 0000000..451f75d --- /dev/null +++ b/lib/core/widget/advertiser_row_widget.dart @@ -0,0 +1,68 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; + +class AdvertiserRowWidget extends StatelessWidget { + const AdvertiserRowWidget( + {Key? key, + required this.advertiserId, + this.companyName, + this.advertiserAvatar}) + : super(key: key); + + final String advertiserId; + final AmityImage? advertiserAvatar; + final String? companyName; + + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Colors.white, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey.withOpacity( + .3, + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + child: advertiserAvatar != null + ? Image.network( + advertiserAvatar!.getUrl(AmityImageSize.MEDIUM), + fit: BoxFit.fill, + ) + : Image.asset('assets/user_placeholder.png'), + ), + const SizedBox(width: 18), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + companyName ?? 'company name is not available', + style: themeData.textTheme.titleLarge, + ), + Text( + 'advertiserId: $advertiserId', + style: themeData.textTheme.bodySmall, + ), + ], + ), + ), + const Spacer() + ], + ), + ); + } +} diff --git a/lib/presentation/screen/ads/ads_list_screen.dart b/lib/presentation/screen/ads/ads_list_screen.dart new file mode 100644 index 0000000..175fc7a --- /dev/null +++ b/lib/presentation/screen/ads/ads_list_screen.dart @@ -0,0 +1,63 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_social_sample_app/core/widget/ad_widget.dart'; + +class AdsListScreen extends StatefulWidget { + const AdsListScreen({Key? key}) : super(key: key); + @override + State createState() => _AdsListScreenState(); +} + +final ScrollController scrollController = ScrollController(); + +class _AdsListScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Ads List 👩‍🚀'), + ), + body: Container( + width: double.maxFinite, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.black26, + width: 1, + ), + ), + child: Column( + children: [ + Expanded( + child: FutureBuilder( + future: AmityCoreClient.newAdRepository().getNetworkAds(), + builder: (context, snapshot) { + if (snapshot.hasData && + (snapshot.data?.ads?.isNotEmpty ?? false)) { + List networkAds = snapshot.data!.ads!; + return ListView.builder( + controller: ScrollController(), + physics: const AlwaysScrollableScrollPhysics(), + itemCount: networkAds.length, + itemBuilder: (context, index) { + final networkAd = networkAds[index]; + return AdWidget(key: UniqueKey(), amityAd: networkAd); + }); + } else if (snapshot.hasError) { + return Center( + child: Text(snapshot.error.toString()), + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/screen/ads/ads_settings_screen.dart b/lib/presentation/screen/ads/ads_settings_screen.dart new file mode 100644 index 0000000..9b62d17 --- /dev/null +++ b/lib/presentation/screen/ads/ads_settings_screen.dart @@ -0,0 +1,83 @@ +import 'package:amity_sdk/amity_sdk.dart'; +import 'package:flutter/material.dart'; + +class AdsSettingsScreen extends StatefulWidget { + const AdsSettingsScreen({Key? key}) : super(key: key); + @override + State createState() => _AdsSettingsScreenState(); +} + +final ScrollController scrollController = ScrollController(); + +class _AdsSettingsScreenState extends State { + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + return Scaffold( + appBar: AppBar( + title: const Text('Ads Settings ⚙️'), + ), + body: Container( + width: double.maxFinite, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.black26, + width: 1, + ), + ), + child: Column( + children: [ + Expanded( + child: FutureBuilder( + future: AmityCoreClient.newAdRepository().getNetworkAds(), + builder: (context, snapshot) { + if (snapshot.hasData && (snapshot.data?.settings != null)) { + AmityAdsSettings settings = snapshot.data!.settings!; + return Expanded( + child: Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Is enabled - ${settings.isEnabled ?? 'N/A'}', + style: themeData.textTheme.bodyMedium, + ), + Text( + 'Max active - ${settings.maxActiveAds ?? 'N/A'}', + style: themeData.textTheme.bodyMedium, + ), + Text( + 'Feed frequency- ${settings.getFrequency()?.getFeedAdFrequency().type ?? 'N/A'} ${settings.getFrequency()?.getFeedAdFrequency().value ?? 'N/A'}', + style: themeData.textTheme.bodyMedium, + ), + Text( + 'Story frequency- ${settings.getFrequency()?.getStoryAdFrequency().type ?? 'N/A'} ${settings.getFrequency()?.getStoryAdFrequency().value ?? 'N/A'}', + style: themeData.textTheme.bodyMedium, + ), + Text( + 'Comment frequency- ${settings.getFrequency()?.getCommentAdFrequency().type ?? 'N/A'} ${settings.getFrequency()?.getCommentAdFrequency().value ?? 'N/A'}', + style: themeData.textTheme.bodyMedium, + ), + ], + ), + )); + } else if (snapshot.hasError) { + return Center( + child: Text(snapshot.error.toString()), + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/screen/dashboard/dashboard_screen.dart b/lib/presentation/screen/dashboard/dashboard_screen.dart index f1b1f63..dcccd88 100644 --- a/lib/presentation/screen/dashboard/dashboard_screen.dart +++ b/lib/presentation/screen/dashboard/dashboard_screen.dart @@ -8,6 +8,7 @@ import 'package:flutter_social_sample_app/core/route/app_route.dart'; import 'package:flutter_social_sample_app/core/widget/dialog/edit_text_dialog.dart'; import 'package:flutter_social_sample_app/core/widget/dialog/error_dialog.dart'; import 'package:flutter_social_sample_app/core/widget/dialog/positive_dialog.dart'; +import 'package:get/utils.dart'; import 'package:go_router/go_router.dart'; class DashboardScreen extends StatefulWidget { @@ -245,7 +246,6 @@ class _DashboardScreenState extends State { child: const Text('Get Story by Id'), ), - const SizedBox(height: 20), TextButton( onPressed: () { @@ -266,7 +266,9 @@ class _DashboardScreenState extends State { const SizedBox(height: 20), TextButton( onPressed: () { - GoRouter.of(context).goNamed(AppRoute.globalStoryTargets,); + GoRouter.of(context).goNamed( + AppRoute.globalStoryTargets, + ); }, child: const Text('Get Global Story Targets'), ), @@ -280,38 +282,33 @@ class _DashboardScreenState extends State { buttonText: "Get", hintText: 'community/commnuityId1,community/commnuityId2', onPress: (value) { - GoRouter.of(context).goNamed(AppRoute.storytargetsByTargets, + GoRouter.of(context).goNamed( + AppRoute.storytargetsByTargets, params: {'targets': value}); }, ); }, child: const Text('Get Story targets By Targets'), ), - const SizedBox(height: 20), TextButton( onPressed: () { - GoRouter.of(context) - .goNamed(AppRoute.validateURL); + GoRouter.of(context).goNamed(AppRoute.validateURL); }, child: const Text('Validate URL'), ), const SizedBox(height: 20), - TextButton( onPressed: () { - GoRouter.of(context) - .goNamed(AppRoute.validateText); + GoRouter.of(context).goNamed(AppRoute.validateText); }, child: const Text('Validate Text'), ), const SizedBox(height: 20), - - TextButton( onPressed: () { GoRouter.of(context) @@ -394,6 +391,21 @@ class _DashboardScreenState extends State { }, child: const Text('Gloabl User Search'), ), + const SizedBox(height: 20), + TextButton( + onPressed: () { + GoRouter.of(context).pushNamed(AppRoute.adsSettings); + }, + child: const Text('Ads Settings'), + ), + const SizedBox(height: 20), + TextButton( + onPressed: () { + GoRouter.of(context).pushNamed(AppRoute.adsList); + }, + child: const Text('Ads List'), + ), + const SizedBox(height: 20), const SizedBox(height: 20), TextButton( onPressed: () { diff --git a/lib/presentation/screen/token_exchange/token_exchange_screen.dart b/lib/presentation/screen/token_exchange/token_exchange_screen.dart index 6120f05..af372a6 100644 --- a/lib/presentation/screen/token_exchange/token_exchange_screen.dart +++ b/lib/presentation/screen/token_exchange/token_exchange_screen.dart @@ -73,7 +73,8 @@ class _TokenExchangeScreenState extends State { }); }, style: TextButton.styleFrom( - foregroundColor: Colors.white, backgroundColor: Colors.blue, + foregroundColor: Colors.white, + backgroundColor: Colors.blue, padding: const EdgeInsets.all(12), ), child: Container(