reason

Merge branch 'dev_chad_v1.0' into 'dev_reason_v1.0'

Dev chad v1.0



See merge request !3
......@@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 32
compileSdkVersion 33
buildToolsVersion "32"
compileOptions {
......
......@@ -27,7 +27,7 @@ PODS:
- Flutter
- flutter_native_splash (0.0.1):
- Flutter
- flutter_secure_storage (3.3.1):
- flutter_secure_storage (6.0.0):
- Flutter
- flutter_sound (9.2.13):
- Flutter
......@@ -53,6 +53,7 @@ PODS:
- Flutter
- in_app_purchase_storekit (0.0.1):
- Flutter
- FlutterMacOS
- integration_test (0.0.1):
- Flutter
- JCore (3.2.5)
......@@ -220,7 +221,7 @@ SPEC CHECKSUMS:
flutter_facebook_auth: c69f4e643b1d9cc9063ec87c9411bd9ec268108f
flutter_inapp_purchase: 5c6a1ac3f11b11d0c8c0321c0c41c1f05805e4c8
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
flutter_sound: c60effa2a350fb977885f0db2fbc4c1ad5160900
flutter_sound_core: 26c10e5832e76aaacfae252d8925232281c486ae
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
......@@ -229,7 +230,7 @@ SPEC CHECKSUMS:
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
in_app_purchase_storekit: d7fcf4646136ec258e237872755da8ea6c1b6096
in_app_purchase_storekit: 6b297e2b5eab9fa3251a492d57301722e4132a71
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
JCore: 4c9febdfdb1a5675ab361bc1f06c0905391d7b23
JPush: 2caabdb6d62d009bc07a9bd115d03c7c42512b5e
......@@ -252,4 +253,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 7d714a5ac7fda1315bc261738086cf00fa98c7e3
COCOAPODS: 1.11.3
COCOAPODS: 1.11.2
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>一言临境</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Parlando</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSMicrophoneUsageDescription</key>
<string>打开话筒</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
......@@ -24,4 +24,19 @@ class OrderApi extends BaseApi {
return json.decode(value.data);
});
}
Future<dynamic> verifyOrder(String orderId, String type, String token, {Map<String, dynamic>? others}) {
var data = {
"order_sn": orderId,
"pay_type": type,
"token": token,
"others": others ?? {},
};
return post("pay", data: data).then((value) {
if (TextUtil.isEmpty(value.data)) {
return {};
}
return value.data;
});
}
}
......
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
abstract class BaseState<T extends StatefulWidget> extends State<T> {
bool _isFirstBuild = true;
List<Timer> delayTimers = [];
@override
Widget build(BuildContext context) {
......@@ -13,6 +16,11 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
return buildBody(context);
}
postDelay(VoidCallback fn, int delayTime) {
var delayTimer = Timer(Duration(milliseconds: delayTime), fn);
delayTimers.add(delayTimer);
}
Widget buildBody(BuildContext context);
void onFirstBuildBody(BuildContext context) {}
......@@ -32,4 +40,12 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
Widget buildLoading() {
return const Center(child: CircularProgressIndicator());
}
@override
void dispose() {
super.dispose();
for (var element in delayTimers) {
element.cancel();
}
}
}
......
......@@ -68,6 +68,10 @@ MembershipData $MembershipDataFromJson(Map<String, dynamic> json) {
if (videoCover != null) {
membershipData.videoCover = videoCover;
}
final String? expireVipAt = jsonConvert.convert<String>(json['expire_vip_time']);
if (videoCover != null) {
membershipData.expireVipAt = expireVipAt;
}
final String? terminal = jsonConvert.convert<String>(json['terminal']);
if (terminal != null) {
membershipData.terminal = terminal;
......@@ -105,6 +109,7 @@ Map<String, dynamic> $MembershipDataToJson(MembershipData entity) {
data['bg_images'] = entity.bgImages;
data['video_url'] = entity.videoUrl;
data['video_cover'] = entity.videoCover;
data['expire_vip_time'] = entity.expireVipAt;
data['terminal'] = entity.terminal;
data['state'] = entity.state;
data['created_at'] = entity.createdAt;
......
......@@ -42,6 +42,8 @@ class MembershipData {
String? createdAt;
@JSONField(name: "updated_at")
String? updatedAt;
@JSONField(name: "expire_vip_time")
String? expireVipAt;
@JSONField(name: "is_vip")
int? isVip;
@JSONField(name: "goods_list")
......
......@@ -41,12 +41,20 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
super.initState();
PaymentSdk.instance.initState(onPaySuccess: () {
hideLoading();
toast("购买成功, 请等待订阅生效");
postDelay(() {
refreshData();
}, 1000);
print("onPaySuccess");
}, onCancel: () {
hideLoading();
print("onCancel");
}, onFailed: () {
hideLoading();
print("onFailed");
}, onPending: () {
toast("订单Pending");
showLoading();
print("onPending");
});
PaymentSdk.instance.queryProducts().then((value) {
_products.clear();
......@@ -54,14 +62,18 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
setState(() {});
});
if (SpUtil.containsKey(Constant.userToken)!) {
showLoading();
Provider.of<MembershipViewProvider>(context, listen: false).setSelectedMembership(null);
Provider.of<MembershipViewProvider>(context, listen: false).fetchMembershipData('0');
refreshData();
} else {
NavigatorUtils.push(context, LoginRouter.loginPage, replace: true);
}
}
void refreshData() {
showLoading();
Provider.of<MembershipViewProvider>(context, listen: false).setSelectedMembership(null);
Provider.of<MembershipViewProvider>(context, listen: false).fetchMembershipData('0');
}
@override
Widget buildBody(BuildContext context) {
///响应数据
......@@ -120,7 +132,7 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
children: [
Text(mb?.title ?? "一言会员", style: TextStyle(fontSize: 18.px, color: Colors.white)),
Gaps.vGap24,
mb?.goodsList?.isNotEmpty == true ? buildProductWidget(mb) : const GFLoader(),
mb?.goodsList?.isNotEmpty == true ? buildProductWidget(mb) : buildLoadingOrSuccess(mb),
Gaps.vGap24,
Text(mb?.intro ?? "一言介绍", style: TextStyle(fontSize: 14.px, color: Colors.white)),
Gaps.vGap10,
......@@ -208,7 +220,10 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
),
Container(width: 0.6, height: 15.0, color: Colours.line),
TextButton(
onPressed: () {},
onPressed: () {
PaymentSdk.instance.restore();
showLoading();
},
child: Text(
"恢复购买",
style: TextStyle(fontSize: 14.px, color: Colors.white),
......@@ -223,4 +238,22 @@ class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingO
PaymentSdk.instance.dispose();
super.dispose();
}
buildLoadingOrSuccess(MembershipData? mb) {
if (mb?.isVip == 1) {
return Column(
children: [
Text(
"过期时间:",
style: TextStyle(fontSize: 14.px, color: Colors.white),
),
Text(
mb?.expireVipAt ?? "",
style: TextStyle(fontSize: 14.px, color: Colors.white),
)
],
);
}
return const GFLoader();
}
}
......
......@@ -4,8 +4,12 @@ import 'package:Parlando/apis/api_order.dart';
import 'package:Parlando/membership/models/membership_entity.dart';
import 'package:common_utils/common_utils.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
class PaymentSdk {
var currentOrder;
PaymentSdk._privateConstructor();
static final PaymentSdk _instance = PaymentSdk._privateConstructor();
......@@ -59,6 +63,7 @@ class PaymentSdk {
onFailed?.call();
return;
}
currentOrder = orderId;
final PurchaseParam purchaseParam = PurchaseParam(productDetails: details, applicationUserName: orderId);
if (_isConsumable(details)) {
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
......@@ -97,7 +102,30 @@ class PaymentSdk {
}
void _deliverProduct(PurchaseDetails purchaseDetails) {
onPaySuccess?.call();
String type = "";
Map<String, dynamic> otherField = {};
if (purchaseDetails is GooglePlayPurchaseDetails) {
currentOrder = purchaseDetails.billingClientPurchase.obfuscatedAccountId;
type = "google";
otherField["google"] = {
"originalJson": purchaseDetails.billingClientPurchase.originalJson,
};
}
if (purchaseDetails is AppStorePurchaseDetails) {
type = "apple";
otherField["apple"] = {
"transactionIdentifier": purchaseDetails.skPaymentTransaction.transactionIdentifier,
"originalTransactionIdentifier":
purchaseDetails.skPaymentTransaction.originalTransaction?.transactionIdentifier ?? "",
};
}
var serverVerifyStr = purchaseDetails.verificationData.serverVerificationData;
var verifySource = purchaseDetails.verificationData.source;
otherField["source"] = verifySource;
OrderApi.request.verifyOrder(currentOrder, type, serverVerifyStr, others: otherField).then((value) {
if (value != null) {}
onPaySuccess?.call();
});
}
void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
......@@ -109,7 +137,7 @@ class PaymentSdk {
}
bool _isConsumable(ProductDetails details) {
return true;
return false;
}
void restore() {
......
import 'dart:async';
import 'dart:convert';
import 'package:Parlando/base/base_state.dart';
import 'package:Parlando/extension/widget_ext.dart';
import 'package:Parlando/models/nearby_response.dart' as nearby;
import 'package:Parlando/routers/fluro_navigator.dart';
......@@ -20,7 +21,7 @@ class AddressSelectPage extends StatefulWidget {
AddressSelectPageState createState() => AddressSelectPageState();
}
class AddressSelectPageState extends State<AddressSelectPage> {
class AddressSelectPageState extends BaseState<AddressSelectPage> {
List<nearby.Results> _nearByList = [];
final ScrollController _controller = ScrollController();
LatLng? _center;
......@@ -32,10 +33,10 @@ class AddressSelectPageState extends State<AddressSelectPage> {
String apiKey = "AIzaSyDQZsMULyO-UtiSht4_MFi1uHT4BIqasjw";
nearby.NearbyPlacesResponse nearbyPlacesResponse = nearby.NearbyPlacesResponse();
@override
void initState() {
super.initState();
showLoading();
_getCurrentLocation();
}
......@@ -81,11 +82,12 @@ class AddressSelectPageState extends State<AddressSelectPage> {
isLoading = false;
});
buildMarkers();
hideLoading();
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
getNearbyPlaces("");
buildMarkers();
}
void _goToCurrentCenter() {
......@@ -95,7 +97,7 @@ class AddressSelectPageState extends State<AddressSelectPage> {
}
@override
Widget build(BuildContext context) {
Widget buildBody(BuildContext context) {
var loaderView = const GFLoader().expanded(flex: 11);
Widget realList = ListView.separated(
controller: _controller,
......@@ -113,6 +115,7 @@ class AddressSelectPageState extends State<AddressSelectPage> {
},
).expanded(flex: 11);
if (_nearByList.isEmpty) {
print("isLoading $isLoading _nearByList ${_nearByList.length}");
realList = const Center(child: Text("没有找到任何地点")).expanded(flex: 11);
}
var listHolder = isLoading ? loaderView : realList;
......@@ -224,15 +227,24 @@ class _AddressItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
var styleMain = TextStyle(color: Colors.black87);
var styleSub = TextStyle(color: Colors.black45);
return InkWell(
onTap: onTap,
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
height: 50.0,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text('${data.name}').expanded(),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${data.name}', style: styleMain),
Text('${data.vicinity}', style: styleSub),
],
).paddingTopBottom(10).expanded(),
Visibility(visible: isSelected, child: const Icon(Icons.done, color: Colors.blue))
],
),
......
This diff is collapsed. Click to expand it.
......@@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+13
version: 1.0.0+15
environment:
sdk: ">=2.16.2 <3.0.0"
......