v5.7.1 - updates

This commit is contained in:
Anthony 2022-02-08 00:00:15 +00:00
parent d8b3a5eee8
commit ed1de1258a
16 changed files with 388 additions and 315 deletions

View File

@ -1,3 +1,11 @@
## [5.7.1] - 2022-02-07
* Refactor account order detail page
* Fix continuous loading if users has no orders
* New styling for tabs in the account order detail page
* Small refactor to controller loading
* Pubspec.yaml dependency updates
## [5.7.0] - 2022-01-29
* Refactor product detail page

View File

@ -4,7 +4,7 @@
# WooCommerce App: Label StoreMax
### Label StoreMax - v5.7.0
### Label StoreMax - v5.7.1
[Official WooSignal WooCommerce App](https://woosignal.com)
@ -45,7 +45,7 @@ Full documentation this available [here](https://woosignal.com/docs/app/label-st
- Change app name, logo, customize default language, currency + more
- Light and dark mode
- Stripe, Cash On Delivery, PayPal
- Localized for en, es, pt, it, hi, fr, zh, tr, nl
- Localized for en, es, pt, it, hi, fr, zh, tr, nl, de
- Orders show as normal in WooCommerce
## Security Vulnerabilities

View File

@ -29,6 +29,8 @@ class ProductCategorySearchLoaderController
category: productCategory.id.toString(),
page: page,
status: "publish",
stockStatus: "instock"));
stockStatus: "instock",
),
);
}
}

View File

