diff --git a/app/lib/pages/apps/add_app.dart b/app/lib/pages/apps/add_app.dart index c9a72b0c1..dcdf0482d 100644 --- a/app/lib/pages/apps/add_app.dart +++ b/app/lib/pages/apps/add_app.dart @@ -252,7 +252,7 @@ class _AddAppPageState extends State { shape: const CircleBorder(), ), SizedBox( - width: MediaQuery.of(context).size.width * 0.80, + width: MediaQuery.of(context).size.width * 0.7, child: const Text("Make my app public"), ), ], @@ -268,7 +268,7 @@ class _AddAppPageState extends State { shape: const CircleBorder(), ), SizedBox( - width: MediaQuery.of(context).size.width * 0.80, + width: MediaQuery.of(context).size.width * 0.78, child: const Text( "By submitting this app, I agree to the Omi AI Terms of Service and Privacy Policy"), ), diff --git a/app/lib/pages/apps/app_detail/app_detail.dart b/app/lib/pages/apps/app_detail/app_detail.dart index 5d57eda81..77a5044c8 100644 --- a/app/lib/pages/apps/app_detail/app_detail.dart +++ b/app/lib/pages/apps/app_detail/app_detail.dart @@ -137,7 +137,6 @@ class _AppDetailPageState extends State { bool hasSetupInstructions = isIntegration && app.externalIntegration?.setupInstructionsFilePath.isNotEmpty == true; bool hasAuthSteps = isIntegration && app.externalIntegration?.authSteps.isNotEmpty == true; int stepsCount = app.externalIntegration?.authSteps.length ?? 0; - return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.primary, @@ -817,7 +816,9 @@ class RecentReviewsSection extends StatelessWidget { constraints: BoxConstraints( maxHeight: reviews.any((e) => e.response.isNotEmpty) ? MediaQuery.of(context).size.height * 0.24 - : MediaQuery.of(context).size.height * 0.138, + : (MediaQuery.of(context).size.height < 680 + ? MediaQuery.of(context).size.height * 0.2 + : MediaQuery.of(context).size.height * 0.138), ), child: ListView.separated( scrollDirection: Axis.horizontal, diff --git a/app/lib/pages/apps/app_detail/reviews_list_page.dart b/app/lib/pages/apps/app_detail/reviews_list_page.dart index 13d94dcdd..d24dd1528 100644 --- a/app/lib/pages/apps/app_detail/reviews_list_page.dart +++ b/app/lib/pages/apps/app_detail/reviews_list_page.dart @@ -20,6 +20,7 @@ class _ReviewsListPageState extends State { @override void initState() { filteredReviews = widget.app.reviews; + filteredReviews.sort((a, b) => b.ratedAt.compareTo(a.ratedAt)); replyController = TextEditingController(); super.initState(); } @@ -39,6 +40,7 @@ class _ReviewsListPageState extends State { .toList(); }); } + filteredReviews.sort((a, b) => b.ratedAt.compareTo(a.ratedAt)); } @override diff --git a/app/lib/pages/apps/app_detail/widgets/add_review_widget.dart b/app/lib/pages/apps/app_detail/widgets/add_review_widget.dart index a3d0024e3..ffcdddea3 100644 --- a/app/lib/pages/apps/app_detail/widgets/add_review_widget.dart +++ b/app/lib/pages/apps/app_detail/widgets/add_review_widget.dart @@ -79,9 +79,9 @@ class _AddReviewWidgetState extends State { color: Colors.grey.shade900, borderRadius: BorderRadius.circular(16.0), ), - child: Row( + child: Column( children: [ - Column( + Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( @@ -89,158 +89,160 @@ class _AddReviewWidgetState extends State { child: Text(widget.app.userReview?.score == null ? 'Rate and Review this App' : 'Your Review', style: const TextStyle(color: Colors.white, fontSize: 16)), ), - const SizedBox(height: 14), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: RatingBar.builder( - initialRating: rating, - minRating: 1, - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 34, - itemPadding: const EdgeInsets.symmetric(horizontal: 18), - itemBuilder: (context, _) => const Icon(Icons.star, color: Colors.deepPurple), - maxRating: 5.0, - onRatingUpdate: (rating) { - if (isLoading) return; - setShowReviewField(true); - updateRating(rating); - updateShowButton(true); - }, - ), - ), - const SizedBox( - height: 20, - ), - ClipRRect( - child: AnimatedContainer( - duration: const Duration(milliseconds: 300), - height: showReviewField - ? (showButton - ? MediaQuery.sizeOf(context).height * 0.2 - : MediaQuery.sizeOf(context).height * 0.132) - : 0, - child: !showReviewField - ? null - : SingleChildScrollView( - physics: const NeverScrollableScrollPhysics(), - child: Column( - children: [ - SizedBox( - width: MediaQuery.sizeOf(context).width * 0.88, - child: TextFormField( - controller: reviewController, - enabled: isLoading ? false : true, - maxLength: 250, - onChanged: (value) { - if (value.isEmpty) { - if (value == widget.app.userReview?.review) { + ], + ), + const SizedBox(height: 14), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: RatingBar.builder( + initialRating: rating, + minRating: 1, + direction: Axis.horizontal, + allowHalfRating: true, + itemCount: 5, + itemSize: MediaQuery.sizeOf(context).width < 400 ? 28 : 34, + itemPadding: const EdgeInsets.symmetric(horizontal: 18), + itemBuilder: (context, _) => const Icon(Icons.star, color: Colors.deepPurple), + maxRating: 5.0, + onRatingUpdate: (rating) { + if (isLoading) return; + setShowReviewField(true); + updateRating(rating); + updateShowButton(true); + }, + ), + ), + const SizedBox( + height: 20, + ), + ClipRRect( + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + height: showReviewField + ? (showButton + ? (MediaQuery.sizeOf(context).height < 680 + ? MediaQuery.sizeOf(context).height * 0.28 + : MediaQuery.sizeOf(context).height * 0.2) + : MediaQuery.sizeOf(context).height * 0.132) + : 0, + child: !showReviewField + ? null + : SingleChildScrollView( + physics: const NeverScrollableScrollPhysics(), + child: Column( + children: [ + SizedBox( + width: MediaQuery.sizeOf(context).width * 0.88, + child: TextFormField( + controller: reviewController, + enabled: isLoading ? false : true, + maxLength: 250, + onChanged: (value) { + if (value.isEmpty) { + if (value == widget.app.userReview?.review) { + updateShowButton(false); + } else { + updateShowButton(true); + } + } else { + updateShowButton(true); + } + }, + decoration: InputDecoration( + hintText: 'Write a review (optional)', + hintStyle: const TextStyle(color: Colors.grey), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey), + ), + enabledBorder: OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey[700]!), + ), + focusedBorder: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(8)), + borderSide: BorderSide(color: Colors.grey), + ), + ), + style: const TextStyle(color: Colors.white), + maxLines: 3, + ), + ), + const SizedBox( + height: 20, + ), + showButton + ? AnimatedLoadingButton( + loaderColor: Colors.black, + text: widget.app.userReview != null ? 'Update Review' : 'Submit Review', + textStyle: const TextStyle(color: Colors.black, fontSize: 16), + onPressed: () async { + FocusScope.of(context).unfocus(); + if (rating == widget.app.userReview?.score && + reviewController.text == widget.app.userReview?.review) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text("No changes in review to update."), + )); + return; + } + final connectivityProvider = + Provider.of(context, listen: false); + if (connectivityProvider.isConnected) { + bool isSuccessful = false; + var rev = AppReview( + uid: SharedPreferencesUtil().uid, + review: reviewController.text, + score: rating, + ratedAt: widget.app.userReview?.ratedAt ?? DateTime.now(), + response: widget.app.userReview?.response ?? '', + username: widget.app.userReview?.username ?? '', + ); + if (widget.app.userReview == null) { + isSuccessful = await reviewApp(widget.app.id, rev); + widget.app.ratingCount += 1; + widget.app.userReview = rev; + } else { + isSuccessful = await updateAppReview(widget.app.id, rev); + widget.app.userReview = rev; + } + if (isSuccessful) { updateShowButton(false); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text("Review added successfully 🚀"), + )); + bool hadReview = widget.app.userReview != null; + if (!hadReview) widget.app.ratingCount += 1; + widget.app.userReview = AppReview( + uid: SharedPreferencesUtil().uid, + ratedAt: DateTime.now(), + review: reviewController.text, + score: rating, + ); + var appsList = SharedPreferencesUtil().appsList; + var index = appsList.indexWhere((element) => element.id == widget.app.id); + appsList[index] = widget.app; + SharedPreferencesUtil().appsList = appsList; + MixpanelManager().appRated(widget.app.id.toString(), rating); + debugPrint('Refreshed apps list.'); + setState(() {}); } else { - updateShowButton(true); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text("Failed to review the app. Please try again later."), + )); } } else { - updateShowButton(true); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text("Can't rate app without internet connection."), + )); } }, - decoration: InputDecoration( - hintText: 'Write a review (optional)', - hintStyle: const TextStyle(color: Colors.grey), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(8)), - borderSide: BorderSide(color: Colors.grey), - ), - enabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all(Radius.circular(8)), - borderSide: BorderSide(color: Colors.grey[700]!), - ), - focusedBorder: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(8)), - borderSide: BorderSide(color: Colors.grey), - ), - ), - style: const TextStyle(color: Colors.white), - maxLines: 3, - ), - ), - const SizedBox( - height: 20, - ), - showButton - ? AnimatedLoadingButton( - loaderColor: Colors.black, - text: widget.app.userReview != null ? 'Update Review' : 'Submit Review', - textStyle: const TextStyle(color: Colors.black, fontSize: 16), - onPressed: () async { - FocusScope.of(context).unfocus(); - if (rating == widget.app.userReview?.score && - reviewController.text == widget.app.userReview?.review) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text("No changes in review to update."), - )); - return; - } - final connectivityProvider = - Provider.of(context, listen: false); - if (connectivityProvider.isConnected) { - bool isSuccessful = false; - var rev = AppReview( - uid: SharedPreferencesUtil().uid, - review: reviewController.text, - score: rating, - ratedAt: widget.app.userReview?.ratedAt ?? DateTime.now(), - response: widget.app.userReview?.response ?? '', - username: widget.app.userReview?.username ?? '', - ); - if (widget.app.userReview == null) { - isSuccessful = await reviewApp(widget.app.id, rev); - widget.app.ratingCount += 1; - widget.app.userReview = rev; - } else { - isSuccessful = await updateAppReview(widget.app.id, rev); - widget.app.userReview = rev; - } - if (isSuccessful) { - updateShowButton(false); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text("Review added successfully 🚀"), - )); - bool hadReview = widget.app.userReview != null; - if (!hadReview) widget.app.ratingCount += 1; - widget.app.userReview = AppReview( - uid: SharedPreferencesUtil().uid, - ratedAt: DateTime.now(), - review: reviewController.text, - score: rating, - ); - var appsList = SharedPreferencesUtil().appsList; - var index = appsList.indexWhere((element) => element.id == widget.app.id); - appsList[index] = widget.app; - SharedPreferencesUtil().appsList = appsList; - MixpanelManager().appRated(widget.app.id.toString(), rating); - debugPrint('Refreshed apps list.'); - setState(() {}); - } else { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text("Failed to review the app. Please try again later."), - )); - } - } else { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text("Can't rate app without internet connection."), - )); - } - }, - color: Colors.white, - ) - : const SizedBox(), - ], - ), - ), - ), - ), - ], + color: Colors.white, + ) + : const SizedBox(), + ], + ), + ), + ), ), ], ), diff --git a/app/lib/pages/apps/list_item.dart b/app/lib/pages/apps/list_item.dart index cae88e0bd..9cca6607e 100644 --- a/app/lib/pages/apps/list_item.dart +++ b/app/lib/pages/apps/list_item.dart @@ -52,26 +52,24 @@ class AppListItem extends StatelessWidget { ), errorWidget: (context, url, error) => const Icon(Icons.error), ), - const SizedBox(width: 16), + const SizedBox(width: 14), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - child: Row( - children: [ - ConstrainedBox( - constraints: BoxConstraints(maxWidth: MediaQuery.sizeOf(context).width * 0.62), - child: Text( - app.name.decodeString + (app.private && showPrivateIcon ? " 🔒".decodeString : ''), - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 16), - ), + Row( + children: [ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: MediaQuery.sizeOf(context).width * 0.62), + child: Text( + app.name.decodeString + (app.private && showPrivateIcon ? " 🔒".decodeString : ''), + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 16), ), - ], - ), + ), + ], ), SizedBox(height: app.ratingAvg != null ? 4 : 0), Padding( @@ -122,7 +120,7 @@ class AppListItem extends StatelessWidget { ], ), ), - const SizedBox(width: 16), + SizedBox(width: MediaQuery.sizeOf(context).width * 0.02), provider.appLoading.isNotEmpty && index != -1 && provider.appLoading[index] ? const Padding( padding: EdgeInsets.all(10), diff --git a/app/lib/pages/apps/widgets/app_section_card.dart b/app/lib/pages/apps/widgets/app_section_card.dart index b10c4b049..abb577025 100644 --- a/app/lib/pages/apps/widgets/app_section_card.dart +++ b/app/lib/pages/apps/widgets/app_section_card.dart @@ -19,7 +19,10 @@ class AppSectionCard extends StatelessWidget { return const SizedBox.shrink(); } return Container( - height: height ?? MediaQuery.sizeOf(context).height * 0.4, + height: height ?? + (MediaQuery.sizeOf(context).height < 680 + ? MediaQuery.sizeOf(context).height * 0.5 + : MediaQuery.sizeOf(context).height * 0.4), margin: const EdgeInsets.only(left: 6.0, right: 6.0, top: 12, bottom: 14), decoration: BoxDecoration( // color: Colors.grey.shade900, @@ -39,7 +42,7 @@ class AppSectionCard extends StatelessWidget { scrollDirection: Axis.horizontal, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, - childAspectRatio: MediaQuery.sizeOf(context).width < 400 ? 0.25 : 0.3, + childAspectRatio: MediaQuery.sizeOf(context).width < 400 ? 0.26 : 0.3, crossAxisSpacing: 14, mainAxisSpacing: 14, ), @@ -110,7 +113,10 @@ class SectionAppItemCard extends StatelessWidget { Text( app.name, maxLines: 1, - style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 16), + style: TextStyle( + fontWeight: FontWeight.w600, + color: Colors.white, + fontSize: MediaQuery.sizeOf(context).width < 400 ? 14 : 16), ), Padding( padding: const EdgeInsets.only(top: 4.0), @@ -118,7 +124,7 @@ class SectionAppItemCard extends StatelessWidget { app.getCategoryName(), maxLines: 2, style: - TextStyle(color: Colors.grey, fontSize: MediaQuery.sizeOf(context).width < 400 ? 12 : 14), + TextStyle(color: Colors.grey, fontSize: MediaQuery.sizeOf(context).width < 400 ? 10 : 14), ), ), app.ratingAvg != null || app.installs > 0