@ -9,17 +9,15 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:bubble_tab_indicator/bubble_tab_indicator.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/customer_orders_loader_controller.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/bootstrap/shared_pref/sp_auth.dart';
import 'package:flutter_app/resources/widgets/account_detail_orders_widget.dart';
import 'package:flutter_app/resources/widgets/account_detail_settings_widget.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:woosignal/models/response/order.dart';
import 'package:wp_json_api/models/responses/wc_customer_info_response.dart';
import 'package:wp_json_api/wp_json_api.dart';
@ -32,35 +30,16 @@ class AccountDetailPage extends StatefulWidget {
class _AccountDetailPageState extends State<AccountDetailPage>
with SingleTickerProviderStateMixin {
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
final CustomerOrdersLoaderController _customerOrdersLoaderController =
CustomerOrdersLoaderController();
bool _shouldStopRequests = false, _isLoading = true, _isLoadingOrders = true;
TabController _tabController;
bool _isLoading = true;
int _currentTabIndex = 0;
WCCustomerInfoResponse _wcCustomerInfoResponse;
Widget _activeBody;
TabController _tabController;
List<Tab> _tabs = [];
@override
void initState() {
super.initState();
_tabs = [
Tab(text: ""),
Tab(text: ""),
];
_tabController = TabController(vsync: this, length: _tabs.length);
_activeBody = AppLoaderWidget();
init();
}
init() async {
await _fetchWpUserData();
await fetchOrders();
_tabController = TabController(vsync: this, length: 2);
_fetchWpUserData();
}
_fetchWpUserData() async {
@ -93,10 +72,23 @@ class _AccountDetailPageState extends State<AccountDetailPage>
@override
Widget build(BuildContext context) {
_tabs = [
Tab(text: trans("Orders")),
Tab(text: trans("Settings")),
];
Widget activeBody;
if (_currentTabIndex == 0) {
activeBody = AccountDetailOrdersWidget();
} else if (_currentTabIndex == 1) {
activeBody = AccountDetailSettingsWidget(
refreshAccount: () {
setState(() {
_isLoading = true;
});
_fetchWpUserData();
},
);
}
if (activeBody == null) {
return SizedBox.shrink();
}
return Scaffold(
appBar: AppBar(
@ -182,13 +174,17 @@ class _AccountDetailPageState extends State<AccountDetailPage>
),
Padding(
child: TabBar(
tabs: _tabs,
tabs: [
Tab(text: trans("Orders")),
Tab(text: trans("Settings")),
],
controller: _tabController,
indicatorSize: TabBarIndicatorSize.tab,
labelColor: Colors.white,
unselectedLabelColor: Colors.black87,
indicator: BubbleTabIndicator(
indicatorHeight: 25.0,
indicatorHeight: 30.0,
indicatorRadius: 5,
indicatorColor: Colors.black87,
tabBarIndicatorSize: TabBarIndicatorSize.tab,
),
@ -207,7 +203,7 @@ class _AccountDetailPageState extends State<AccountDetailPage>
color: ThemeColor.get(context).backgroundContainer,
),
),
Expanded(child: _activeBody),
Expanded(child: activeBody),
],
),
),
@ -221,259 +217,8 @@ class _AccountDetailPageState extends State<AccountDetailPage>
}
_tabsTapped(int i) {
_currentTabIndex = i;
setState(() {
if (_currentTabIndex == 0) {
_activeBody = _widgetOrders();
} else {
_activeBody = _widgetSettings();
}
_currentTabIndex = i;
});
}
ListView _widgetSettings() {
return ListView(
children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.account_circle),
title: Text(trans("Update details")),
onTap: () =>
Navigator.pushNamed(context, "/account-update").then((onValue) {
setState(() {
_isLoading = true;
});
_fetchWpUserData();
}),
),
),
Card(
child: ListTile(
leading: Icon(Icons.local_shipping),
title: Text(trans("Shipping Details")),
onTap: () =>
Navigator.pushNamed(context, "/account-shipping-details"),
),
),
Card(
child: ListTile(
leading: Icon(Icons.credit_card),
title: Text(trans("Billing Details")),
onTap: () =>
Navigator.pushNamed(context, "/account-billing-details"),
),
),
Card(
child: ListTile(
leading: Icon(Icons.exit_to_app),
title: Text(trans("Logout")),
onTap: () => authLogout(context),
),
),
],
);
}
fetchOrders() async {
String userId = await readUserId();
if (userId == null) {
setState(() {
_isLoadingOrders = false;
_activeBody = _widgetOrders();
});
return;
}
await _customerOrdersLoaderController.loadOrders(
hasResults: (result) {
if (result == false) {
_isLoadingOrders = false;
_shouldStopRequests = true;
_activeBody = _widgetOrders();
return false;
}
return true;
},
didFinish: () => setState(() {
_isLoadingOrders = false;
_activeBody = _widgetOrders();
}),
userId: userId);
}
Widget _widgetOrders() {
List<Order> orders = _customerOrdersLoaderController.getResults();
return _isLoadingOrders
? AppLoaderWidget()
: SmartRefresher(
enablePullDown: true,
enablePullUp: true,
footer: CustomFooter(
builder: (BuildContext context, LoadStatus mode) {
Widget body;
if (mode == LoadStatus.idle) {
body = Text(trans("pull up load"));
} else if (mode == LoadStatus.loading) {
body = CupertinoActivityIndicator();
} else if (mode == LoadStatus.failed) {
body = Text(trans("Load Failed! Click retry!"));
} else if (mode == LoadStatus.canLoading) {
body = Text(trans("release to load more"));
} else {
body = Text(trans("No more orders"));
}
return Container(
height: 55.0,
child: Center(child: body),
);
},
),
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
child: (orders.isNotEmpty
? ListView.builder(
itemBuilder: (cxt, i) {
Order order = orders[i];
return Card(
child: ListTile(
contentPadding: EdgeInsets.only(
top: 5, bottom: 5, left: 8, right: 6),
title: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFFCFCFC),
width: 1,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"#${order.id.toString()}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
order.status.capitalize(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
formatStringCurrency(total: order.total),
style: Theme.of(context)
.textTheme
.bodyText2
.copyWith(
fontWeight: FontWeight.w600),
textAlign: TextAlign.left,
),
Text(
order.lineItems.length.toString() +
" " +
trans("items"),
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(
fontWeight: FontWeight.w600),
textAlign: TextAlign.left,
),
],
),
Text(
dateFormatted(
date: order.dateCreated,
formatType:
formatForDateTime(FormatType.date),
) +
"\n" +
dateFormatted(
date: order.dateCreated,
formatType:
formatForDateTime(FormatType.time),
),
textAlign: TextAlign.right,
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(
fontWeight: FontWeight.w400,
),
),
],
),
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.chevron_right),
],
),
onTap: () => _viewProfileDetail(i),
),
);
},
itemCount: orders.length,
)
: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.shopping_cart,
color: Colors.black54,
size: 40,
),
Text(
trans("No orders found"),
),
],
),
)),
);
}
void _onRefresh() async {
_customerOrdersLoaderController.clear();
_shouldStopRequests = false;
await fetchOrders();
_refreshController.refreshCompleted();
}
void _onLoading() async {
await fetchOrders();
if (mounted) {
setState(() {});
if (_shouldStopRequests) {
_refreshController.loadNoData();
} else {
_refreshController.loadComplete();
}
}
}
_viewProfileDetail(int i) => Navigator.pushNamed(
context,
"/account-order-detail",
arguments: _customerOrdersLoaderController.getResults()[i].id,
);
}

View File

@ -94,10 +94,12 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
void _onRefresh() async {
_productCategorySearchLoaderController.clear();
_shouldStopRequests = false;
await fetchProducts();
_refreshController.refreshCompleted();
setState(() {
_shouldStopRequests = false;
_refreshController.refreshCompleted(resetFooterState: true);
});
}
void _onLoading() async {

View File

@ -83,10 +83,12 @@ class _BrowseSearchState extends NyState<BrowseSearchPage> {
void _onRefresh() async {
_productSearchLoaderController.clear();
_shouldStopRequests = false;
await fetchProducts();
_refreshController.refreshCompleted();
setState(() {
_shouldStopRequests = false;
_refreshController.refreshCompleted(resetFooterState: true);
});
}
void _onLoading() async {

View File

@ -175,10 +175,12 @@ class _ProductReviewsPageState extends NyState<ProductReviewsPage> {
_onRefresh() async {
_productReviewsLoaderController.clear();
_shouldStopRequests = false;
await fetchProductReviews();
_refreshController.refreshCompleted();
setState(() {
_shouldStopRequests = false;
_refreshController.refreshCompleted(resetFooterState: true);
});
}
_onLoading() async {

View File

@ -0,0 +1,246 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/customer_orders_loader_controller.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/bootstrap/shared_pref/sp_auth.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:woosignal/models/response/order.dart';
class AccountDetailOrdersWidget extends StatefulWidget {
@override
_AccountDetailOrdersWidgetState createState() =>
_AccountDetailOrdersWidgetState();
}
class _AccountDetailOrdersWidgetState extends State<AccountDetailOrdersWidget> {
bool _isLoadingOrders = true, _shouldStopRequests = false;
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
final CustomerOrdersLoaderController _customerOrdersLoaderController =
CustomerOrdersLoaderController();
@override
void initState() {
super.initState();
fetchOrders();
}
@override
Widget build(BuildContext context) {
List<Order> orders = _customerOrdersLoaderController.getResults();
if (_isLoadingOrders == true) {
return AppLoaderWidget();
}
if (orders.isEmpty) {
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.shopping_cart,
color: Colors.black54,
size: 40,
),
Text(
trans("No orders found"),
),
],
),
);
}
return SmartRefresher(
enablePullDown: true,
enablePullUp: true,
footer: CustomFooter(
builder: (BuildContext context, LoadStatus mode) {
Widget body;
if (mode == LoadStatus.idle) {
body = Text(trans("pull up load"));
} else if (mode == LoadStatus.loading) {
body = CupertinoActivityIndicator();
} else if (mode == LoadStatus.failed) {
body = Text(trans("Load Failed! Click retry!"));
} else if (mode == LoadStatus.canLoading) {
body = Text(trans("release to load more"));
} else {
body = Text(trans("No more orders"));
}
return Container(
height: 55.0,
child: Center(child: body),
);
},
),
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
child: ListView.builder(
itemBuilder: (context, i) {
Order order = orders[i];
return Card(
child: ListTile(
contentPadding: EdgeInsets.only(
top: 5,
bottom: 5,
left: 8,
right: 6,
),
title: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFFCFCFC),
width: 1,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"#${order.id.toString()}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
order.status.capitalize(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
formatStringCurrency(total: order.total),
style: Theme.of(context)
.textTheme
.bodyText2
.copyWith(fontWeight: FontWeight.w600),
textAlign: TextAlign.left,
),
Text(
order.lineItems.length.toString() +
" " +
trans("items"),
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(fontWeight: FontWeight.w600),
textAlign: TextAlign.left,
),
],
),
Text(
dateFormatted(
date: order.dateCreated,
formatType: formatForDateTime(FormatType.date),
) +
"\n" +
dateFormatted(
date: order.dateCreated,
formatType: formatForDateTime(FormatType.time),
),
textAlign: TextAlign.right,
style: Theme.of(context).textTheme.bodyText1.copyWith(
fontWeight: FontWeight.w400,
),
),
],
),
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.chevron_right),
],
),
onTap: () => _viewOrderDetail(i, order.id),
),
);
},
itemCount: orders.length,
),
);
}
void _onRefresh() async {
_customerOrdersLoaderController.clear();
await fetchOrders();
setState(() {
_shouldStopRequests = false;
_refreshController.refreshCompleted(resetFooterState: true);
});
}
void _onLoading() async {
await fetchOrders();
if (mounted) {
setState(() {});
if (_shouldStopRequests) {
_refreshController.loadNoData();
} else {
_refreshController.loadComplete();
}
}
}
fetchOrders() async {
String userId = await readUserId();
if (userId == null) {
setState(() {
_isLoadingOrders = false;
});
return;
}
await _customerOrdersLoaderController.loadOrders(
hasResults: (result) {
if (result == false) {
setState(() {
_isLoadingOrders = false;
_shouldStopRequests = true;
});
return false;
}
return true;
},
didFinish: () => setState(() {
_isLoadingOrders = false;
}),
userId: userId);
}
_viewOrderDetail(int i, int orderId) => Navigator.pushNamed(
context,
"/account-order-detail",
arguments: orderId,
);
}

View File

@ -0,0 +1,59 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/bootstrap/shared_pref/sp_auth.dart';
import 'package:nylo_framework/nylo_framework.dart';
class AccountDetailSettingsWidget extends StatelessWidget {
const AccountDetailSettingsWidget({Key key, @required this.refreshAccount})
: super(key: key);
final Function refreshAccount;
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.account_circle),
title: Text(trans("Update details")),
onTap: () =>
Navigator.pushNamed(context, "/account-update").then((onValue) {
refreshAccount();
}),
),
),
Card(
child: ListTile(
leading: Icon(Icons.local_shipping),
title: Text(trans("Shipping Details")),
onTap: () =>
Navigator.pushNamed(context, "/account-shipping-details"),
),
),
Card(
child: ListTile(
leading: Icon(Icons.credit_card),
title: Text(trans("Billing Details")),
onTap: () =>
Navigator.pushNamed(context, "/account-billing-details"),
),
),
Card(
child: ListTile(
leading: Icon(Icons.exit_to_app),
title: Text(trans("Logout")),
onTap: () => authLogout(context),
),
),
],
);
}
}

View File

@ -18,6 +18,7 @@ class AppLoaderWidget extends StatelessWidget {
Widget build(BuildContext context) {
bool isDark = (Theme.of(context).brightness == Brightness.dark);
return SpinKitDoubleBounce(
color: Color(!isDark ? 0xFF424242 : 0xFFC7C7C7));
color: Color(!isDark ? 0xFF424242 : 0xFFC7C7C7),
);
}
}

View File

@ -122,10 +122,12 @@ class _MelloThemeWidgetState extends State<MelloThemeWidget> {
_onRefresh() async {
_productLoaderController.clear();
_shouldStopRequests = false;
await fetchProducts();
_refreshController.refreshCompleted();
setState(() {
_shouldStopRequests = false;
_refreshController.refreshCompleted(resetFooterState: true);
});
}
_onLoading() async {

View File

@ -209,10 +209,12 @@ class _NoticHomeWidgetState extends State<NoticHomeWidget> {
_onRefresh() async {
_productLoaderController.clear();
_shouldStopRequests = false;
await fetchProducts();
_refreshController.refreshCompleted();
setState(() {
_shouldStopRequests = false;
_refreshController.refreshCompleted(resetFooterState: true);
});
}
_onLoading() async {

View File

@ -132,10 +132,12 @@ class _ProductDetailUpsellWidgetState extends State<ProductDetailUpsellWidget> {
_onRefresh() async {
_productLoaderController.clear();
_shouldStopRequests = false;
await fetchProducts();
_refreshControllerUpsell.refreshCompleted();
setState(() {
_shouldStopRequests = false;
_refreshControllerUpsell.refreshCompleted(resetFooterState: true);
});
}
_onLoading() async {

View File

@ -377,7 +377,7 @@ packages:
name: flutter_widget_from_html
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.4"
version: "0.8.5"
flutter_widget_from_html_core:
dependency: transitive
description:
@ -447,7 +447,7 @@ packages:
name: fwfh_webview
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.2+1"
version: "0.6.2+2"
glob:
dependency: transitive
description:

View File

@ -1,14 +1,14 @@
# Official WooSignal App Template for WooCommerce
# Label StoreMax
# Version: 5.7.0
# Version: 5.7.1
# Author: Anthony Gordon
# Homepage: https://woosignal.com
# Documentation: https://woosignal.com/docs/app/label-storemax
### Change App Icon
# 1 Replace: public/assets/icon/appicon.png (1024px1024px icon size)
# 2 Run this command from the terminal: "flutter pub run nylo_framework:main appicons:build"
# 2 Run this command from the terminal: "flutter pub run flutter_launcher_icons:main"
### Uploading the IOS/Android app
# IOS https://flutter.dev/docs/deployment/ios
@ -25,7 +25,7 @@ environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
google_fonts: ^2.2.0
google_fonts: 2.2.0
analyzer: ^1.5.0
intl: ^0.17.0
page_transition: ^2.0.5
@ -51,7 +51,7 @@ dependencies:
flutter_spinkit: ^5.1.0
auto_size_text: ^3.0.0
html: ^0.15.0
flutter_widget_from_html: ^0.8.4
flutter_widget_from_html: ^0.8.5
flutter_rating_bar: ^4.0.0
flutter_staggered_grid_view: ^0.4.1
# firebase_messaging: ^11.2.3

View File

@ -4,7 +4,7 @@
# WooCommerce App: Label StoreMax
### Label StoreMax - v5.7.0
### Label StoreMax - v5.7.1
[Official WooSignal WooCommerce App](https://woosignal.com)