Compare commits
No commits in common. "94c2315894ce78725b38f594cd3f596834f2bee8" and "6163dd1ca8d1a0dc848c62ec369b42e2c1595eb9" have entirely different histories.
94c2315894
...
6163dd1ca8
|
|
@ -1,47 +0,0 @@
|
||||||
# Xcode 编译产物
|
|
||||||
DerivedData/
|
|
||||||
build/
|
|
||||||
*.xcworkspace/
|
|
||||||
*.xcuserstate
|
|
||||||
*.xcuserdatad/
|
|
||||||
xcuserdata/
|
|
||||||
UserInterfaceState.xcuserstate
|
|
||||||
*.xccheckout
|
|
||||||
*.xcscmblueprint
|
|
||||||
*.xcscmblueprint
|
|
||||||
*.xcbkptn
|
|
||||||
*.xcbkptn/
|
|
||||||
|
|
||||||
# 模拟器缓存
|
|
||||||
~/Library/Developer/CoreSimulator/
|
|
||||||
CoreSimulator/
|
|
||||||
|
|
||||||
# Pods / Carthage / Swift Package Manager
|
|
||||||
Pods/
|
|
||||||
Podfile.lock
|
|
||||||
Carthage/Build/
|
|
||||||
Package/.build/
|
|
||||||
Package.resolved
|
|
||||||
|
|
||||||
# 隐私配置、密钥、本地环境文件
|
|
||||||
*.env
|
|
||||||
*.plist.local
|
|
||||||
*.entitlements.local
|
|
||||||
secret*.txt
|
|
||||||
key*.pem
|
|
||||||
*.p12
|
|
||||||
*.mobileprovision
|
|
||||||
|
|
||||||
# 本地缓存、日志
|
|
||||||
*.log
|
|
||||||
*.swp
|
|
||||||
*.DS_Store
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
node_modules/
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
# 图片临时导出、截图
|
|
||||||
*.png.orig
|
|
||||||
*.jpg.orig
|
|
||||||
screenshot/
|
|
||||||
|
|
@ -295,8 +295,6 @@
|
||||||
30EFF3E22FDA9DEE00EB35D4 /* EmergencyContactVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E12FDA9DEE00EB35D4 /* EmergencyContactVM.swift */; };
|
30EFF3E22FDA9DEE00EB35D4 /* EmergencyContactVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E12FDA9DEE00EB35D4 /* EmergencyContactVM.swift */; };
|
||||||
30EFF3E52FDAA93400EB35D4 /* PrivacyPolicyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E42FDAA93300EB35D4 /* PrivacyPolicyVC.swift */; };
|
30EFF3E52FDAA93400EB35D4 /* PrivacyPolicyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E42FDAA93300EB35D4 /* PrivacyPolicyVC.swift */; };
|
||||||
30EFF3E72FDAA93D00EB35D4 /* PrivacyPolicyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E62FDAA93D00EB35D4 /* PrivacyPolicyView.swift */; };
|
30EFF3E72FDAA93D00EB35D4 /* PrivacyPolicyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E62FDAA93D00EB35D4 /* PrivacyPolicyView.swift */; };
|
||||||
30EFF3E82FCA8C7000227D26 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30EFF3E92FCA8C7000227D26 /* AuthenticationServices.framework */; };
|
|
||||||
55BF752D2FFE53F70055DA57 /* LocationPermissionPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55BF752C2FFE53F70055DA57 /* LocationPermissionPopView.swift */; };
|
|
||||||
D698E9C56D6F2C152772131D /* Pods_QuickLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */; };
|
D698E9C56D6F2C152772131D /* Pods_QuickLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
|
@ -600,10 +598,8 @@
|
||||||
30EFF3E12FDA9DEE00EB35D4 /* EmergencyContactVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmergencyContactVM.swift; sourceTree = "<group>"; };
|
30EFF3E12FDA9DEE00EB35D4 /* EmergencyContactVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmergencyContactVM.swift; sourceTree = "<group>"; };
|
||||||
30EFF3E42FDAA93300EB35D4 /* PrivacyPolicyVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyVC.swift; sourceTree = "<group>"; };
|
30EFF3E42FDAA93300EB35D4 /* PrivacyPolicyVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyVC.swift; sourceTree = "<group>"; };
|
||||||
30EFF3E62FDAA93D00EB35D4 /* PrivacyPolicyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyView.swift; sourceTree = "<group>"; };
|
30EFF3E62FDAA93D00EB35D4 /* PrivacyPolicyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyView.swift; sourceTree = "<group>"; };
|
||||||
30EFF3E92FCA8C7000227D26 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; };
|
|
||||||
3E4359082FC48D26003470A5 /* QuickLocation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickLocation.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
3E4359082FC48D26003470A5 /* QuickLocation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickLocation.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QuickLocation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QuickLocation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
55BF752C2FFE53F70055DA57 /* LocationPermissionPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPermissionPopView.swift; sourceTree = "<group>"; };
|
|
||||||
DA16D49AA46D4F6838340B55 /* Pods-QuickLocation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickLocation.debug.xcconfig"; path = "Target Support Files/Pods-QuickLocation/Pods-QuickLocation.debug.xcconfig"; sourceTree = "<group>"; };
|
DA16D49AA46D4F6838340B55 /* Pods-QuickLocation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickLocation.debug.xcconfig"; path = "Target Support Files/Pods-QuickLocation/Pods-QuickLocation.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
E483B9929C03809ADEDE8341 /* Pods-QuickLocation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickLocation.release.xcconfig"; path = "Target Support Files/Pods-QuickLocation/Pods-QuickLocation.release.xcconfig"; sourceTree = "<group>"; };
|
E483B9929C03809ADEDE8341 /* Pods-QuickLocation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QuickLocation.release.xcconfig"; path = "Target Support Files/Pods-QuickLocation/Pods-QuickLocation.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
@ -636,7 +632,6 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
30EFF3E82FCA8C7000227D26 /* AuthenticationServices.framework in Frameworks */,
|
|
||||||
D698E9C56D6F2C152772131D /* Pods_QuickLocation.framework in Frameworks */,
|
D698E9C56D6F2C152772131D /* Pods_QuickLocation.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
@ -1012,7 +1007,6 @@
|
||||||
30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */,
|
30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */,
|
||||||
30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */,
|
30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */,
|
||||||
30D87CDC2FDFF07500E958FD /* InteractionView.swift */,
|
30D87CDC2FDFF07500E958FD /* InteractionView.swift */,
|
||||||
55BF752C2FFE53F70055DA57 /* LocationPermissionPopView.swift */,
|
|
||||||
30A87A692FEF59E60095E7C6 /* SearchLocation */,
|
30A87A692FEF59E60095E7C6 /* SearchLocation */,
|
||||||
30A87A5C2FEE711C0095E7C6 /* Bubble */,
|
30A87A5C2FEE711C0095E7C6 /* Bubble */,
|
||||||
30CCDE4F2FE2782700F5214A /* SignIn */,
|
30CCDE4F2FE2782700F5214A /* SignIn */,
|
||||||
|
|
@ -1654,7 +1648,6 @@
|
||||||
47CD8471BE2146A2656CF27E /* Frameworks */ = {
|
47CD8471BE2146A2656CF27E /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
30EFF3E92FCA8C7000227D26 /* AuthenticationServices.framework */,
|
|
||||||
475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */,
|
475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
|
|
@ -2074,7 +2067,6 @@
|
||||||
30BF30102FED0C8E00D9CB52 /* ScheduleDetailVM.swift in Sources */,
|
30BF30102FED0C8E00D9CB52 /* ScheduleDetailVM.swift in Sources */,
|
||||||
30DA36BD2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift in Sources */,
|
30DA36BD2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift in Sources */,
|
||||||
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
|
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
|
||||||
55BF752D2FFE53F70055DA57 /* LocationPermissionPopView.swift in Sources */,
|
|
||||||
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */,
|
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */,
|
||||||
30D74ABA2FEA37AD0050EB2C /* ScheduleModel.swift in Sources */,
|
30D74ABA2FEA37AD0050EB2C /* ScheduleModel.swift in Sources */,
|
||||||
30CCDE512FE2785D00F5214A /* SignInVC.swift in Sources */,
|
30CCDE512FE2785D00F5214A /* SignInVC.swift in Sources */,
|
||||||
|
|
@ -2138,11 +2130,9 @@
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = QuickLocation/QuickLocation.entitlements;
|
CODE_SIGN_ENTITLEMENTS = QuickLocation/QuickLocation.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = XSJJKSY9FB;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = LRDLWZ2Y83;
|
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = QuickLocation/Info.plist;
|
INFOPLIST_FILE = QuickLocation/Info.plist;
|
||||||
|
|
@ -2163,10 +2153,9 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuomeng.jisuloca;
|
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisuloca;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = jsdw_dev;
|
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
|
@ -2190,11 +2179,9 @@
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = QuickLocation/QuickLocation.entitlements;
|
CODE_SIGN_ENTITLEMENTS = QuickLocation/QuickLocation.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = XSJJKSY9FB;
|
||||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = LRDLWZ2Y83;
|
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = QuickLocation/Info.plist;
|
INFOPLIST_FILE = QuickLocation/Info.plist;
|
||||||
|
|
@ -2215,10 +2202,9 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuomeng.jisuloca;
|
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisuloca;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = jsdw_dev;
|
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
|
|
|
||||||
|
|
@ -70,16 +70,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
|
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
|
||||||
print("📱 application open url: \(url.absoluteString) host: \(url.host ?? "")")
|
print("📱 application open url: \(url.absoluteString) host: \(url.host ?? "")")
|
||||||
|
|
||||||
// 微信 OAuth 回调:直接从 URL 提取 code,不依赖 SDK 异步 onResp
|
|
||||||
if let scheme = url.scheme, scheme == "wx\(AppSettings.kAppsWXApiAppId)" || scheme == AppSettings.kAppsWXApiAppId {
|
|
||||||
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
||||||
let code = components.queryItems?.first(where: { $0.name == "code" })?.value {
|
|
||||||
handleWechatLoginCode(code)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理支付宝支付结果(不限制 host,适应新版 SDK)
|
// 处理支付宝支付结果(不限制 host,适应新版 SDK)
|
||||||
if url.scheme?.hasPrefix("Alipay") == true || url.host == "safepay" {
|
if url.scheme?.hasPrefix("Alipay") == true || url.host == "safepay" {
|
||||||
AlipaySDK.defaultService().processOrder(withPaymentResult: url) { resultDic in
|
AlipaySDK.defaultService().processOrder(withPaymentResult: url) { resultDic in
|
||||||
|
|
@ -91,18 +81,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// else if url.host == "pay" {
|
else if url.host == "pay" {
|
||||||
return WXApi.handleOpen(url, delegate: self)
|
return WXApi.handleOpen(url, delegate: self)
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication,
|
return true
|
||||||
continue userActivity: NSUserActivity,
|
|
||||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
|
||||||
print("📱 continue userActivity: \(userActivity.webpageURL?.absoluteString ?? "")")
|
|
||||||
return WXApi.handleOpenUniversalLink(userActivity, delegate: self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupLocation() {
|
func setupLocation() {
|
||||||
|
|
@ -157,37 +140,11 @@ extension AppDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: WXApiDelegate {
|
extension AppDelegate: WXApiDelegate {
|
||||||
private var handledWechatLoginCode: String? {
|
|
||||||
get { objc_getAssociatedObject(self, &AssociatedKeys.handledWechatLoginCode) as? String }
|
|
||||||
set { objc_setAssociatedObject(self, &AssociatedKeys.handledWechatLoginCode, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct AssociatedKeys {
|
|
||||||
static var handledWechatLoginCode: Void?
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleWechatLoginCode(_ code: String) {
|
|
||||||
guard code != handledWechatLoginCode else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handledWechatLoginCode = code
|
|
||||||
LoginSessionHandler.login(type: "weixin", data: ["code": code, "code_type": ""])
|
|
||||||
}
|
|
||||||
|
|
||||||
func onReq(_ req: BaseReq) {
|
func onReq(_ req: BaseReq) {
|
||||||
print(req)
|
print(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func onResp(_ resp: BaseResp) {
|
func onResp(_ resp: BaseResp) {
|
||||||
if let authResp = resp as? SendAuthResp, authResp.errCode == 0, let code = authResp.code {
|
|
||||||
handleWechatLoginCode(code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.isKind(of: SendAuthResp.self) {
|
|
||||||
NotificationCenter.default.post(name: .resumePopupQueue, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.isKind(of: PayResp.self),
|
if resp.isKind(of: PayResp.self),
|
||||||
let response: PayResp = resp as? PayResp {
|
let response: PayResp = resp as? PayResp {
|
||||||
print("===================== WxPay =====================")
|
print("===================== WxPay =====================")
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "logo108@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB |
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "permission_text@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "permission_text@3x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Group 1919@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Group 1919@3x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Group_2220@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "Group_2220@3x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "mask_bg@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "mask_bg@3x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 243 KiB |
|
|
@ -20,9 +20,7 @@ extension DefaultsKeys {
|
||||||
/// 重新登录(IM需要获取id和签名)
|
/// 重新登录(IM需要获取id和签名)
|
||||||
var isReLogin: DefaultsKey<Bool> { .init("isReLogin", defaultValue: false) }
|
var isReLogin: DefaultsKey<Bool> { .init("isReLogin", defaultValue: false) }
|
||||||
/// 显示引导页
|
/// 显示引导页
|
||||||
var isShowGuide: DefaultsKey<Bool> { .init("isShowGuide", defaultValue: true) }
|
var guideShowVersion: DefaultsKey<Int?> { .init("guideShowVersion") }
|
||||||
/// 气泡持续时间
|
|
||||||
var bubbleKeepTimes: DefaultsKey<Date?> { .init("bubbleKeepTimes") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 通知常量
|
/// 通知常量
|
||||||
|
|
@ -37,12 +35,6 @@ extension Notification.Name {
|
||||||
static let ShowMemberLocationNotification = Notification.Name("ShowMemberLocationNotification")
|
static let ShowMemberLocationNotification = Notification.Name("ShowMemberLocationNotification")
|
||||||
/// 支付宝/微信支付结果回调
|
/// 支付宝/微信支付结果回调
|
||||||
static let RequestOrderPayStatusNotification = Notification.Name("RequestOrderPayStatusNotification")
|
static let RequestOrderPayStatusNotification = Notification.Name("RequestOrderPayStatusNotification")
|
||||||
/// 暂停弹窗队列(第三方登录/注册跳转时)
|
|
||||||
static let pausePopupQueue = Notification.Name("pausePopupQueue")
|
|
||||||
/// 恢复弹窗队列(第三方登录取消/失败时)
|
|
||||||
static let resumePopupQueue = Notification.Name("resumePopupQueue")
|
|
||||||
/// 终止弹窗队列(登录成功后)
|
|
||||||
static let invalidatePopupQueue = Notification.Name("invalidatePopupQueue")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ class BaseViewController: UIViewController {
|
||||||
/// 导航栏标题
|
/// 导航栏标题
|
||||||
var navTitle: String = "" {
|
var navTitle: String = "" {
|
||||||
didSet {
|
didSet {
|
||||||
navigationItem.title = navTitle
|
backButton.setTitle(" \(navTitle)", for: .normal)
|
||||||
|
// navigationItem.title = navTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,13 +102,32 @@ class BaseViewController: UIViewController {
|
||||||
dismissItem.tintColor = .white
|
dismissItem.tintColor = .white
|
||||||
navigationItem.leftBarButtonItem = dismissItem
|
navigationItem.leftBarButtonItem = dismissItem
|
||||||
} else if let count = navigationController?.viewControllers.count, count > 1 {
|
} else if let count = navigationController?.viewControllers.count, count > 1 {
|
||||||
let leftBackItem = UIBarButtonItem(image: UIImage(named: "Common/back"),
|
let leftBarBtn = UIBarButtonItem (customView: backButton)
|
||||||
style: .plain,
|
|
||||||
target: self,
|
//用于消除左边空隙,要不然按钮顶不到最前面
|
||||||
action: #selector(leftBackClicked(_:)))
|
let spacer = UIBarButtonItem (barButtonSystemItem: .fixedSpace, target: nil, action: nil)
|
||||||
navigationItem.leftBarButtonItem = leftBackItem
|
spacer.width = -10
|
||||||
|
navigationItem.leftBarButtonItem = leftBarBtn
|
||||||
|
// navigationItem.leftBarButtonItems = [spacer, leftBarBtn]
|
||||||
|
// let leftBackItem = UIBarButtonItem(image: UIImage(named: "Common/back"),
|
||||||
|
// style: .plain,
|
||||||
|
// target: self, action: #selector(leftBackClicked(_:)))
|
||||||
|
// leftBackItem.tintColor = .white
|
||||||
|
// navigationItem.leftBarButtonItem = leftBackItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy var backButton: UIButton = {
|
||||||
|
let button = UIButton (type: .custom)
|
||||||
|
button.frame = CGRectMake (0, 0, 200, 30)
|
||||||
|
button.setImage(UIImage(named: "Common/back"), for: .normal)
|
||||||
|
button.titleLabel?.font = .systemFont(ofSize: 18, weight: .medium)
|
||||||
|
button.contentHorizontalAlignment = .left
|
||||||
|
button.setTitleColor(ThemeManager.shared.color.titleColor, for: .normal)
|
||||||
|
button.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 验证码
|
// MARK: - 验证码
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ final class MainTabBarController: UITabBarController {
|
||||||
|
|
||||||
viewControllers = navControllers
|
viewControllers = navControllers
|
||||||
tabBar.isHidden = true
|
tabBar.isHidden = true
|
||||||
tabBar.removeFromSuperview()
|
|
||||||
|
|
||||||
for nav in navControllers {
|
for nav in navControllers {
|
||||||
nav.delegate = self
|
nav.delegate = self
|
||||||
|
|
@ -72,7 +71,7 @@ final class MainTabBarController: UITabBarController {
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
customTabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
customTabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
customTabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
customTabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -41),
|
||||||
customTabBar.heightAnchor.constraint(equalToConstant: tabBarHeight)
|
customTabBar.heightAnchor.constraint(equalToConstant: tabBarHeight)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,20 +92,6 @@ class AppContextManager: NSObject {
|
||||||
Defaults[\.loginToken] = token
|
Defaults[\.loginToken] = token
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 状态:0、无状态 1、SOS 2、气泡等
|
|
||||||
public func updateAccountStatus(_ status: Int) {
|
|
||||||
guard var account = self.account else { return }
|
|
||||||
account.status = status
|
|
||||||
saveAccount(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 心情:0、无状态 1、开心 2、愤怒
|
|
||||||
public func updateAccountMood(_ mood: Int) {
|
|
||||||
guard var account = self.account else { return }
|
|
||||||
account.mood = mood
|
|
||||||
saveAccount(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func deleteAccount() {
|
public func deleteAccount() {
|
||||||
Defaults[\.loginToken] = nil
|
Defaults[\.loginToken] = nil
|
||||||
Defaults[\.userConfig] = nil
|
Defaults[\.userConfig] = nil
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,6 @@ struct SystemConfigModel: Mappable {
|
||||||
var popupConfig: PopupWindowConfigModel?
|
var popupConfig: PopupWindowConfigModel?
|
||||||
/// 弹窗时间配置
|
/// 弹窗时间配置
|
||||||
var timeConfig: PopupWindowTimeModel?
|
var timeConfig: PopupWindowTimeModel?
|
||||||
/// 微信分享配置
|
|
||||||
var shareConfig: WxShareConfigModel?
|
|
||||||
|
|
||||||
/// 聊天界面的警告说明
|
/// 聊天界面的警告说明
|
||||||
var chatWarning: String = ""
|
var chatWarning: String = ""
|
||||||
|
|
@ -77,12 +75,6 @@ struct SystemConfigModel: Mappable {
|
||||||
/// 圈子里的banner
|
/// 圈子里的banner
|
||||||
var groupBannerList: [String] = []
|
var groupBannerList: [String] = []
|
||||||
|
|
||||||
/// 引导页显示开关
|
|
||||||
var guideDisplay: Bool = true
|
|
||||||
|
|
||||||
/// 全局控制显示隐藏 弹窗和充值
|
|
||||||
var globalDisplay: Bool = true
|
|
||||||
|
|
||||||
init?(map: Map) {
|
init?(map: Map) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -92,18 +84,15 @@ struct SystemConfigModel: Mappable {
|
||||||
groupBannerList <- map["client.team.ad", nested: false]
|
groupBannerList <- map["client.team.ad", nested: false]
|
||||||
popupConfig <- map["client.popup.display", nested: false]
|
popupConfig <- map["client.popup.display", nested: false]
|
||||||
timeConfig <- map["client.time.setting", nested: false]
|
timeConfig <- map["client.time.setting", nested: false]
|
||||||
shareConfig <- map["client.weixin.share", nested: false]
|
|
||||||
guideDisplay <- map["client.guide_display", nested: false]
|
|
||||||
globalDisplay <- map["client.global_display", nested: false]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PopupWindowConfigModel: Mappable {
|
struct PopupWindowConfigModel: Mappable {
|
||||||
/// 是否弹vip弹窗
|
/// 是否弹vip弹窗
|
||||||
var pay_pop: Bool = true
|
var pay_pop: Bool = false
|
||||||
|
|
||||||
/// 搜索位置弹窗
|
/// 搜索位置弹窗
|
||||||
var search_phone: Bool = true
|
var search_phone: Bool = false
|
||||||
|
|
||||||
|
|
||||||
init?(map: Map) {
|
init?(map: Map) {
|
||||||
|
|
@ -143,28 +132,3 @@ struct PopupWindowTimeModel: Mappable {
|
||||||
pay_pop_type <- map["pay_pop_type"]
|
pay_pop_type <- map["pay_pop_type"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WxShareConfigModel: Mappable {
|
|
||||||
/// 链接
|
|
||||||
var link: String = ""
|
|
||||||
|
|
||||||
/// 标题
|
|
||||||
var title: String = ""
|
|
||||||
|
|
||||||
/// 内容
|
|
||||||
var content: String = ""
|
|
||||||
|
|
||||||
/// 图标
|
|
||||||
var image: String = ""
|
|
||||||
|
|
||||||
init?(map: Map) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func mapping(map: Map) {
|
|
||||||
link <- map["link"]
|
|
||||||
title <- map["title"]
|
|
||||||
content <- map["content"]
|
|
||||||
image <- map["image"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@ import Moya
|
||||||
import SwiftyJSON
|
import SwiftyJSON
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import CommonCrypto
|
import CommonCrypto
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
import GeYanSdk
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class ApiManager: NSObject {
|
class ApiManager: NSObject {
|
||||||
|
|
||||||
|
|
@ -160,8 +157,6 @@ extension ApiManager {
|
||||||
// MARK: - 处理Token过期
|
// MARK: - 处理Token过期
|
||||||
private func handleTokenExpired(msg: String?) {
|
private func handleTokenExpired(msg: String?) {
|
||||||
MainAsync {
|
MainAsync {
|
||||||
NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil)
|
|
||||||
GeYanSdk.deletePreResultCache()
|
|
||||||
AppContextManager.shared.deleteAccount()
|
AppContextManager.shared.deleteAccount()
|
||||||
GroupIMService.shared.logout()
|
GroupIMService.shared.logout()
|
||||||
DLToast.showError(text: msg ?? "登录失效,请重新登录!") {
|
DLToast.showError(text: msg ?? "登录失效,请重新登录!") {
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,6 @@ struct GroupMemberModel: Mappable, Equatable {
|
||||||
}
|
}
|
||||||
return UIImage(named: "VipIcon/\(level)") ?? UIImage()
|
return UIImage(named: "VipIcon/\(level)") ?? UIImage()
|
||||||
}
|
}
|
||||||
/// 状态:0、无状态 1、SOS 2、气泡等
|
|
||||||
var status: Int = 0
|
|
||||||
/// 是否在线
|
/// 是否在线
|
||||||
var is_online: Bool = false
|
var is_online: Bool = false
|
||||||
/// 设备电量
|
/// 设备电量
|
||||||
|
|
@ -230,7 +228,6 @@ struct GroupMemberModel: Mappable, Equatable {
|
||||||
last_position <- map["last_position"]
|
last_position <- map["last_position"]
|
||||||
is_online <- map["is_online"]
|
is_online <- map["is_online"]
|
||||||
last_active_time <- map["last_active_time"]
|
last_active_time <- map["last_active_time"]
|
||||||
status <- map["status"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,6 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.developer.applesignin</key>
|
|
||||||
<array>
|
|
||||||
<string>Default</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.developer.associated-domains</key>
|
<key>com.apple.developer.associated-domains</key>
|
||||||
<array>
|
<array>
|
||||||
<string>applinks:smartdrive.zuom8.cn</string>
|
<string>applinks:smartdrive.zuom8.cn</string>
|
||||||
|
|
|
||||||
|
|
@ -398,9 +398,9 @@ class CreateGroupView: UIView {
|
||||||
lazy var submitBtn: UIButton = {
|
lazy var submitBtn: UIButton = {
|
||||||
let btn = UIButton(type: .custom)
|
let btn = UIButton(type: .custom)
|
||||||
btn.setTitle("创建", for: .normal)
|
btn.setTitle("创建", for: .normal)
|
||||||
btn.setTitleColor(.white, for: .normal)
|
btn.setTitleColor(UIColor(hexStr: "#0F2846"), for: .normal)
|
||||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||||
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
btn.setBackgroundImage(UIImage(named: "Common/gradient_bg"), for: .normal)
|
||||||
btn.cornerRadius = 25
|
btn.cornerRadius = 25
|
||||||
|
|
||||||
return btn
|
return btn
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,6 @@ class CreateGroupViewModel {
|
||||||
}
|
}
|
||||||
}, onError: { (error) in
|
}, onError: { (error) in
|
||||||
guard let code = error.underlyingError?.code else { return }
|
guard let code = error.underlyingError?.code else { return }
|
||||||
|
|
||||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
|
||||||
if code == 20009 { // "您创建的圈子个数已达上限,请升级会员等级"
|
if code == 20009 { // "您创建的圈子个数已达上限,请升级会员等级"
|
||||||
CreateGroupVipPopView.show()
|
CreateGroupVipPopView.show()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,7 @@ final class GroupChatVC: BaseViewController {
|
||||||
|
|
||||||
// 并行加载:业务接口 + IM SDK 互不依赖,同时发起
|
// 并行加载:业务接口 + IM SDK 互不依赖,同时发起
|
||||||
requestGroupInfoByKey()
|
requestGroupInfoByKey()
|
||||||
GroupIMService.shared.ensureLogin { [weak self] success in
|
viewModel.loadMessages()
|
||||||
guard let self = self, success else { return }
|
|
||||||
self.viewModel.loadMessages()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理系统配置
|
// 处理系统配置
|
||||||
guard let config = AppContextManager.shared.systemConfig else { return }
|
guard let config = AppContextManager.shared.systemConfig else { return }
|
||||||
|
|
@ -99,7 +96,7 @@ final class GroupChatVC: BaseViewController {
|
||||||
// 收起表情/语音面板
|
// 收起表情/语音面板
|
||||||
self.rootView.dismissAllPanels(excludeTextField: true)
|
self.rootView.dismissAllPanels(excludeTextField: true)
|
||||||
UIView.animate(withDuration: duration) {
|
UIView.animate(withDuration: duration) {
|
||||||
self.rootView.bottomBar.layoutChain.bottom(height + 20)
|
self.rootView.bottomBar.layoutChain.bottom(height + kSafeBottomMargin + 20)
|
||||||
}
|
}
|
||||||
self.scrollToBottom()
|
self.scrollToBottom()
|
||||||
})
|
})
|
||||||
|
|
@ -154,7 +151,7 @@ final class GroupChatVC: BaseViewController {
|
||||||
private func scrollToBottom() {
|
private func scrollToBottom() {
|
||||||
let count = dataSource.sectionModels.first?.items.count ?? 0
|
let count = dataSource.sectionModels.first?.items.count ?? 0
|
||||||
guard count > 0 else { return }
|
guard count > 0 else { return }
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
self.rootView.tableView.layoutIfNeeded()
|
self.rootView.tableView.layoutIfNeeded()
|
||||||
let indexPath = IndexPath(row: count - 1, section: 0)
|
let indexPath = IndexPath(row: count - 1, section: 0)
|
||||||
self.rootView.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
|
self.rootView.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
|
||||||
|
|
@ -295,7 +292,7 @@ final class GroupChatVC: BaseViewController {
|
||||||
let offset: CGFloat = show ? 252 : 0
|
let offset: CGFloat = show ? 252 : 0
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
self.rootView.bottomBar.layoutChain.bottom(show ? offset - self.rootView.bottomBar.dl.height : kSafeBottomMargin + 20)
|
self.rootView.bottomBar.layoutChain.bottom(show ? offset - self.rootView.bottomBar.dl.height : kSafeBottomMargin + 20)
|
||||||
self.rootView.voiceRecordView.layoutChain.bottom(offset - 252)
|
self.rootView.voiceRecordView.layoutChain.bottom(offset - 252 + kSafeBottomMargin)
|
||||||
}
|
}
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +306,7 @@ final class GroupChatVC: BaseViewController {
|
||||||
self.rootView.emojiPanelView.isHidden = !show
|
self.rootView.emojiPanelView.isHidden = !show
|
||||||
let offset: CGFloat = show ? 220 : 0
|
let offset: CGFloat = show ? 220 : 0
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
self.rootView.bottomBar.layoutChain.bottom(20 + offset)
|
self.rootView.bottomBar.layoutChain.bottom(kSafeBottomMargin + 20 + offset)
|
||||||
}
|
}
|
||||||
// completion: { _ in
|
// completion: { _ in
|
||||||
// let offset: CGFloat = self.rootView.tableView.contentSize.height
|
// let offset: CGFloat = self.rootView.tableView.contentSize.height
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,6 @@ class GroupMemberListVC: BaseViewController {
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
|
||||||
rootView.unlockVipMaskView.isHidden = AppContextManager.shared.vip > 1
|
rootView.unlockVipMaskView.isHidden = AppContextManager.shared.vip > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -220,9 +220,7 @@ final class GroupViewController: BaseViewController {
|
||||||
GroupService.groupInfo().subscribe { response in
|
GroupService.groupInfo().subscribe { response in
|
||||||
guard let model = response.model else { return }
|
guard let model = response.model else { return }
|
||||||
self.viewModel.groupList = model.groups
|
self.viewModel.groupList = model.groups
|
||||||
DispatchQueue.global(qos: .default).async { [weak self] in
|
self.loadIMData()
|
||||||
self?.loadIMData()
|
|
||||||
}
|
|
||||||
}.disposed(by: disposeBag)
|
}.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import UIKit
|
||||||
import ObjectMapper
|
import ObjectMapper
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import Kingfisher
|
|
||||||
|
|
||||||
class InviteJoinVC: BaseViewController {
|
class InviteJoinVC: BaseViewController {
|
||||||
|
|
||||||
|
|
@ -29,64 +28,8 @@ class InviteJoinVC: BaseViewController {
|
||||||
|
|
||||||
// 分享
|
// 分享
|
||||||
rootView.shareAppBtn.rx.tap.subscribe(onNext: { _ in
|
rootView.shareAppBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
SharePopView.show()
|
let url = "https://smartdrive.zuom8.cn/jisu/share/user/"
|
||||||
}).disposed(by: disposeBag)
|
SharePopView.show(shareURL: url)
|
||||||
|
|
||||||
// 分享邀请码
|
|
||||||
rootView.shareCodeBtn.rx.tap.subscribe(onNext: { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard let config = AppContextManager.shared.systemConfig,
|
|
||||||
let shareConfig = config.shareConfig else { return }
|
|
||||||
|
|
||||||
DLToast.showLoading()
|
|
||||||
|
|
||||||
let fallbackImage = UIImage(named: "Common/logo108")
|
|
||||||
let imageURL: URL? = {
|
|
||||||
if shareConfig.image.isEmpty { return nil }
|
|
||||||
return URL(string: shareConfig.image)
|
|
||||||
}()
|
|
||||||
|
|
||||||
let sendMessage: (UIImage?) -> Void = { [weak self] thumbImage in
|
|
||||||
guard let self = self else { return }
|
|
||||||
DLToast.dismiss()
|
|
||||||
|
|
||||||
guard let screenshot = self.rootView.inviteInfoView.takeScreenshot(),
|
|
||||||
let imageData = screenshot.pngData() else {
|
|
||||||
DLToast.showError(text: "分享失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let message = WXMediaMessage()
|
|
||||||
message.title = "邀请加入圈子"
|
|
||||||
message.description = shareConfig.content
|
|
||||||
if let thumb = thumbImage {
|
|
||||||
message.setThumbImage(thumb)
|
|
||||||
}
|
|
||||||
|
|
||||||
let imageObject = WXImageObject()
|
|
||||||
imageObject.imageData = imageData
|
|
||||||
message.mediaObject = imageObject
|
|
||||||
|
|
||||||
let req = SendMessageToWXReq()
|
|
||||||
req.bText = false
|
|
||||||
req.message = message
|
|
||||||
req.scene = 0
|
|
||||||
|
|
||||||
WXApi.send(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let url = imageURL {
|
|
||||||
KingfisherManager.shared.retrieveImage(with: url) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let value):
|
|
||||||
sendMessage(value.image)
|
|
||||||
case .failure:
|
|
||||||
sendMessage(fallbackImage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sendMessage(fallbackImage)
|
|
||||||
}
|
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
guard let model = groupInModel else { return }
|
guard let model = groupInModel else { return }
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class InviteJoinView: UIView {
|
||||||
inviteInfoView.layoutChain
|
inviteInfoView.layoutChain
|
||||||
.topToBottomOfView(groupIcon, offset: 50)
|
.topToBottomOfView(groupIcon, offset: 50)
|
||||||
.edgesHorzontal(30)
|
.edgesHorzontal(30)
|
||||||
// .bottomToTopOfView(shareAppBtn, offset: -32)
|
.bottomToTopOfView(shareAppBtn, offset: -32)
|
||||||
|
|
||||||
inviteInfoTitleLab.layoutChain
|
inviteInfoTitleLab.layoutChain
|
||||||
.top(20)
|
.top(20)
|
||||||
|
|
@ -265,9 +265,10 @@ class InviteJoinView: UIView {
|
||||||
lazy var shareCodeBtn: UIButton = {
|
lazy var shareCodeBtn: UIButton = {
|
||||||
let btn = UIButton(type: .custom)
|
let btn = UIButton(type: .custom)
|
||||||
btn.setTitle("分享邀请码", for: .normal)
|
btn.setTitle("分享邀请码", for: .normal)
|
||||||
btn.setTitleColor(.white, for: .normal)
|
|
||||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||||
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
btn.setTitleColor(UIColor(hexStr: "#0F2846"), for: .normal)
|
||||||
|
btn.backgroundColor = .clear
|
||||||
|
btn.setBackgroundImage(UIImage(named: "Common/gradient_bg"), for: .normal)
|
||||||
btn.cornerRadius = 25
|
btn.cornerRadius = 25
|
||||||
return btn
|
return btn
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import SwiftyUserDefaults
|
|
||||||
|
|
||||||
class CreateBubbleVC: BaseViewController {
|
class CreateBubbleVC: BaseViewController {
|
||||||
|
|
||||||
|
|
@ -32,13 +31,6 @@ class CreateBubbleVC: BaseViewController {
|
||||||
|
|
||||||
doneBtn.rx.tap.subscribe(onNext: { [weak self] in
|
doneBtn.rx.tap.subscribe(onNext: { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else {
|
|
||||||
let hours = self.rootView.createBubbleTiemView.selectedHour.value
|
|
||||||
requestSetBubble(enable: true, keep_time: hours)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if AppContextManager.shared.vip > 1 {
|
if AppContextManager.shared.vip > 1 {
|
||||||
let hours = self.rootView.createBubbleTiemView.selectedHour.value
|
let hours = self.rootView.createBubbleTiemView.selectedHour.value
|
||||||
requestSetBubble(enable: true, keep_time: hours)
|
requestSetBubble(enable: true, keep_time: hours)
|
||||||
|
|
@ -57,13 +49,6 @@ class CreateBubbleVC: BaseViewController {
|
||||||
self.requestSetBubble(enable: false, keep_time: -1)
|
self.requestSetBubble(enable: false, keep_time: -1)
|
||||||
}, cancelText: "取消")
|
}, cancelText: "取消")
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
guard AppContextManager.shared.status == 2, let endDate = Defaults[\.bubbleKeepTimes] else { return }
|
|
||||||
self.rootView.navTitleLabel.text = "活动气泡"
|
|
||||||
self.rootView.createBubbleDoneView.messageLab.text = self.rootView.createBubbleTipsView.messageText
|
|
||||||
self.rootView.createBubbleTipsView.isHidden = true
|
|
||||||
self.rootView.createBubbleDoneView.isHidden = false
|
|
||||||
self.rootView.createBubbleDoneView.startCountdown(endDate: endDate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestSetBubble(enable: Bool, keep_time: Int) {
|
private func requestSetBubble(enable: Bool, keep_time: Int) {
|
||||||
|
|
@ -72,17 +57,13 @@ class CreateBubbleVC: BaseViewController {
|
||||||
DLToast.dismiss()
|
DLToast.dismiss()
|
||||||
guard enable else {
|
guard enable else {
|
||||||
DLToast.show(text: "气泡已弹出") {
|
DLToast.show(text: "气泡已弹出") {
|
||||||
Defaults[\.bubbleKeepTimes] = nil
|
|
||||||
AppContextManager.shared.updateAccountStatus(0)
|
|
||||||
AppRouter.shared.popOrDismiss()
|
AppRouter.shared.popOrDismiss()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
AppContextManager.shared.updateAccountStatus(2)
|
|
||||||
self.rootView.navTitleLabel.text = "活动气泡"
|
self.rootView.navTitleLabel.text = "活动气泡"
|
||||||
self.rootView.createBubbleDoneView.messageLab.text = self.rootView.createBubbleTipsView.messageText
|
self.rootView.createBubbleDoneView.messageLab.text = self.rootView.createBubbleTipsView.messageText
|
||||||
let endDate = Calendar.current.date(byAdding: .hour, value: keep_time, to: Date()) ?? Date()
|
let endDate = Calendar.current.date(byAdding: .hour, value: keep_time, to: Date()) ?? Date()
|
||||||
Defaults[\.bubbleKeepTimes] = endDate
|
|
||||||
self.rootView.createBubbleDoneView.startCountdown(endDate: endDate)
|
self.rootView.createBubbleDoneView.startCountdown(endDate: endDate)
|
||||||
self.rootView.createBubbleTipsView.isHidden = true
|
self.rootView.createBubbleTipsView.isHidden = true
|
||||||
self.rootView.createBubbleDoneView.isHidden = false
|
self.rootView.createBubbleDoneView.isHidden = false
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import UIKit
|
||||||
import UIKit
|
import UIKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import Lottie
|
|
||||||
|
|
||||||
class GroupMemberView: UIView {
|
class GroupMemberView: UIView {
|
||||||
|
|
||||||
|
|
@ -185,13 +184,6 @@ class GroupMemberCell: UITableViewCell {
|
||||||
onlineStatusLab.text = model.lastUpdateText
|
onlineStatusLab.text = model.lastUpdateText
|
||||||
onlineStatusLab.textColor = UIColor(hexStr: "#999999")
|
onlineStatusLab.textColor = UIColor(hexStr: "#999999")
|
||||||
}
|
}
|
||||||
|
|
||||||
// sos
|
|
||||||
sosLottieView.isHidden = model.status != 1
|
|
||||||
model.status == 1 ? sosLottieView.play() : sosLottieView.stop()
|
|
||||||
|
|
||||||
// 气泡
|
|
||||||
shieldIconView.isHidden = model.status != 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSubviews() {
|
private func setupSubviews() {
|
||||||
|
|
@ -271,7 +263,7 @@ class GroupMemberCell: UITableViewCell {
|
||||||
tagView.layoutChain
|
tagView.layoutChain
|
||||||
.leftToRightOfView(nameLab, offset: 4)
|
.leftToRightOfView(nameLab, offset: 4)
|
||||||
.centerY(nameLab)
|
.centerY(nameLab)
|
||||||
.height(30)
|
.height(20)
|
||||||
|
|
||||||
ownView.layoutChain.height(15)
|
ownView.layoutChain.height(15)
|
||||||
|
|
||||||
|
|
@ -362,10 +354,10 @@ class GroupMemberCell: UITableViewCell {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var tagView: UIStackView = {
|
lazy var tagView: UIStackView = {
|
||||||
let view = UIStackView(arrangedSubviews: [ownView, offlineView, sosLottieView, shieldIconView])
|
let view = UIStackView(arrangedSubviews: [ownView, offlineView])
|
||||||
view.axis = .horizontal
|
view.axis = .horizontal
|
||||||
view.alignment = .center
|
view.alignment = .center
|
||||||
view.spacing = 5
|
view.spacing = 0
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
@ -409,28 +401,6 @@ class GroupMemberCell: UITableViewCell {
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// sos
|
|
||||||
lazy var sosLottieView: LottieAnimationView = {
|
|
||||||
let view = LottieAnimationView(name: "siren")
|
|
||||||
view.loopMode = .loop
|
|
||||||
view.isHidden = true
|
|
||||||
|
|
||||||
view.layoutChain.width(26).height(26)
|
|
||||||
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 气泡(盾牌)
|
|
||||||
lazy var shieldIconView: UIImageView = {
|
|
||||||
let view = UIImageView(image: UIImage(named: "Home/shield"))
|
|
||||||
view.contentMode = .scaleAspectFill
|
|
||||||
view.isHidden = true
|
|
||||||
|
|
||||||
view.layoutChain.width(20).height(20)
|
|
||||||
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 在线
|
// 在线
|
||||||
lazy var onlineView: UIView = {
|
lazy var onlineView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class HomeView: UIView {
|
||||||
mv.showsScale = false
|
mv.showsScale = false
|
||||||
mv.isRotateEnabled = false
|
mv.isRotateEnabled = false
|
||||||
mv.isRotateCameraEnabled = false
|
mv.isRotateCameraEnabled = false
|
||||||
DispatchQueue.main.async { mv.logoCenter = CGPoint(x: kScreenWidth - 50, y: kScreenHeight / 2 + 65) }
|
DispatchQueue.main.async { mv.logoCenter = CGPoint(x: 15, y: mv.bounds.height / 2) }
|
||||||
return mv
|
return mv
|
||||||
}()
|
}()
|
||||||
#else
|
#else
|
||||||
|
|
@ -92,6 +92,7 @@ class HomeView: UIView {
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
||||||
groupMemberView.tableView.rx.setDelegate(self)
|
groupMemberView.tableView.rx.setDelegate(self)
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
@ -99,11 +100,6 @@ class HomeView: UIView {
|
||||||
searchLottieView.rx.tapGesture.subscribe { _ in
|
searchLottieView.rx.tapGesture.subscribe { _ in
|
||||||
AppRouter.push(Route.searchLocation)
|
AppRouter.push(Route.searchLocation)
|
||||||
}.disposed(by: disposeBag)
|
}.disposed(by: disposeBag)
|
||||||
|
|
||||||
// 感叹号
|
|
||||||
warningIconView.rx.tapGesture.subscribe { _ in
|
|
||||||
LocationPermissionPopView.show()
|
|
||||||
}.disposed(by: disposeBag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupUI() {
|
private func setupUI() {
|
||||||
|
|
@ -122,7 +118,6 @@ class HomeView: UIView {
|
||||||
addSubview(messageView)
|
addSubview(messageView)
|
||||||
addSubview(messageStackView)
|
addSubview(messageStackView)
|
||||||
addSubview(toolsView)
|
addSubview(toolsView)
|
||||||
addSubview(warningIconView)
|
|
||||||
addSubview(searchLottieView)
|
addSubview(searchLottieView)
|
||||||
addSubview(locationView)
|
addSubview(locationView)
|
||||||
locationView.addSubview(locationIconView)
|
locationView.addSubview(locationIconView)
|
||||||
|
|
@ -188,12 +183,6 @@ class HomeView: UIView {
|
||||||
.width(40)
|
.width(40)
|
||||||
.centerY(self, offset: -50)
|
.centerY(self, offset: -50)
|
||||||
|
|
||||||
warningIconView.layoutChain
|
|
||||||
.bottomToTopOfView(toolsView, offset: -10)
|
|
||||||
.centerX(toolsView)
|
|
||||||
.width(28)
|
|
||||||
.heightToWidth(1)
|
|
||||||
|
|
||||||
searchLottieView.layoutChain
|
searchLottieView.layoutChain
|
||||||
.centerY(toolsView)
|
.centerY(toolsView)
|
||||||
.right()
|
.right()
|
||||||
|
|
@ -202,7 +191,7 @@ class HomeView: UIView {
|
||||||
|
|
||||||
locationView.layoutChain
|
locationView.layoutChain
|
||||||
.right(15)
|
.right(15)
|
||||||
.bottomToView(toolsView, offset: -10)
|
.bottomToView(toolsView)
|
||||||
.width(40)
|
.width(40)
|
||||||
.height(40)
|
.height(40)
|
||||||
|
|
||||||
|
|
@ -247,7 +236,6 @@ class HomeView: UIView {
|
||||||
quickMessageView.isHidden = false
|
quickMessageView.isHidden = false
|
||||||
interactionView.isHidden = false
|
interactionView.isHidden = false
|
||||||
toolsView.isHidden = true
|
toolsView.isHidden = true
|
||||||
searchLottieView.isHidden = true
|
|
||||||
quickMessageView.frame.origin.y = kScreenHeight - 384 - 93
|
quickMessageView.frame.origin.y = kScreenHeight - 384 - 93
|
||||||
interactionView.frame.origin.y = kScreenHeight - 384
|
interactionView.frame.origin.y = kScreenHeight - 384
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +246,6 @@ class HomeView: UIView {
|
||||||
quickMessageView.isHidden = true
|
quickMessageView.isHidden = true
|
||||||
interactionView.isHidden = true
|
interactionView.isHidden = true
|
||||||
toolsView.isHidden = false
|
toolsView.isHidden = false
|
||||||
searchLottieView.isHidden = AppContextManager.shared.systemConfig?.globalDisplay == false
|
|
||||||
onDismissPanel?()
|
onDismissPanel?()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -648,15 +635,6 @@ class HomeView: UIView {
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: - warning 惊叹号图标
|
|
||||||
lazy var warningIconView: UIImageView = {
|
|
||||||
let view = UIImageView()
|
|
||||||
view.image = UIImage(named: "Home/warning")
|
|
||||||
view.contentMode = .scaleAspectFill
|
|
||||||
view.isHidden = true
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
// MARK: - 侧边工具栏
|
// MARK: - 侧边工具栏
|
||||||
lazy var toolsView: UIStackView = {
|
lazy var toolsView: UIStackView = {
|
||||||
let view = UIStackView(arrangedSubviews: [bubbleView, signInView, sosView, scheduleView])
|
let view = UIStackView(arrangedSubviews: [bubbleView, signInView, sosView, scheduleView])
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,13 @@ class HomeViewController: BaseViewController {
|
||||||
private var subscribedMemberIds: [String] = []
|
private var subscribedMemberIds: [String] = []
|
||||||
/// 底部成员面板是否显示
|
/// 底部成员面板是否显示
|
||||||
private var isMemberPanelShown = false
|
private var isMemberPanelShown = false
|
||||||
private var isSOSPlaying = false
|
|
||||||
/// 记录每个成员最后一次收到位置的时间(MQTT userId → Date)
|
/// 记录每个成员最后一次收到位置的时间(MQTT userId → Date)
|
||||||
private var lastUpdateTimes: [String: Date] = [:]
|
private var lastUpdateTimes: [String: Date] = [:]
|
||||||
/// 离线检测定时器
|
/// 离线检测定时器
|
||||||
private var offlineCheckTimer: Timer?
|
private var offlineCheckTimer: Timer?
|
||||||
private var popupQueue: PopupQueueManager?
|
private var popupQueue: PopupQueueManager?
|
||||||
|
|
||||||
private static var sosPlayerKey: UInt8 = 0
|
private var sosPlayerKey: UInt8 = 0
|
||||||
private var groupRefreshWorkItem: DispatchWorkItem?
|
private var groupRefreshWorkItem: DispatchWorkItem?
|
||||||
private var groupSwitchWorkItem: DispatchWorkItem?
|
private var groupSwitchWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
|
|
@ -74,6 +73,8 @@ class HomeViewController: BaseViewController {
|
||||||
setupHeading()
|
setupHeading()
|
||||||
reactiveAction()
|
reactiveAction()
|
||||||
|
|
||||||
|
requestUserConfig()
|
||||||
|
|
||||||
rootView.quickMessageView.tagListView.delegate = self
|
rootView.quickMessageView.tagListView.delegate = self
|
||||||
// MQTT 位置上报
|
// MQTT 位置上报
|
||||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||||
|
|
@ -83,13 +84,6 @@ class HomeViewController: BaseViewController {
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
let manager = AuthorizeManager.manager(type: .locationAlways)
|
|
||||||
rootView.warningIconView.isHidden = manager?.authorizeStatus() == .authorized
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidDisappear(_ animated: Bool) {
|
|
||||||
super.viewDidDisappear(animated)
|
|
||||||
// sosPlayer?.stop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MQTT 位置上报
|
// MARK: - MQTT 位置上报
|
||||||
|
|
@ -195,27 +189,9 @@ class HomeViewController: BaseViewController {
|
||||||
|
|
||||||
// User Config刷新
|
// User Config刷新
|
||||||
NotificationCenter.default.rx.notification(.RefreshUserConfigNotification, object: nil)
|
NotificationCenter.default.rx.notification(.RefreshUserConfigNotification, object: nil)
|
||||||
.startWith(Notification(name: .RefreshUserConfigNotification))
|
.subscribe { [weak self] notification in
|
||||||
.do(onNext: { [weak self] _ in
|
self?.requestUserConfig()
|
||||||
self?.popupQueue = nil
|
}.disposed(by: disposeBag)
|
||||||
})
|
|
||||||
.flatMapLatest { _ in
|
|
||||||
SystemService.userConfig()
|
|
||||||
}
|
|
||||||
.subscribe(onNext: { [weak self] response in
|
|
||||||
guard let self = self, let model = response.model else { return }
|
|
||||||
Defaults[\.loginToken] = model.token
|
|
||||||
AppContextManager.shared.systemConfig = model.config
|
|
||||||
rootView.searchLottieView.isHidden = model.config?.globalDisplay == false
|
|
||||||
self.getUserIMToken()
|
|
||||||
self.requestUserInfo { [weak self] in
|
|
||||||
self?.requestGroupInfo()
|
|
||||||
}
|
|
||||||
self.requestNotice()
|
|
||||||
self.popupQueue = nil
|
|
||||||
self.popupQueue = PopupQueueManager()
|
|
||||||
self.popupQueue?.start(from: self)
|
|
||||||
}).disposed(by: disposeBag)
|
|
||||||
|
|
||||||
// 圈子刷新
|
// 圈子刷新
|
||||||
NotificationCenter.default.rx.notification(.RefreshGroupInfoNotification, object: nil)
|
NotificationCenter.default.rx.notification(.RefreshGroupInfoNotification, object: nil)
|
||||||
|
|
@ -272,8 +248,10 @@ class HomeViewController: BaseViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分享
|
// 分享
|
||||||
rootView.interactionView.onShare = {
|
rootView.interactionView.onShare = { [weak self] in
|
||||||
SharePopView.show()
|
guard let self = self, let member = self.rootView.interactionView.currentMember else { return }
|
||||||
|
let url = "https://smartdrive.zuom8.cn/jisu/share/user/\(member.id)"
|
||||||
|
SharePopView.show(shareURL: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,6 +290,25 @@ class HomeViewController: BaseViewController {
|
||||||
|
|
||||||
// MARK: - API
|
// MARK: - API
|
||||||
|
|
||||||
|
/// 获取用户配置
|
||||||
|
func requestUserConfig() {
|
||||||
|
SystemService.userConfig().subscribe(onNext: { response in
|
||||||
|
guard let model = response.model else { return }
|
||||||
|
Defaults[\.loginToken] = model.token
|
||||||
|
AppContextManager.shared.systemConfig = model.config
|
||||||
|
self.getUserIMToken()
|
||||||
|
// 先更新用户信息(含头像),再拉群列表同步地图标注,避免头像旧
|
||||||
|
self.requestUserInfo { [weak self] in
|
||||||
|
self?.requestGroupInfo()
|
||||||
|
}
|
||||||
|
// 首页公告
|
||||||
|
self.requestNotice()
|
||||||
|
// 弹窗控制器
|
||||||
|
self.popupQueue = PopupQueueManager()
|
||||||
|
self.popupQueue?.start(from: self)
|
||||||
|
}).disposed(by: disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取用户IM Token
|
/// 获取用户IM Token
|
||||||
func getUserIMToken() {
|
func getUserIMToken() {
|
||||||
DLToast.showLoading()
|
DLToast.showLoading()
|
||||||
|
|
@ -469,8 +466,8 @@ class HomeViewController: BaseViewController {
|
||||||
let newMembers = others + [me]
|
let newMembers = others + [me]
|
||||||
members = newMembers
|
members = newMembers
|
||||||
currentUserMember = me
|
currentUserMember = me
|
||||||
// 从 members 中过滤出在线成员用于地图标注(当前用户坐标无效,由 GPS 回调单独添加)
|
// 从 members 中过滤出在线成员用于地图标注
|
||||||
let onlineMembers = newMembers.filter { $0.isOnline && !$0.isCurrentUser }
|
let onlineMembers = newMembers.filter { $0.isOnline }
|
||||||
|
|
||||||
guard let mapView = rootView.mapView else { return }
|
guard let mapView = rootView.mapView else { return }
|
||||||
|
|
||||||
|
|
@ -480,33 +477,10 @@ class HomeViewController: BaseViewController {
|
||||||
|
|
||||||
let toRemove = existing.filter { !onlineIDs.contains($0.member.id) }
|
let toRemove = existing.filter { !onlineIDs.contains($0.member.id) }
|
||||||
mapView.removeAnnotations(toRemove)
|
mapView.removeAnnotations(toRemove)
|
||||||
if toRemove.contains(where: { $0 === currentUserAnnotation }) {
|
|
||||||
currentUserAnnotation = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let toAdd = onlineMembers.filter { !existingIDs.contains($0.id) }
|
let toAdd = onlineMembers.filter { !existingIDs.contains($0.id) }
|
||||||
let annotations = toAdd.map { MemberAnnotation(member: $0) }
|
let annotations = toAdd.map { MemberAnnotation(member: $0) }
|
||||||
mapView.addAnnotations(annotations)
|
mapView.addAnnotations(annotations)
|
||||||
|
|
||||||
if currentUserAnnotation == nil,
|
|
||||||
let me = currentUserMember,
|
|
||||||
let loc = lastLocation,
|
|
||||||
CLLocationCoordinate2DIsValid(loc.coordinate) {
|
|
||||||
let updatedMe = CircleMember(
|
|
||||||
id: "current",
|
|
||||||
name: me.name,
|
|
||||||
avatar: me.avatar,
|
|
||||||
isOnline: true,
|
|
||||||
isOwner: false,
|
|
||||||
coordinate: loc.coordinate,
|
|
||||||
address: me.address,
|
|
||||||
heading: me.heading,
|
|
||||||
lastUpdateTime: me.lastUpdateTime,
|
|
||||||
battery: me.battery
|
|
||||||
)
|
|
||||||
currentUserMember = updatedMe
|
|
||||||
mapView.addAnnotation(MemberAnnotation(member: updatedMe))
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -664,37 +638,17 @@ extension HomeViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "bubble": // 气泡
|
|
||||||
guard let userId = msg.data?.user_id else { return }
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
self?.viewModel.updateMemberStatus(userId: userId, status: 2)
|
|
||||||
self?.removeAnnotation(userId: userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "sos": // 求助
|
case "sos": // 求助
|
||||||
|
self.requestGroupInfo(isDefaultGroup: false)
|
||||||
guard let userId = msg.data?.user_id,
|
guard let userId = msg.data?.user_id,
|
||||||
userId != AppContextManager.shared.userId,
|
userId != AppContextManager.shared.userId else { return }
|
||||||
topic.contains(userId) else { return }
|
playSOSAlarm()
|
||||||
DispatchQueue.main.async { [weak self] in
|
rootView.sosPopView.isHidden = false
|
||||||
guard let self = self else { return }
|
rootView.sosPopView.play(toFrame: 30.0) { completed in
|
||||||
self.viewModel.updateMemberStatus(userId: userId, status: 1)
|
guard completed else { return }
|
||||||
|
|
||||||
guard !self.isSOSPlaying else { return }
|
|
||||||
self.isSOSPlaying = true
|
|
||||||
self.playSOSAlarm()
|
|
||||||
self.rootView.sosPopView.isHidden = false
|
|
||||||
self.rootView.sosPopView.play { completed in
|
|
||||||
self.rootView.sosPopView.stop()
|
self.rootView.sosPopView.stop()
|
||||||
self.rootView.sosPopView.isHidden = true
|
self.rootView.sosPopView.isHidden = true
|
||||||
}
|
}
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak self] in
|
|
||||||
self?.sosPlayer?.stop()
|
|
||||||
self?.sosPlayer = nil
|
|
||||||
self?.rootView.sosPopView.stop()
|
|
||||||
self?.rootView.sosPopView.isHidden = true
|
|
||||||
self?.isSOSPlaying = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "join": // 圈子有成员加入
|
case "join": // 圈子有成员加入
|
||||||
guard let gk = msg.data?.group_key,
|
guard let gk = msg.data?.group_key,
|
||||||
let defaultGk = self.viewModel.groupModel?.default_group_key,
|
let defaultGk = self.viewModel.groupModel?.default_group_key,
|
||||||
|
|
@ -844,22 +798,6 @@ extension HomeViewController: MAMapViewDelegate {
|
||||||
|
|
||||||
if let ann = currentUserAnnotation {
|
if let ann = currentUserAnnotation {
|
||||||
ann.coordinate = coordinate
|
ann.coordinate = coordinate
|
||||||
} else if let me = currentUserMember {
|
|
||||||
let updatedMe = CircleMember(
|
|
||||||
id: "current",
|
|
||||||
name: me.name,
|
|
||||||
avatar: me.avatar,
|
|
||||||
isOnline: true,
|
|
||||||
isOwner: false,
|
|
||||||
coordinate: coordinate,
|
|
||||||
address: me.address,
|
|
||||||
heading: me.heading,
|
|
||||||
lastUpdateTime: me.lastUpdateTime,
|
|
||||||
battery: me.battery
|
|
||||||
)
|
|
||||||
currentUserMember = updatedMe
|
|
||||||
let annotation = MemberAnnotation(member: updatedMe)
|
|
||||||
mapView.addAnnotation(annotation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isMemberPanelShown {
|
if !isMemberPanelShown {
|
||||||
|
|
@ -873,8 +811,8 @@ extension HomeViewController: MAMapViewDelegate {
|
||||||
extension HomeViewController {
|
extension HomeViewController {
|
||||||
|
|
||||||
private var sosPlayer: AVAudioPlayer? {
|
private var sosPlayer: AVAudioPlayer? {
|
||||||
get { return objc_getAssociatedObject(self, &HomeViewController.sosPlayerKey) as? AVAudioPlayer }
|
get { return objc_getAssociatedObject(self, &sosPlayerKey) as? AVAudioPlayer }
|
||||||
set { objc_setAssociatedObject(self, &HomeViewController.sosPlayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
|
set { objc_setAssociatedObject(self, &sosPlayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func playSOSAlarm() {
|
private func playSOSAlarm() {
|
||||||
|
|
@ -897,10 +835,14 @@ extension HomeViewController {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
print("✅ SOS: playing alarm")
|
print("✅ SOS: playing alarm")
|
||||||
player.numberOfLoops = -1
|
player.numberOfLoops = 0
|
||||||
player.volume = 1.0
|
player.volume = 1.0
|
||||||
player.play()
|
player.play()
|
||||||
self.sosPlayer = player
|
self.sosPlayer = player
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 15) { [weak self] in
|
||||||
|
self?.sosPlayer?.stop()
|
||||||
|
self?.sosPlayer = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,12 +144,6 @@ class HomeViewModel {
|
||||||
setMemberList(updated)
|
setMemberList(updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMemberStatus(userId: String, status: Int) {
|
|
||||||
guard let index = memberList.firstIndex(where: { $0.user_id == userId }) else { return }
|
|
||||||
memberList[index].status = status
|
|
||||||
sectionedItems.onNext(memberList.mapSection())
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
init() {
|
init() {
|
||||||
output = Output(
|
output = Output(
|
||||||
|
|
|
||||||
|
|
@ -108,10 +108,7 @@ class InteractionView: UIView {
|
||||||
let pages = (items.count + InteractionView.emojiPerPage - 1) / InteractionView.emojiPerPage
|
let pages = (items.count + InteractionView.emojiPerPage - 1) / InteractionView.emojiPerPage
|
||||||
self.emojiPageControl.numberOfPages = max(pages, 1)
|
self.emojiPageControl.numberOfPages = max(pages, 1)
|
||||||
self.emojiPageControl.currentPage = 0
|
self.emojiPageControl.currentPage = 0
|
||||||
self.emojiCollectionView.layoutIfNeeded()
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.emojiCollectionView.setContentOffset(.zero, animated: false)
|
self.emojiCollectionView.setContentOffset(.zero, animated: false)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
@ -520,11 +517,6 @@ extension InteractionView: UICollectionViewDelegate {
|
||||||
emojiPageControl.currentPage = page
|
emojiPageControl.currentPage = page
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
|
||||||
guard collectionView == emojiCollectionView else { return }
|
|
||||||
(cell as? EmojiPanelCell)?.playAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||||||
guard collectionView == emojiCollectionView else { return }
|
guard collectionView == emojiCollectionView else { return }
|
||||||
(cell as? EmojiPanelCell)?.stopAnimation()
|
(cell as? EmojiPanelCell)?.stopAnimation()
|
||||||
|
|
@ -536,46 +528,29 @@ final class EmojiPanelCell: UICollectionViewCell {
|
||||||
|
|
||||||
private static var animationCache: [String: LottieAnimation] = [:]
|
private static var animationCache: [String: LottieAnimation] = [:]
|
||||||
private static var loadingCompletions: [String: [(LottieAnimation?) -> Void]] = [:]
|
private static var loadingCompletions: [String: [(LottieAnimation?) -> Void]] = [:]
|
||||||
private static let cacheLock = NSLock()
|
private static let animationQueue = DispatchQueue(label: "com.quicklocation.emoji.animation", qos: .userInitiated)
|
||||||
private static let animationQueue = DispatchQueue(label: "com.quicklocation.emoji.animation", qos: .userInitiated, attributes: .concurrent)
|
|
||||||
|
|
||||||
private static let emojiPathMap: [String: String] = {
|
|
||||||
Bundle.main.paths(forResourcesOfType: "json", inDirectory: nil).reduce(into: [:]) { map, path in
|
|
||||||
let name = (path as NSString).lastPathComponent.replacingOccurrences(of: ".json", with: "")
|
|
||||||
map[name] = path
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
static func loadAnimation(for name: String, completion: @escaping (LottieAnimation?) -> Void) {
|
static func loadAnimation(for name: String, completion: @escaping (LottieAnimation?) -> Void) {
|
||||||
animationQueue.async {
|
animationQueue.async {
|
||||||
Self.cacheLock.lock()
|
if let cached = animationCache[name] {
|
||||||
if let cached = Self.animationCache[name] {
|
|
||||||
Self.cacheLock.unlock()
|
|
||||||
DispatchQueue.main.async { completion(cached) }
|
DispatchQueue.main.async { completion(cached) }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if Self.loadingCompletions[name] != nil {
|
if loadingCompletions[name] != nil {
|
||||||
Self.loadingCompletions[name]?.append(completion)
|
loadingCompletions[name]?.append(completion)
|
||||||
Self.cacheLock.unlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Self.loadingCompletions[name] = [completion]
|
loadingCompletions[name] = [completion]
|
||||||
Self.cacheLock.unlock()
|
|
||||||
|
|
||||||
let animation: LottieAnimation?
|
let animation: LottieAnimation?
|
||||||
if let path = Self.emojiPathMap[name] {
|
if let path = Bundle.main.path(forResource: name, ofType: "json") {
|
||||||
animation = LottieAnimation.filepath(path)
|
animation = LottieAnimation.filepath(path)
|
||||||
} else {
|
} else {
|
||||||
animation = nil
|
animation = nil
|
||||||
}
|
}
|
||||||
|
animationCache[name] = animation
|
||||||
Self.cacheLock.lock()
|
let completions = loadingCompletions.removeValue(forKey: name) ?? []
|
||||||
Self.animationCache[name] = animation
|
|
||||||
let completions = Self.loadingCompletions.removeValue(forKey: name) ?? []
|
|
||||||
Self.cacheLock.unlock()
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
completions.forEach { $0(animation) }
|
completions.forEach { $0(animation) }
|
||||||
}
|
}
|
||||||
|
|
@ -583,6 +558,7 @@ final class EmojiPanelCell: UICollectionViewCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var animationName: String?
|
private var animationName: String?
|
||||||
|
private var shouldPlayAnimation = false
|
||||||
|
|
||||||
private let lottieView: LottieAnimationView = {
|
private let lottieView: LottieAnimationView = {
|
||||||
let v = LottieAnimationView()
|
let v = LottieAnimationView()
|
||||||
|
|
@ -622,28 +598,36 @@ final class EmojiPanelCell: UICollectionViewCell {
|
||||||
|
|
||||||
func configure(name: String) {
|
func configure(name: String) {
|
||||||
animationName = name
|
animationName = name
|
||||||
|
shouldPlayAnimation = false
|
||||||
lottieView.stop()
|
lottieView.stop()
|
||||||
|
lottieView.animation = nil
|
||||||
|
lottieView.currentProgress = 0
|
||||||
Self.loadAnimation(for: name) { [weak self] animation in
|
Self.loadAnimation(for: name) { [weak self] animation in
|
||||||
guard let self = self, self.animationName == name else { return }
|
guard let self = self, self.animationName == name else { return }
|
||||||
self.lottieView.animation = animation
|
self.lottieView.animation = animation
|
||||||
self.lottieView.currentProgress = 0
|
self.lottieView.currentProgress = 0
|
||||||
|
if self.shouldPlayAnimation {
|
||||||
self.lottieView.play()
|
self.lottieView.play()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lockView.isHidden = AppContextManager.shared.vip > 1
|
lockView.isHidden = AppContextManager.shared.vip > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func playAnimation() {
|
func playAnimation() {
|
||||||
|
shouldPlayAnimation = true
|
||||||
lottieView.play()
|
lottieView.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopAnimation() {
|
func stopAnimation() {
|
||||||
|
shouldPlayAnimation = false
|
||||||
lottieView.stop()
|
lottieView.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
animationName = nil
|
animationName = nil
|
||||||
|
shouldPlayAnimation = false
|
||||||
lottieView.stop()
|
lottieView.stop()
|
||||||
lottieView.animation = nil
|
lottieView.animation = nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
//
|
|
||||||
// LocationPermissionPopView.swift
|
|
||||||
// QuickLocation
|
|
||||||
//
|
|
||||||
// Created by 八条 on 2026/7/8.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import RxSwift
|
|
||||||
import RxCocoa
|
|
||||||
|
|
||||||
class LocationPermissionPopView: UIView {
|
|
||||||
|
|
||||||
private static let shared = LocationPermissionPopView(frame: CGRect(origin: .zero, size: kScreenSize))
|
|
||||||
|
|
||||||
var disposeBag = DisposeBag()
|
|
||||||
|
|
||||||
static func show() {
|
|
||||||
guard let superView = kKeyWindow else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if LocationPermissionPopView.shared.superview != nil {
|
|
||||||
LocationPermissionPopView.shared.removeFromSuperview()
|
|
||||||
LocationPermissionPopView.shared.bgView.frame = .zero
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationPermissionPopView.shared.bgView.alpha = 1
|
|
||||||
LocationPermissionPopView.shared.bgView.frame = CGRect(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight)
|
|
||||||
superView.addSubview(LocationPermissionPopView.shared)
|
|
||||||
superView.bringSubviewToFront(LocationPermissionPopView.shared)
|
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.25) {
|
|
||||||
LocationPermissionPopView.shared.bgView.alpha = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 关闭
|
|
||||||
static func dismiss() {
|
|
||||||
guard LocationPermissionPopView.shared.superview != nil else { return }
|
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseIn]) {
|
|
||||||
LocationPermissionPopView.shared.bgView.alpha = 0
|
|
||||||
} completion: { _ in
|
|
||||||
LocationPermissionPopView.shared.removeFromSuperview()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupRx() {
|
|
||||||
settingBtn.rx.tap.subscribe(onNext: { _ in
|
|
||||||
guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
|
|
||||||
LocationPermissionPopView.dismiss()
|
|
||||||
if UIApplication.shared.canOpenURL(settingsURL) {
|
|
||||||
UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
|
|
||||||
}
|
|
||||||
}).disposed(by: disposeBag)
|
|
||||||
|
|
||||||
closeBtn.rx.tap.subscribe(onNext: { _ in
|
|
||||||
LocationPermissionPopView.dismiss()
|
|
||||||
}).disposed(by: disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy var bgView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.backgroundColor = .black.withAlphaComponent(0.5)
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var popView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.backgroundColor = .clear
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var popBgView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.backgroundColor = .white
|
|
||||||
view.cornerRadius = 30
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var logoImgView: UIImageView = {
|
|
||||||
let view = UIImageView(image: UIImage(named: "Popup/bell"))
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var headerBgImg: UIImageView = {
|
|
||||||
let view = UIImageView(image: UIImage(named: "Popup/header_bg"))
|
|
||||||
view.contentMode = .scaleAspectFill
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var textImgView: UIImageView = {
|
|
||||||
let view = UIImageView()
|
|
||||||
view.contentMode = .scaleAspectFill
|
|
||||||
view.image = UIImage(named: "Home/permission_text")
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var settingBtn: UIButton = {
|
|
||||||
let btn = UIButton(type: .custom)
|
|
||||||
btn.setTitle("马上开启", for: .normal)
|
|
||||||
btn.setTitleColor(.white, for: .normal)
|
|
||||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
|
||||||
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
|
||||||
btn.cornerRadius = 25
|
|
||||||
return btn
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var closeBtn: UIButton = {
|
|
||||||
let btn = UIButton(type: .custom)
|
|
||||||
btn.setTitle("取消", for: .normal)
|
|
||||||
btn.setTitleColor(UIColor(hexStr: "#16B3FF"), for: .normal)
|
|
||||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
|
||||||
btn.backgroundColor = .clear
|
|
||||||
return btn
|
|
||||||
}()
|
|
||||||
|
|
||||||
// MARK: - Init
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
backgroundColor = .clear
|
|
||||||
addSubview(bgView)
|
|
||||||
bgView.addSubview(popView)
|
|
||||||
popView.addSubview(popBgView)
|
|
||||||
popView.addSubview(logoImgView)
|
|
||||||
popBgView.addSubview(headerBgImg)
|
|
||||||
popBgView.addSubview(textImgView)
|
|
||||||
popBgView.addSubview(settingBtn)
|
|
||||||
popBgView.addSubview(closeBtn)
|
|
||||||
|
|
||||||
popView.layoutChain
|
|
||||||
.centerX()
|
|
||||||
.centerY()
|
|
||||||
.width(315)
|
|
||||||
.height(362)
|
|
||||||
|
|
||||||
popBgView.layoutChain.edges()
|
|
||||||
|
|
||||||
logoImgView.layoutChain
|
|
||||||
.left(16)
|
|
||||||
.top(-45)
|
|
||||||
.width(109)
|
|
||||||
.height(109)
|
|
||||||
|
|
||||||
headerBgImg.layoutChain
|
|
||||||
.edges(excludingEdge: .bottom)
|
|
||||||
.heightToWidth(100/315)
|
|
||||||
|
|
||||||
textImgView.layoutChain
|
|
||||||
.top(70)
|
|
||||||
.centerX()
|
|
||||||
.width(275)
|
|
||||||
.height(173)
|
|
||||||
|
|
||||||
closeBtn.layoutChain
|
|
||||||
.bottom()
|
|
||||||
.height(50)
|
|
||||||
.edgesHorzontal(20)
|
|
||||||
|
|
||||||
settingBtn.layoutChain
|
|
||||||
.bottomToTopOfView(closeBtn)
|
|
||||||
.height(50)
|
|
||||||
.edgesHorzontal(20)
|
|
||||||
|
|
||||||
setupRx()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -23,12 +23,19 @@ class SOSViewController: BaseViewController {
|
||||||
|
|
||||||
// Do any additional setup after loading the view.
|
// Do any additional setup after loading the view.
|
||||||
fd_interactivePopDisabled = true
|
fd_interactivePopDisabled = true
|
||||||
|
// requestUserInfo()
|
||||||
|
|
||||||
rootView.didSendSOS = { status in
|
rootView.didSendSOS = { status in
|
||||||
self.requestSOS(status)
|
self.requestSOS(status)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
guard AppContextManager.shared.status == 1 else { return }
|
private func requestUserInfo(completion: (() -> Void)? = nil) {
|
||||||
|
UserService.userInfo().subscribe { response in
|
||||||
|
guard let model = response.model else { return }
|
||||||
|
AppContextManager.shared.saveAccount(model)
|
||||||
|
|
||||||
|
guard model.status == 1 else { return }
|
||||||
self.rootView.countDownView.alpha = 1
|
self.rootView.countDownView.alpha = 1
|
||||||
self.rootView.countDownFinish = true
|
self.rootView.countDownFinish = true
|
||||||
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
|
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
|
||||||
|
|
@ -37,13 +44,14 @@ class SOSViewController: BaseViewController {
|
||||||
self.rootView.countDownLottieView.play()
|
self.rootView.countDownLottieView.play()
|
||||||
self.rootView.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
|
self.rootView.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestSOS(_ enable: Bool) {
|
private func requestSOS(_ enable: Bool) {
|
||||||
dl.showLoading()
|
dl.showLoading()
|
||||||
SystemService.sos(enable: enable).subscribe(onNext: { response in
|
SystemService.sos(enable: enable).subscribe(onNext: { response in
|
||||||
self.dl.show(text: enable ? "发送成功" : "取消成功") {
|
self.dl.show(text: enable ? "发送成功" : "取消成功") {
|
||||||
AppContextManager.shared.updateAccountStatus(enable ? 1 : 0)
|
|
||||||
AppRouter.shared.popOrDismiss()
|
AppRouter.shared.popOrDismiss()
|
||||||
}
|
}
|
||||||
}, onError: { (error) in }).disposed(by: disposeBag)
|
}, onError: { (error) in }).disposed(by: disposeBag)
|
||||||
|
|
|
||||||
|
|
@ -30,29 +30,39 @@ class LaunchViewController: BaseViewController {
|
||||||
setupLayout()
|
setupLayout()
|
||||||
|
|
||||||
experienceBtn.rx.tap.subscribe(onNext: { _ in
|
experienceBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
Defaults[\.isShowGuide] = false
|
// Defaults[\.guideShowVersion] = self.showVersion
|
||||||
AppDelegate.shared.showMainViewController()
|
AppDelegate.shared.showMainViewController()
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
||||||
getUserConfig()
|
getUserConfig()
|
||||||
|
|
||||||
|
navigateAfterDelay()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func navigateAfterDelay() {
|
private func navigateAfterDelay() {
|
||||||
Observable.just(())
|
Observable.just(())
|
||||||
.delay(.milliseconds(2500), scheduler: MainScheduler.instance)
|
.delay(.milliseconds(2500), scheduler: MainScheduler.instance)
|
||||||
.subscribe(onNext: { [weak self] in
|
.subscribe(onNext: { [weak self] in
|
||||||
guard let systemConfig = AppContextManager.shared.systemConfig else { return }
|
self?.navigateToNext()
|
||||||
if systemConfig.guideDisplay, Defaults[\.isShowGuide] {
|
|
||||||
self?.collectionView.isHidden = false
|
|
||||||
self?.pageControl.isHidden = false
|
|
||||||
self?.experienceBtn.isHidden = false
|
|
||||||
} else {
|
|
||||||
AppDelegate.shared.showMainViewController()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func navigateToNext() {
|
||||||
|
let hasShownGuide = UserDefaults.standard.bool(forKey: "hasShownGuide")
|
||||||
|
if hasShownGuide {
|
||||||
|
let loginVC = LoginViewController()
|
||||||
|
UIView.transition(with: view.window!, duration: 0.4, options: .transitionCrossDissolve) {
|
||||||
|
self.view.window?.rootViewController = UINavigationController(rootViewController: loginVC)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collectionView.isHidden = false
|
||||||
|
pageControl.isHidden = false
|
||||||
|
experienceBtn.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取用户配置
|
/// 获取用户配置
|
||||||
func getUserConfig() {
|
func getUserConfig() {
|
||||||
SystemService.userConfig().subscribe(onNext: { response in
|
SystemService.userConfig().subscribe(onNext: { response in
|
||||||
|
|
@ -72,11 +82,11 @@ class LaunchViewController: BaseViewController {
|
||||||
|
|
||||||
/// 获取用户IM Token
|
/// 获取用户IM Token
|
||||||
func getUserIMToken() {
|
func getUserIMToken() {
|
||||||
|
DLToast.showLoading()
|
||||||
UserService.imToken().subscribe(onNext: { response in
|
UserService.imToken().subscribe(onNext: { response in
|
||||||
guard let data = response.data, let token = data["token"] as? String else { return }
|
guard let data = response.data, let token = data["token"] as? String else { return }
|
||||||
AppContextManager.shared.imToken = token
|
AppContextManager.shared.imToken = token
|
||||||
GroupIMService.shared.login { _ in }
|
GroupIMService.shared.login { _ in }
|
||||||
self.navigateAfterDelay()
|
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,14 +68,13 @@ class LoginView: UIView {
|
||||||
addSubview(bgMaskImage)
|
addSubview(bgMaskImage)
|
||||||
addSubview(backBtn)
|
addSubview(backBtn)
|
||||||
addSubview(guestLoginButton)
|
addSubview(guestLoginButton)
|
||||||
addSubview(inputContainerView)
|
addSubview(welcomeLabel)
|
||||||
inputContainerView.addSubview(welcomeLabel)
|
addSubview(phoneNumberLabel)
|
||||||
inputContainerView.addSubview(phoneNumberLabel)
|
addSubview(carrierLabel)
|
||||||
inputContainerView.addSubview(carrierLabel)
|
addSubview(otherPhoneView)
|
||||||
inputContainerView.addSubview(otherPhoneView)
|
|
||||||
otherPhoneView.addSubview(phoneInputView)
|
otherPhoneView.addSubview(phoneInputView)
|
||||||
otherPhoneView.addSubview(smsCodeInputView)
|
otherPhoneView.addSubview(smsCodeInputView)
|
||||||
inputContainerView.addSubview(loginButton)
|
addSubview(loginButton)
|
||||||
addSubview(appleLoginBtn)
|
addSubview(appleLoginBtn)
|
||||||
addSubview(wechatLoginBtn)
|
addSubview(wechatLoginBtn)
|
||||||
addSubview(phoneLoginBtn)
|
addSubview(phoneLoginBtn)
|
||||||
|
|
@ -99,14 +98,8 @@ class LoginView: UIView {
|
||||||
.width(85)
|
.width(85)
|
||||||
.height(29)
|
.height(29)
|
||||||
|
|
||||||
inputContainerView.layoutChain
|
|
||||||
.centerX()
|
|
||||||
.centerY(self, offset: -17)
|
|
||||||
.edgesHorzontal()
|
|
||||||
.height(306)
|
|
||||||
|
|
||||||
welcomeLabel.layoutChain
|
welcomeLabel.layoutChain
|
||||||
.top(0)
|
.top(202 - kNaviHeight)
|
||||||
.centerX()
|
.centerX()
|
||||||
.edgesHorzontal()
|
.edgesHorzontal()
|
||||||
|
|
||||||
|
|
@ -168,7 +161,7 @@ class LoginView: UIView {
|
||||||
agreementView.layoutChain
|
agreementView.layoutChain
|
||||||
.leftToView(loginButton)
|
.leftToView(loginButton)
|
||||||
.rightToView(loginButton)
|
.rightToView(loginButton)
|
||||||
.bottom(kSafeBottomMargin + 20)
|
.bottom(55)
|
||||||
.height(30)
|
.height(30)
|
||||||
|
|
||||||
agreementLabel.layoutChain.edges()
|
agreementLabel.layoutChain.edges()
|
||||||
|
|
@ -185,7 +178,7 @@ class LoginView: UIView {
|
||||||
.top()
|
.top()
|
||||||
|
|
||||||
phoneLoginBtn.layoutChain
|
phoneLoginBtn.layoutChain
|
||||||
.bottomToTopOfView(agreementView, offset: -30)
|
.bottomToTopOfView(agreementView, offset: -81)
|
||||||
.centerX()
|
.centerX()
|
||||||
.width(38)
|
.width(38)
|
||||||
.heightToWidth(1)
|
.heightToWidth(1)
|
||||||
|
|
@ -265,13 +258,6 @@ class LoginView: UIView {
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 输入区域容器
|
|
||||||
lazy var inputContainerView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.backgroundColor = .clear
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 其他手机号登录
|
// 其他手机号登录
|
||||||
lazy var otherPhoneView: UIView = {
|
lazy var otherPhoneView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
|
|
@ -408,6 +394,7 @@ class LoginView: UIView {
|
||||||
textView.backgroundColor = .clear
|
textView.backgroundColor = .clear
|
||||||
textView.isEditable = false
|
textView.isEditable = false
|
||||||
textView.isScrollEnabled = false
|
textView.isScrollEnabled = false
|
||||||
|
textView.isSelectable = false
|
||||||
textView.linkTextAttributes = [:]
|
textView.linkTextAttributes = [:]
|
||||||
|
|
||||||
return textView
|
return textView
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import UIKit
|
||||||
import SnapKit
|
import SnapKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import AuthenticationServices
|
|
||||||
#if !targetEnvironment(simulator)
|
#if !targetEnvironment(simulator)
|
||||||
import GeYanSdk
|
import GeYanSdk
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -91,67 +90,27 @@ class LoginViewController: BaseViewController {
|
||||||
|
|
||||||
rootView.wechatLoginBtn.rx.tap
|
rootView.wechatLoginBtn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] in
|
.subscribe(onNext: { [weak self] in
|
||||||
guard self?.isAgreed == true else {
|
|
||||||
DLToast.showError(text: "请先同意用户协议")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self?.viewModel.performWechatLogin()
|
self?.viewModel.performWechatLogin()
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
rootView.appleLoginBtn.rx.tap
|
rootView.appleLoginBtn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] in
|
.subscribe(onNext: { [weak self] in
|
||||||
guard self?.isAgreed == true else {
|
|
||||||
DLToast.showError(text: "请先同意用户协议")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self?.viewModel.performAppleLogin()
|
self?.viewModel.performAppleLogin()
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
viewModel.onAppleLoginRequest = { [weak self] controller in
|
|
||||||
guard let self = self else { return }
|
|
||||||
controller.delegate = self
|
|
||||||
controller.presentationContextProvider = self
|
|
||||||
controller.performRequests()
|
|
||||||
}
|
|
||||||
|
|
||||||
rootView.phoneLoginBtn.rx.tap
|
rootView.phoneLoginBtn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] in
|
.subscribe(onNext: { [weak self] in
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
if GeYanSdk.getCurrentCarrierCount() == 0 {
|
|
||||||
DLToast.show(text: "请插入SIM卡后重试")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if GeYanSdk.isPreGettedTokenValidate() { //预登录有效,启动登录授权页
|
|
||||||
let oneTapView = OneTapLoginView(frame: UIScreen.main.bounds)
|
|
||||||
self?.viewModel.performOneTapLogin(from: self!, contentView: oneTapView)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 预登录
|
|
||||||
GeYanSdk.preGetToken { preDic in
|
|
||||||
guard GeYanSdk.isPreGettedTokenValidate() else {
|
|
||||||
var message = ""
|
|
||||||
if let dict = preDic as? [String: Any] {
|
|
||||||
message = dict["msg"] as? String ?? "获取手机号失败,请使用验证码登录"
|
|
||||||
}
|
|
||||||
DLToast.show(text: message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let oneTapView = OneTapLoginView(frame: UIScreen.main.bounds)
|
|
||||||
self?.viewModel.performOneTapLogin(from: self!, contentView: oneTapView)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
// viewModel.loginSuccess
|
viewModel.loginSuccess
|
||||||
// .subscribe(onNext: { [weak self] in
|
.subscribe(onNext: { [weak self] in
|
||||||
//
|
|
||||||
// })
|
})
|
||||||
// .disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - API 验证码
|
// MARK: - API 验证码
|
||||||
|
|
@ -191,40 +150,3 @@ extension LoginViewController: UITextViewDelegate {
|
||||||
|
|
||||||
private func openURL(_ url: String) {}
|
private func openURL(_ url: String) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Apple Sign In
|
|
||||||
extension LoginViewController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
|
|
||||||
|
|
||||||
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
|
|
||||||
view.window ?? UIWindow()
|
|
||||||
}
|
|
||||||
|
|
||||||
func authorizationController(controller: ASAuthorizationController,
|
|
||||||
didCompleteWithAuthorization authorization: ASAuthorization) {
|
|
||||||
guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else { return }
|
|
||||||
|
|
||||||
let userId = credential.user
|
|
||||||
let userName: String = {
|
|
||||||
if let name = credential.fullName {
|
|
||||||
let parts = [name.givenName, name.familyName].compactMap { $0 }
|
|
||||||
if !parts.isEmpty { return parts.joined(separator: " ") }
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}()
|
|
||||||
let identityToken = credential.identityToken.flatMap { String(data: $0, encoding: .utf8) } ?? ""
|
|
||||||
let authCode = credential.authorizationCode.flatMap { String(data: $0, encoding: .utf8) } ?? ""
|
|
||||||
|
|
||||||
viewModel.loginAction(type: "apple", data: [
|
|
||||||
"user_id": userId,
|
|
||||||
"user_name": userName,
|
|
||||||
"identify_code": authCode,
|
|
||||||
"identify_token": identityToken
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
func authorizationController(controller: ASAuthorizationController,
|
|
||||||
didCompleteWithError error: Error) {
|
|
||||||
NotificationCenter.default.post(name: .resumePopupQueue, object: nil)
|
|
||||||
DLToast.showError(text: "Apple 登录失败")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,43 +6,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import SwiftyUserDefaults
|
import SwiftyUserDefaults
|
||||||
import AuthenticationServices
|
|
||||||
#if !targetEnvironment(simulator)
|
#if !targetEnvironment(simulator)
|
||||||
import GeYanSdk
|
import GeYanSdk
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum LoginSessionHandler {
|
|
||||||
private static let disposeBag = DisposeBag()
|
|
||||||
|
|
||||||
static func login(type: String, data: [String: Any]) {
|
|
||||||
DLToast.showLoading()
|
|
||||||
UserService.login(type: type, data: data)
|
|
||||||
.delaySubscription(.seconds(1), scheduler: MainScheduler.instance)
|
|
||||||
.retry(1)
|
|
||||||
.subscribe(onNext: { response in
|
|
||||||
GroupIMService.shared.logout()
|
|
||||||
DLToast.dismiss()
|
|
||||||
guard let model = response.model else { return }
|
|
||||||
NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil)
|
|
||||||
Defaults[\.loginToken] = model.token
|
|
||||||
DLToast.showSuccess(text: "登录成功") {
|
|
||||||
if let userId = model.uid {
|
|
||||||
MQTTService.shared.updateClientID("smartdrive_\(userId)")
|
|
||||||
}
|
|
||||||
NotificationCenter.default.post(name: .RefreshUserConfigNotification, object: nil)
|
|
||||||
if let nav = AppRouter.shared.navigationController, nav.viewControllers.count > 1 {
|
|
||||||
AppRouter.shared.popOrDismiss()
|
|
||||||
} else {
|
|
||||||
AppDelegate.shared.showMainViewController()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, onError: { error in
|
|
||||||
DLToast.dismiss()
|
|
||||||
DLToast.showError(text: "登录失败,请重试")
|
|
||||||
}).disposed(by: disposeBag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class LoginViewModel: BaseViewModel {
|
final class LoginViewModel: BaseViewModel {
|
||||||
|
|
||||||
/// 验证码接口返回值
|
/// 验证码接口返回值
|
||||||
|
|
@ -51,86 +18,47 @@ final class LoginViewModel: BaseViewModel {
|
||||||
let oneClickLoginResult = PublishSubject<Bool>()
|
let oneClickLoginResult = PublishSubject<Bool>()
|
||||||
let loginSuccess = PublishSubject<Void>()
|
let loginSuccess = PublishSubject<Void>()
|
||||||
|
|
||||||
var onAppleLoginRequest: ((ASAuthorizationController) -> Void)?
|
var gyAuthVM: GyAuthViewModel {
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
lazy var gyAuthVM: GyAuthViewModel = {
|
|
||||||
let viewModel = GyAuthViewModel()
|
let viewModel = GyAuthViewModel()
|
||||||
viewModel.statusBarStyle = .default
|
viewModel.statusBarStyle = .default
|
||||||
viewModel.userInterfaceStyle = 0
|
viewModel.userInterfaceStyle = 0
|
||||||
viewModel.pullAuthVCStyle = .modal
|
viewModel.pullAuthVCStyle = .push
|
||||||
|
viewModel.viewLifeCycleBlock = { (str, bool) in
|
||||||
|
|
||||||
|
}
|
||||||
viewModel.clickAuthButtonBlock = {
|
viewModel.clickAuthButtonBlock = {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return viewModel
|
return viewModel
|
||||||
}()
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/// 登录
|
/// 登录
|
||||||
func loginAction(type: String, data: [String : Any]) {
|
func loginAction(type: String, data: [String : Any]) {
|
||||||
LoginSessionHandler.login(type: type, data: data)
|
DLToast.showLoading()
|
||||||
|
UserService.login(type: type, data: data).subscribe { response in
|
||||||
|
GroupIMService.shared.logout()
|
||||||
|
DLToast.dismiss()
|
||||||
|
guard let model = response.model else { return }
|
||||||
|
Defaults[\.loginToken] = model.token
|
||||||
|
DLToast.showSuccess(text: "登录成功") {
|
||||||
|
if let userId = model.uid {
|
||||||
|
MQTTService.shared.updateClientID("smartdrive_\(userId)")
|
||||||
|
}
|
||||||
|
NotificationCenter.default.post(name: .RefreshUserConfigNotification, object: nil)
|
||||||
|
AppRouter.shared.popOrDismiss()
|
||||||
|
}
|
||||||
|
}.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func performGuestLogin() {
|
func performGuestLogin() {
|
||||||
loginSuccess.onNext(())
|
loginSuccess.onNext(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
func performOneTapLogin(from vc: UIViewController, contentView: OneTapLoginView) {
|
|
||||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
|
||||||
let subViews: [UIView] = [
|
|
||||||
contentView.phoneNumberLabel,
|
|
||||||
contentView.carrierLabel,
|
|
||||||
contentView.loginButton,
|
|
||||||
contentView.checkBox,
|
|
||||||
contentView.agreementLabel
|
|
||||||
]
|
|
||||||
GeYanSdk.oneTapLogin(vc, viewModel: gyAuthVM, contentView: contentView,
|
|
||||||
subViewList: subViews) { [weak self] result in
|
|
||||||
|
|
||||||
guard let dict = result as? [String: Any],
|
|
||||||
let code = dict["code"] as? Int else { return }
|
|
||||||
print(dict)
|
|
||||||
if code == 30000, // 成功
|
|
||||||
let gyuid = dict["gyuid"],
|
|
||||||
let token = dict["token"] {
|
|
||||||
|
|
||||||
// let opType: String = {
|
|
||||||
// switch dict["operatorType"] as? Int {
|
|
||||||
// case 1: return "CM"
|
|
||||||
// case 2: return "CU"
|
|
||||||
// case 3: return "CT"
|
|
||||||
// default: return ""
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
GeYanSdk.closeAuthVC(true) {
|
|
||||||
self?.loginAction(type: "onekey", data: [
|
|
||||||
"gyuid": gyuid,
|
|
||||||
"token": token
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DLToast.show(text: dict["msg"] as? String ?? "运营商返回失败")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
func performWechatLogin() {
|
func performWechatLogin() {
|
||||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
// TODO: Integrate WeChat SDK
|
||||||
let req = SendAuthReq()
|
|
||||||
req.scope = "snsapi_userinfo"
|
|
||||||
req.state = "login"
|
|
||||||
WXApi.send(req)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func performAppleLogin() {
|
func performAppleLogin() {
|
||||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
// TODO: Integrate Sign in with Apple
|
||||||
let provider = ASAuthorizationAppleIDProvider()
|
|
||||||
let request = provider.createRequest()
|
|
||||||
request.requestedScopes = [.fullName, .email]
|
|
||||||
let controller = ASAuthorizationController(authorizationRequests: [request])
|
|
||||||
onAppleLoginRequest?(controller)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,13 @@ class OneTapLoginView: UIView {
|
||||||
|
|
||||||
private func setupRx() {
|
private func setupRx() {
|
||||||
backBtn.rx.tap.subscribe(onNext: { _ in
|
backBtn.rx.tap.subscribe(onNext: { _ in
|
||||||
#if !targetEnvironment(simulator)
|
AppRouter.shared.popOrDismiss()
|
||||||
GeYanSdk.closeAuthVC(true, completion: nil)
|
|
||||||
#endif
|
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
checkBox.rx.tap.subscribe(onNext: { [weak self] in
|
checkBox.rx.tap.subscribe(onNext: { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.checkBox.isSelected = !self.checkBox.isSelected
|
self.checkBox.isSelected = !self.checkBox.isSelected
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
agreementTV.delegate = self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupAgreementTextWithCarrier(carrier: String) {
|
func setupAgreementTextWithCarrier(carrier: String) {
|
||||||
|
|
@ -59,16 +55,15 @@ class OneTapLoginView: UIView {
|
||||||
private func setupUI() {
|
private func setupUI() {
|
||||||
addSubview(bgMaskImage)
|
addSubview(bgMaskImage)
|
||||||
addSubview(backBtn)
|
addSubview(backBtn)
|
||||||
addSubview(inputContainerView)
|
addSubview(welcomeLabel)
|
||||||
inputContainerView.addSubview(welcomeLabel)
|
addSubview(phoneNumberLabel)
|
||||||
inputContainerView.addSubview(phoneNumberLabel)
|
addSubview(carrierLabel)
|
||||||
inputContainerView.addSubview(carrierLabel)
|
addSubview(loginButton)
|
||||||
inputContainerView.addSubview(loginButton)
|
|
||||||
|
|
||||||
addSubview(agreementView)
|
addSubview(agreementView)
|
||||||
agreementView.addSubview(checkBox)
|
agreementView.addSubview(checkBox)
|
||||||
agreementView.addSubview(agreementLabel)
|
|
||||||
agreementView.addSubview(agreementTV)
|
agreementView.addSubview(agreementTV)
|
||||||
|
agreementView.addSubview(agreementLabel)
|
||||||
|
|
||||||
bgMaskImage.layoutChain.edges()
|
bgMaskImage.layoutChain.edges()
|
||||||
|
|
||||||
|
|
@ -78,14 +73,8 @@ class OneTapLoginView: UIView {
|
||||||
.width(24)
|
.width(24)
|
||||||
.height(24)
|
.height(24)
|
||||||
|
|
||||||
inputContainerView.layoutChain
|
|
||||||
.centerX()
|
|
||||||
.centerY()
|
|
||||||
.edgesHorzontal()
|
|
||||||
.height(301)
|
|
||||||
|
|
||||||
welcomeLabel.layoutChain
|
welcomeLabel.layoutChain
|
||||||
.top(0)
|
.top(202 - kNaviHeight)
|
||||||
.centerX()
|
.centerX()
|
||||||
.edgesHorzontal()
|
.edgesHorzontal()
|
||||||
|
|
||||||
|
|
@ -104,26 +93,23 @@ class OneTapLoginView: UIView {
|
||||||
.height(50)
|
.height(50)
|
||||||
|
|
||||||
agreementView.layoutChain
|
agreementView.layoutChain
|
||||||
.edgesHorzontal(30)
|
.leftToView(loginButton)
|
||||||
.bottom(kSafeBottomMargin + 20)
|
.rightToView(loginButton)
|
||||||
|
.bottom(55)
|
||||||
|
.height(30)
|
||||||
|
|
||||||
|
agreementLabel.layoutChain.edges()
|
||||||
|
|
||||||
checkBox.layoutChain
|
checkBox.layoutChain
|
||||||
.left()
|
.left()
|
||||||
.top(10)
|
.centerY()
|
||||||
.width(12)
|
.width(12)
|
||||||
.height(12)
|
.height(12)
|
||||||
|
|
||||||
agreementLabel.layoutChain
|
|
||||||
.leftToRightOfView(checkBox, offset: 0)
|
|
||||||
.right()
|
|
||||||
.top()
|
|
||||||
.bottom()
|
|
||||||
|
|
||||||
agreementTV.layoutChain
|
agreementTV.layoutChain
|
||||||
.leftToRightOfView(checkBox, offset: 0)
|
.leftToRightOfView(checkBox, offset: 0)
|
||||||
.right()
|
.right()
|
||||||
.top()
|
.top()
|
||||||
.bottom()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
|
|
@ -144,12 +130,6 @@ class OneTapLoginView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UI Components
|
// MARK: - UI Components
|
||||||
lazy var inputContainerView: UIView = {
|
|
||||||
let view = UIView()
|
|
||||||
view.backgroundColor = .clear
|
|
||||||
return view
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var bgMaskImage: UIImageView = {
|
lazy var bgMaskImage: UIImageView = {
|
||||||
let iv = UIImageView()
|
let iv = UIImageView()
|
||||||
iv.image = UIImage(named: "Login/bg")
|
iv.image = UIImage(named: "Login/bg")
|
||||||
|
|
@ -198,12 +178,6 @@ class OneTapLoginView: UIView {
|
||||||
return btn
|
return btn
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var label: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var agreementView: UIView = {
|
lazy var agreementView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
|
|
@ -214,16 +188,15 @@ class OneTapLoginView: UIView {
|
||||||
let btn = UIButton(type: .custom)
|
let btn = UIButton(type: .custom)
|
||||||
btn.setImage(UIImage(named: "Login/checkbox"), for: .normal)
|
btn.setImage(UIImage(named: "Login/checkbox"), for: .normal)
|
||||||
btn.setImage(UIImage(named: "Login/selected"), for: .selected)
|
btn.setImage(UIImage(named: "Login/selected"), for: .selected)
|
||||||
btn.extendEdgeInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 20)
|
btn.extendEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
|
||||||
btn.isSelected = true
|
|
||||||
return btn
|
return btn
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var agreementLabel: UILabel = {
|
lazy var agreementLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.textColor = .clear
|
label.textColor = .clear
|
||||||
label.numberOfLines = 0
|
label.numberOfLines = 1
|
||||||
// label.textAlignment = .center
|
label.textAlignment = .center
|
||||||
label.font = .systemFont(ofSize: 10)
|
label.font = .systemFont(ofSize: 10)
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
@ -234,34 +207,9 @@ class OneTapLoginView: UIView {
|
||||||
textView.backgroundColor = .clear
|
textView.backgroundColor = .clear
|
||||||
textView.isEditable = false
|
textView.isEditable = false
|
||||||
textView.isScrollEnabled = false
|
textView.isScrollEnabled = false
|
||||||
textView.isSelectable = true
|
textView.isSelectable = false
|
||||||
textView.linkTextAttributes = [:]
|
textView.linkTextAttributes = [:]
|
||||||
|
|
||||||
return textView
|
return textView
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
extension OneTapLoginView: UITextViewDelegate {
|
|
||||||
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
|
||||||
var urlStr: String?
|
|
||||||
if URL.absoluteString == "Carrier", let model = GyContentModel.current(), let link = model.link {
|
|
||||||
urlStr = link
|
|
||||||
}
|
|
||||||
if URL.absoluteString == "UserAgreement" {
|
|
||||||
urlStr = URLManager.shared.userAgreementUrl
|
|
||||||
}
|
|
||||||
if URL.absoluteString == "PrivacyPolicy" {
|
|
||||||
urlStr = URLManager.shared.privacyPolicyUrl
|
|
||||||
}
|
|
||||||
guard let urlString = urlStr else { return true }
|
|
||||||
let vc = WebViewController(url: urlString, isShare: false, fullscreen: false, style: "default")
|
|
||||||
AppRouter.present(UINavigationController(rootViewController: vc))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,6 @@ import UIKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
import RxDataSources
|
import RxDataSources
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
import GeYanSdk
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class AccountVC: BaseViewController {
|
class AccountVC: BaseViewController {
|
||||||
|
|
||||||
|
|
@ -62,8 +59,6 @@ class AccountVC: BaseViewController {
|
||||||
DLToast.showLoading()
|
DLToast.showLoading()
|
||||||
UserService.logout().subscribe(onNext: { response in
|
UserService.logout().subscribe(onNext: { response in
|
||||||
DLToast.dismiss()
|
DLToast.dismiss()
|
||||||
NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil)
|
|
||||||
GeYanSdk.deletePreResultCache()
|
|
||||||
AppContextManager.shared.deleteAccount()
|
AppContextManager.shared.deleteAccount()
|
||||||
GroupIMService.shared.logout()
|
GroupIMService.shared.logout()
|
||||||
AppDelegate.shared.showMainViewController()
|
AppDelegate.shared.showMainViewController()
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,7 @@ final class MineView: UIView {
|
||||||
private let vipBtnTextColor = UIColor(hexStr: "#00343B")
|
private let vipBtnTextColor = UIColor(hexStr: "#00343B")
|
||||||
|
|
||||||
func setupData(_ model: UserConfigModel) {
|
func setupData(_ model: UserConfigModel) {
|
||||||
guard AppContextManager.shared.systemConfig?.globalDisplay == true else {
|
|
||||||
maskBgView.isHidden = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
maskBgView.isHidden = true
|
|
||||||
guard model.vip > 1 else {
|
guard model.vip > 1 else {
|
||||||
vipTitleLab.text = "开通会员"
|
vipTitleLab.text = "开通会员"
|
||||||
vipContentLab.text = "会员尊享10+VIP特权"
|
vipContentLab.text = "会员尊享10+VIP特权"
|
||||||
|
|
@ -125,19 +121,11 @@ final class MineView: UIView {
|
||||||
return iv
|
return iv
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var maskBgView: UIImageView = {
|
|
||||||
let iv = UIImageView()
|
|
||||||
iv.image = UIImage(named: "Mine/mask_bg")
|
|
||||||
iv.contentMode = .scaleToFill
|
|
||||||
iv.isHidden = true
|
|
||||||
return iv
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var vipTitleLab: UILabel = {
|
lazy var vipTitleLab: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.font = .systemFont(ofSize: 18, weight: .bold)
|
label.font = .systemFont(ofSize: 18, weight: .bold)
|
||||||
label.textColor = .white
|
label.textColor = .white
|
||||||
label.text = " "
|
label.text = "开通会员"
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -145,7 +133,7 @@ final class MineView: UIView {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||||||
label.textColor = .white
|
label.textColor = .white
|
||||||
label.text = " "
|
label.text = "会员尊享10+VIP特权"
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -200,10 +188,6 @@ final class MineView: UIView {
|
||||||
// MARK: - Callbacks
|
// MARK: - Callbacks
|
||||||
var onMenuTap: ((Int) -> Void)?
|
var onMenuTap: ((Int) -> Void)?
|
||||||
|
|
||||||
// MARK: - Dynamic Height
|
|
||||||
private var settingsCardHeightConstraint: NSLayoutConstraint?
|
|
||||||
private var contentSizeObservation: NSKeyValueObservation?
|
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
@ -232,7 +216,6 @@ final class MineView: UIView {
|
||||||
vipCardView.addSubview(vipActivateButton)
|
vipCardView.addSubview(vipActivateButton)
|
||||||
vipCardView.addSubview(vipTitleLab)
|
vipCardView.addSubview(vipTitleLab)
|
||||||
vipCardView.addSubview(vipContentLab)
|
vipCardView.addSubview(vipContentLab)
|
||||||
vipCardView.addSubview(maskBgView)
|
|
||||||
|
|
||||||
addSubview(profileBgImageView)
|
addSubview(profileBgImageView)
|
||||||
addSubview(bottomFillView)
|
addSubview(bottomFillView)
|
||||||
|
|
@ -295,8 +278,6 @@ final class MineView: UIView {
|
||||||
.right(19).centerY()
|
.right(19).centerY()
|
||||||
.width(70).height(28)
|
.width(70).height(28)
|
||||||
|
|
||||||
maskBgView.layoutChain.edges()
|
|
||||||
|
|
||||||
// Profile transition background (100pt, overlaps VIP card bottom half)
|
// Profile transition background (100pt, overlaps VIP card bottom half)
|
||||||
profileBgImageView.layoutChain
|
profileBgImageView.layoutChain
|
||||||
.topToView(vipCardView, offset: 32)
|
.topToView(vipCardView, offset: 32)
|
||||||
|
|
@ -308,42 +289,14 @@ final class MineView: UIView {
|
||||||
.topToBottomOfView(profileBgImageView)
|
.topToBottomOfView(profileBgImageView)
|
||||||
.left().right().bottom()
|
.left().right().bottom()
|
||||||
|
|
||||||
// Settings Card (dynamic height, scrolls when exceeds area above tabbar)
|
// Settings Card (fixed height, not stretched)
|
||||||
settingsCardView.layoutChain
|
settingsCardView.layoutChain
|
||||||
.topToBottomOfView(vipCardView, offset: 20)
|
.topToBottomOfView(vipCardView, offset: 20)
|
||||||
.left(16).right(16)
|
.left(16).right(16)
|
||||||
|
.height(312)
|
||||||
settingsCardHeightConstraint = settingsCardView.heightAnchor.constraint(equalToConstant: 312)
|
|
||||||
settingsCardHeightConstraint?.isActive = true
|
|
||||||
|
|
||||||
settingsTableView.layoutChain
|
settingsTableView.layoutChain
|
||||||
.edges()
|
.edges()
|
||||||
|
|
||||||
contentSizeObservation = settingsTableView.observe(\.contentSize, options: [.new, .initial]) { [weak self] _, _ in
|
|
||||||
self?.refreshTableViewHeight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Layout
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
refreshTableViewHeight()
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshTableViewHeight() {
|
|
||||||
let contentHeight = settingsTableView.contentSize.height
|
|
||||||
guard contentHeight > 0 else { return }
|
|
||||||
|
|
||||||
let tabBarHeight: CGFloat = 56 + kSafeBottomMargin
|
|
||||||
let maxHeight = bounds.height - tabBarHeight - settingsCardView.frame.minY
|
|
||||||
|
|
||||||
if contentHeight <= maxHeight {
|
|
||||||
settingsCardHeightConstraint?.constant = contentHeight
|
|
||||||
settingsTableView.isScrollEnabled = false
|
|
||||||
} else {
|
|
||||||
settingsCardHeightConstraint?.constant = maxHeight
|
|
||||||
settingsTableView.isScrollEnabled = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,7 @@ class PopupQueueManager {
|
||||||
|
|
||||||
private weak var sourceVC: UIViewController?
|
private weak var sourceVC: UIViewController?
|
||||||
private var currentStep = 0
|
private var currentStep = 0
|
||||||
private var stepGeneration = 0
|
|
||||||
private var countdownTimer: Timer?
|
private var countdownTimer: Timer?
|
||||||
private var isPaused = false
|
|
||||||
|
|
||||||
init() {
|
|
||||||
NotificationCenter.default.addObserver(self,
|
|
||||||
selector: #selector(handlePause),
|
|
||||||
name: .pausePopupQueue,
|
|
||||||
object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self,
|
|
||||||
selector: #selector(handleResume),
|
|
||||||
name: .resumePopupQueue,
|
|
||||||
object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self,
|
|
||||||
selector: #selector(handleInvalidate),
|
|
||||||
name: .invalidatePopupQueue,
|
|
||||||
object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(from vc: UIViewController) {
|
func start(from vc: UIViewController) {
|
||||||
sourceVC = vc
|
sourceVC = vc
|
||||||
|
|
@ -40,27 +19,7 @@ class PopupQueueManager {
|
||||||
executeNextStep()
|
executeNextStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handlePause() {
|
|
||||||
countdownTimer?.invalidate()
|
|
||||||
countdownTimer = nil
|
|
||||||
isPaused = true
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func handleResume() {
|
|
||||||
guard isPaused else { return }
|
|
||||||
isPaused = false
|
|
||||||
executeNextStep()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func handleInvalidate() {
|
|
||||||
countdownTimer?.invalidate()
|
|
||||||
countdownTimer = nil
|
|
||||||
isPaused = true
|
|
||||||
currentStep = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
private func executeNextStep() {
|
private func executeNextStep() {
|
||||||
guard AppContextManager.shared.systemConfig?.globalDisplay == true else { return }
|
|
||||||
switch currentStep {
|
switch currentStep {
|
||||||
case 0:
|
case 0:
|
||||||
tryStartPromotionalActivities()
|
tryStartPromotionalActivities()
|
||||||
|
|
@ -86,14 +45,12 @@ class PopupQueueManager {
|
||||||
skipToNext()
|
skipToNext()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let gen = stepGeneration
|
|
||||||
startCountdown(seconds: timeConfig.pay_pop_time) { [weak self] in
|
startCountdown(seconds: timeConfig.pay_pop_time) { [weak self] in
|
||||||
guard let self = self, self.currentStep == 0, self.stepGeneration == gen else { return }
|
|
||||||
let vc = PromotionalActivitiesVC()
|
let vc = PromotionalActivitiesVC()
|
||||||
vc.onDismiss = { [weak self] in
|
vc.onDismiss = { [weak self] in
|
||||||
self?.skipToNext()
|
self?.skipToNext()
|
||||||
}
|
}
|
||||||
self.push(vc)
|
self?.push(vc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,11 +66,9 @@ class PopupQueueManager {
|
||||||
skipToNext()
|
skipToNext()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let gen = stepGeneration
|
startCountdown(seconds: timeConfig.search_phone) { [weak self] in
|
||||||
startCountdown(seconds: timeConfig.search_phone) {
|
|
||||||
guard self.currentStep == 1, self.stepGeneration == gen else { return }
|
|
||||||
PopupWindow.show(popupType: .search) {
|
PopupWindow.show(popupType: .search) {
|
||||||
self.skipToNext()
|
self?.skipToNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,17 +82,13 @@ class PopupQueueManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if AppContextManager.shared.isGuest {
|
if AppContextManager.shared.isGuest {
|
||||||
let gen = stepGeneration
|
startCountdown(seconds: timeConfig.signup_tip) { [weak self] in
|
||||||
startCountdown(seconds: timeConfig.signup_tip) {
|
|
||||||
guard self.currentStep == 2, self.stepGeneration == gen else { return }
|
|
||||||
PopupWindow.show(popupType: .register) {
|
PopupWindow.show(popupType: .register) {
|
||||||
// done
|
// done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if AppContextManager.shared.vip == 1 {
|
} else if AppContextManager.shared.vip == 1 {
|
||||||
let gen = stepGeneration
|
startCountdown(seconds: timeConfig.buy_vip_tip) { [weak self] in
|
||||||
startCountdown(seconds: timeConfig.buy_vip_tip) {
|
|
||||||
guard self.currentStep == 2, self.stepGeneration == gen else { return }
|
|
||||||
PopupWindow.show(popupType: .vip) {
|
PopupWindow.show(popupType: .vip) {
|
||||||
// done
|
// done
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +99,6 @@ class PopupQueueManager {
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func skipToNext() {
|
private func skipToNext() {
|
||||||
stepGeneration += 1
|
|
||||||
currentStep += 1
|
currentStep += 1
|
||||||
executeNextStep()
|
executeNextStep()
|
||||||
}
|
}
|
||||||
|
|
@ -172,11 +122,7 @@ class PopupQueueManager {
|
||||||
|
|
||||||
private func push(_ vc: UIViewController) {
|
private func push(_ vc: UIViewController) {
|
||||||
let topVC = topViewController()
|
let topVC = topViewController()
|
||||||
guard let nav = topVC?.navigationController else { return }
|
topVC?.navigationController?.pushViewController(vc, animated: true)
|
||||||
if nav.viewControllers.contains(where: { type(of: $0) == type(of: vc) }) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nav.pushViewController(vc, animated: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func topViewController() -> UIViewController? {
|
private func topViewController() -> UIViewController? {
|
||||||
|
|
|
||||||
|
|
@ -151,12 +151,8 @@ class PopupWindow: UIView {
|
||||||
|
|
||||||
registerBtn.rx.tap
|
registerBtn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
|
||||||
self?.activePopupView == 2 ? self?.slideOutRegister2Popup() : self?.slideOutRegisterPopup()
|
self?.activePopupView == 2 ? self?.slideOutRegister2Popup() : self?.slideOutRegisterPopup()
|
||||||
let topVC = UIViewController.currentViewController()
|
|
||||||
if !(topVC is LoginViewController) {
|
|
||||||
AppRouter.push(Route.login)
|
AppRouter.push(Route.login)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
@ -168,12 +164,8 @@ class PopupWindow: UIView {
|
||||||
|
|
||||||
register2Btn.rx.tap
|
register2Btn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
|
||||||
self?.slideOutRegister2Popup()
|
self?.slideOutRegister2Popup()
|
||||||
let topVC = UIViewController.currentViewController()
|
|
||||||
if !(topVC is LoginViewController) {
|
|
||||||
AppRouter.push(Route.login)
|
AppRouter.push(Route.login)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
@ -186,10 +178,6 @@ class PopupWindow: UIView {
|
||||||
vip1UnlockBtn.rx.tap
|
vip1UnlockBtn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
self?.slideOutUnlockVip1Popup()
|
self?.slideOutUnlockVip1Popup()
|
||||||
let topVC = UIViewController.currentViewController()
|
|
||||||
if !(topVC is VipRechargeVC) {
|
|
||||||
AppRouter.push(Route.vipRecharge)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
@ -202,10 +190,6 @@ class PopupWindow: UIView {
|
||||||
unlockVip2Btn.rx.tap
|
unlockVip2Btn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
self?.slideOutUnlockVip2Popup()
|
self?.slideOutUnlockVip2Popup()
|
||||||
let topVC = UIViewController.currentViewController()
|
|
||||||
if !(topVC is VipRechargeVC) {
|
|
||||||
AppRouter.push(Route.vipRecharge)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
@ -218,10 +202,6 @@ class PopupWindow: UIView {
|
||||||
unlockVip3Btn.rx.tap
|
unlockVip3Btn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
self?.slideOutUnlockVip3Popup()
|
self?.slideOutUnlockVip3Popup()
|
||||||
let topVC = UIViewController.currentViewController()
|
|
||||||
if !(topVC is VipRechargeVC) {
|
|
||||||
AppRouter.push(Route.vipRecharge)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
|
|
@ -234,10 +214,6 @@ class PopupWindow: UIView {
|
||||||
unlockVip4Btn.rx.tap
|
unlockVip4Btn.rx.tap
|
||||||
.subscribe(onNext: { [weak self] _ in
|
.subscribe(onNext: { [weak self] _ in
|
||||||
self?.slideOutUnlockVip4Popup()
|
self?.slideOutUnlockVip4Popup()
|
||||||
let topVC = UIViewController.currentViewController()
|
|
||||||
if !(topVC is VipRechargeVC) {
|
|
||||||
AppRouter.push(Route.vipRecharge)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ class PromotionalActivitiesVC: BaseViewController {
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
fd_interactivePopDisabled = true
|
|
||||||
setupTableView()
|
setupTableView()
|
||||||
requestRechargeInfo()
|
requestRechargeInfo()
|
||||||
|
|
||||||
|
|
@ -103,11 +103,6 @@ class PromotionalActivitiesVC: BaseViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - API
|
// MARK: - API
|
||||||
private func requestRechargeInfo() {
|
private func requestRechargeInfo() {
|
||||||
DLToast.showLoading()
|
DLToast.showLoading()
|
||||||
|
|
@ -131,29 +126,29 @@ class PromotionalActivitiesVC: BaseViewController {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if timeConfig.pay_pop_type == 3 {
|
if timeConfig.pay_pop_type == 3 {
|
||||||
// IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in
|
IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in
|
||||||
// guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
// if success {
|
if success {
|
||||||
// self.showPayResultPop(showCloseBtn: false,
|
self.showPayResultPop(showCloseBtn: false,
|
||||||
// status: true,
|
status: true,
|
||||||
// title: "支付成功",
|
title: "支付成功",
|
||||||
// message: "恭喜您!成功开通!",
|
message: "恭喜您!成功开通!",
|
||||||
// confirmText: "确定", confirmBlock: {
|
confirmText: "确定", confirmBlock: {
|
||||||
// AppRouter.shared.popOrDismiss()
|
AppRouter.shared.popOrDismiss()
|
||||||
// }, cancelBlock: { })
|
}, cancelBlock: { })
|
||||||
// } else {
|
} else {
|
||||||
// self.showPayResultPop(status: false,
|
self.showPayResultPop(status: false,
|
||||||
// title: "支付失败",
|
title: "支付失败",
|
||||||
// message: msg ?? "很抱歉,请您重新支付!",
|
message: msg ?? "很抱歉,请您重新支付!",
|
||||||
// confirmText: "重新支付", confirmBlock: { },
|
confirmText: "重新支付", confirmBlock: { },
|
||||||
// cancelText: "取消", cancelBlock: {
|
cancelText: "取消", cancelBlock: {
|
||||||
// AppRouter.shared.popOrDismiss()
|
AppRouter.shared.popOrDismiss()
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
let payType = timeConfig.pay_pop_type == 1 ? "alipay" : "weixin"
|
let payType = timeConfig.pay_pop_type == 1 ? "alipay" : "weixin"
|
||||||
dl.showLoading()
|
dl.showLoading()
|
||||||
|
|
@ -163,20 +158,16 @@ class PromotionalActivitiesVC: BaseViewController {
|
||||||
guard let data = response.data else { return }
|
guard let data = response.data else { return }
|
||||||
|
|
||||||
if payType == "weixin" { // 微信
|
if payType == "weixin" { // 微信
|
||||||
guard let partnerId = data["partnerId"] as? String,
|
// WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink)
|
||||||
let prepayId = data["prepayId"] as? String,
|
//
|
||||||
let package = data["package"] as? String,
|
// let request: PayReq = PayReq()
|
||||||
let nonceStr = data["nonceStr"] as? String,
|
// request.partnerId = model.partnerId
|
||||||
let timeStamp = data["timeStamp"] as? String,
|
// request.prepayId = model.prepayId
|
||||||
let sign = data["sign"] as? String else { return }
|
// request.package = model.packageValue
|
||||||
let request: PayReq = PayReq()
|
// request.nonceStr = model.nonceStr
|
||||||
request.partnerId = partnerId
|
// request.timeStamp = UInt32(model.timeStamp) ?? 0
|
||||||
request.prepayId = prepayId
|
// request.sign = model.sign
|
||||||
request.package = package
|
// WXApi.send(request)
|
||||||
request.nonceStr = nonceStr
|
|
||||||
request.timeStamp = UInt32(timeStamp) ?? 0
|
|
||||||
request.sign = sign
|
|
||||||
WXApi.send(request)
|
|
||||||
}
|
}
|
||||||
else if payType == "alipay" { // 支付宝
|
else if payType == "alipay" { // 支付宝
|
||||||
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
|
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
|
||||||
|
|
|
||||||
|
|
@ -533,7 +533,7 @@ class PromotionalRecommendCell: UITableViewCell {
|
||||||
|
|
||||||
tipsTitleLab.layoutChain
|
tipsTitleLab.layoutChain
|
||||||
.centerX()
|
.centerX()
|
||||||
.top(7)
|
.top(5)
|
||||||
|
|
||||||
priceLab.layoutChain
|
priceLab.layoutChain
|
||||||
.leftToRightOfView(unitLab, offset: 0)
|
.leftToRightOfView(unitLab, offset: 0)
|
||||||
|
|
|
||||||
|
|
@ -110,9 +110,6 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
||||||
}
|
}
|
||||||
}, onError: { error in
|
}, onError: { error in
|
||||||
guard let code = error.underlyingError?.code else { return }
|
guard let code = error.underlyingError?.code else { return }
|
||||||
|
|
||||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
|
||||||
|
|
||||||
if code == 20010 { // "创建的行程数达到上限"
|
if code == 20010 { // "创建的行程数达到上限"
|
||||||
CreateScheduleVipPopView.show()
|
CreateScheduleVipPopView.show()
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +235,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
||||||
.observe(on: MainScheduler.asyncInstance)
|
.observe(on: MainScheduler.asyncInstance)
|
||||||
.subscribe(onNext: { [weak self] items in
|
.subscribe(onNext: { [weak self] items in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
let h = max(CGFloat(items.count) * 129, 129)
|
let h = max(CGFloat(items.count) * 122, 122)
|
||||||
self.popView.tableView.layoutChain.height(h)
|
self.popView.tableView.layoutChain.height(h)
|
||||||
self.refreshMapPoints()
|
self.refreshMapPoints()
|
||||||
})
|
})
|
||||||
|
|
@ -363,9 +360,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
||||||
|
|
||||||
// 缩放至包含所有点
|
// 缩放至包含所有点
|
||||||
if !pointAnnotations.isEmpty {
|
if !pointAnnotations.isEmpty {
|
||||||
rootView.mapView.showAnnotations(pointAnnotations,
|
rootView.mapView.showAnnotations(pointAnnotations, animated: true)
|
||||||
edgePadding: UIEdgeInsets(top: kNaviHeight + 20, left: 30, bottom: kScreenHeight / 3 + 20, right: 30),
|
|
||||||
animated: true)
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ class CreateScheduleView: UIView {
|
||||||
mv.showsUserLocation = false
|
mv.showsUserLocation = false
|
||||||
mv.showsCompass = false
|
mv.showsCompass = false
|
||||||
mv.userTrackingMode = .none
|
mv.userTrackingMode = .none
|
||||||
DispatchQueue.main.async { mv.logoCenter = CGPoint(x: kScreenWidth - 50, y: kScreenHeight / 3 * 2 - 20) }
|
DispatchQueue.main.async { mv.logoCenter = CGPoint(x: mv.bounds.width - 55, y: kNaviHeight) }
|
||||||
return mv
|
return mv
|
||||||
}()
|
}()
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class ItineraryDetailView: UIView {
|
||||||
mv.showsUserLocation = false
|
mv.showsUserLocation = false
|
||||||
mv.showsCompass = false
|
mv.showsCompass = false
|
||||||
mv.userTrackingMode = .none
|
mv.userTrackingMode = .none
|
||||||
// DispatchQueue.main.async { mv.logoCenter = CGPoint(x: mv.bounds.width - 55, y: kNaviHeight) }
|
DispatchQueue.main.async { mv.logoCenter = CGPoint(x: mv.bounds.width - 55, y: kNaviHeight) }
|
||||||
return mv
|
return mv
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class LocationPickerView: UIView {
|
||||||
bottomView.layoutChain
|
bottomView.layoutChain
|
||||||
.edgesHorzontal()
|
.edgesHorzontal()
|
||||||
.bottom()
|
.bottom()
|
||||||
.height(160 + kSafeBottomMargin)
|
.height(160)
|
||||||
|
|
||||||
poiNameLab.layoutChain
|
poiNameLab.layoutChain
|
||||||
.top(16).left(16).right(16)
|
.top(16).left(16).right(16)
|
||||||
|
|
@ -81,7 +81,7 @@ class LocationPickerView: UIView {
|
||||||
mv.zoomLevel = 18
|
mv.zoomLevel = 18
|
||||||
mv.showsUserLocation = false
|
mv.showsUserLocation = false
|
||||||
mv.showsCompass = false
|
mv.showsCompass = false
|
||||||
// DispatchQueue.main.async { mv.logoCenter = CGPoint(x: mv.bounds.width - 55, y: kNaviHeight) }
|
DispatchQueue.main.async { mv.logoCenter = CGPoint(x: mv.bounds.width - 55, y: 25) }
|
||||||
return mv
|
return mv
|
||||||
}()
|
}()
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,6 @@ class ScheduleDetailVC: BaseViewController {
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
|
||||||
|
|
||||||
rootView.vipTipsLab.text = AppContextManager.shared.vip > 1 ? "" : "升级 VIP,可查看具体人员与节点"
|
rootView.vipTipsLab.text = AppContextManager.shared.vip > 1 ? "" : "升级 VIP,可查看具体人员与节点"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,6 @@ class ScheduleVC: BaseViewController {
|
||||||
self.rootView.tableView.refresh(status: status, isEmpty: isEmpty)
|
self.rootView.tableView.refresh(status: status, isEmpty: isEmpty)
|
||||||
}).disposed(by: disposeBag)
|
}).disposed(by: disposeBag)
|
||||||
|
|
||||||
viewModel.output.sectionedItems
|
|
||||||
.map { !($0.first?.items.isEmpty ?? true) }
|
|
||||||
.bind(to: rootView.emptyVisitorsLabel.rx.isHidden)
|
|
||||||
.disposed(by: disposeBag)
|
|
||||||
|
|
||||||
viewModel.output.scheduleSectionedItems
|
|
||||||
.map { !($0.first?.items.isEmpty ?? true) }
|
|
||||||
.bind(to: rootView.emptyScheduleLabel.rx.isHidden)
|
|
||||||
.disposed(by: disposeBag)
|
|
||||||
|
|
||||||
rootView.collectionView.rx.modelSelected(ViewedModel.self)
|
rootView.collectionView.rx.modelSelected(ViewedModel.self)
|
||||||
.subscribe(viewModel.viewedCellAction.inputs)
|
.subscribe(viewModel.viewedCellAction.inputs)
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
|
||||||
|
|
@ -129,12 +129,6 @@ class ScheduleView: UIView {
|
||||||
.height(80)
|
.height(80)
|
||||||
.bottom(15)
|
.bottom(15)
|
||||||
|
|
||||||
// 暂无访客
|
|
||||||
view.addSubview(emptyVisitorsLabel)
|
|
||||||
emptyVisitorsLabel.layoutChain
|
|
||||||
.centerY(collectionView)
|
|
||||||
.centerX()
|
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -151,16 +145,6 @@ class ScheduleView: UIView {
|
||||||
return cv
|
return cv
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var emptyVisitorsLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.text = "暂无访客"
|
|
||||||
label.font = .systemFont(ofSize: 14)
|
|
||||||
label.textColor = UIColor(hexStr: "#999999")
|
|
||||||
label.textAlignment = .center
|
|
||||||
label.isHidden = true
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
/// 行程路线
|
/// 行程路线
|
||||||
lazy var travelRouteView: UIView = {
|
lazy var travelRouteView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
|
|
@ -229,20 +213,9 @@ class ScheduleView: UIView {
|
||||||
tableView.showsVerticalScrollIndicator = false
|
tableView.showsVerticalScrollIndicator = false
|
||||||
tableView.register(ScheduleListPopCell.self)
|
tableView.register(ScheduleListPopCell.self)
|
||||||
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 97 + kSafeBottomMargin, right: 0)
|
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 97 + kSafeBottomMargin, right: 0)
|
||||||
tableView.backgroundView = emptyScheduleLabel
|
|
||||||
return tableView
|
return tableView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
lazy var emptyScheduleLabel: UILabel = {
|
|
||||||
let label = UILabel()
|
|
||||||
label.text = "暂无行程"
|
|
||||||
label.font = .systemFont(ofSize: 14)
|
|
||||||
label.textColor = UIColor(hexStr: "#999999")
|
|
||||||
label.textAlignment = .center
|
|
||||||
label.isHidden = true
|
|
||||||
return label
|
|
||||||
}()
|
|
||||||
|
|
||||||
lazy var createBtn: UIButton = {
|
lazy var createBtn: UIButton = {
|
||||||
let btn = UIButton()
|
let btn = UIButton()
|
||||||
btn.setImage(UIImage(named: "Schedule/create"), for: .normal)
|
btn.setImage(UIImage(named: "Schedule/create"), for: .normal)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Kingfisher
|
|
||||||
|
|
||||||
class SharePopView: UIView {
|
class SharePopView: UIView {
|
||||||
|
|
||||||
|
|
@ -15,13 +14,14 @@ class SharePopView: UIView {
|
||||||
|
|
||||||
private let contentHeight: CGFloat = 191 + kSafeBottomMargin
|
private let contentHeight: CGFloat = 191 + kSafeBottomMargin
|
||||||
|
|
||||||
static func show() {
|
static func show(shareURL: String) {
|
||||||
guard let superView = kKeyWindow else { return }
|
guard let superView = kKeyWindow else { return }
|
||||||
|
|
||||||
if SharePopView.shared.superview != nil {
|
if SharePopView.shared.superview != nil {
|
||||||
SharePopView.shared.dismiss(animated: false)
|
SharePopView.shared.dismiss(animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharePopView.shared.shareURL = shareURL
|
||||||
SharePopView.shared.bgView.alpha = 0
|
SharePopView.shared.bgView.alpha = 0
|
||||||
superView.addSubview(SharePopView.shared)
|
superView.addSubview(SharePopView.shared)
|
||||||
superView.bringSubviewToFront(SharePopView.shared)
|
superView.bringSubviewToFront(SharePopView.shared)
|
||||||
|
|
@ -65,29 +65,10 @@ class SharePopView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func shareToWeChat(scene: Int32) {
|
private func shareToWeChat(scene: Int32) {
|
||||||
guard let config = AppContextManager.shared.systemConfig,
|
|
||||||
let shareConfig = config.shareConfig else { return }
|
|
||||||
|
|
||||||
DLToast.showLoading()
|
|
||||||
|
|
||||||
let fallbackImage = UIImage(named: "Common/logo108")
|
|
||||||
let imageURL: URL? = {
|
|
||||||
if shareConfig.image.isEmpty { return nil }
|
|
||||||
return URL(string: shareConfig.image)
|
|
||||||
}()
|
|
||||||
|
|
||||||
let sendMessage: (UIImage?) -> Void = { [weak self] thumbImage in
|
|
||||||
guard let self = self else { return }
|
|
||||||
DLToast.dismiss()
|
|
||||||
let message = WXMediaMessage()
|
let message = WXMediaMessage()
|
||||||
message.title = shareConfig.title
|
message.title = kAppName
|
||||||
message.description = shareConfig.content
|
|
||||||
if let thumb = thumbImage {
|
|
||||||
message.setThumbImage(thumb)
|
|
||||||
}
|
|
||||||
|
|
||||||
let webpage = WXWebpageObject()
|
let webpage = WXWebpageObject()
|
||||||
webpage.webpageUrl = shareConfig.link
|
webpage.webpageUrl = shareURL
|
||||||
message.mediaObject = webpage
|
message.mediaObject = webpage
|
||||||
|
|
||||||
let req = SendMessageToWXReq()
|
let req = SendMessageToWXReq()
|
||||||
|
|
@ -96,21 +77,7 @@ class SharePopView: UIView {
|
||||||
req.scene = scene
|
req.scene = scene
|
||||||
WXApi.send(req)
|
WXApi.send(req)
|
||||||
|
|
||||||
self.dismiss()
|
dismiss()
|
||||||
}
|
|
||||||
|
|
||||||
if let url = imageURL {
|
|
||||||
KingfisherManager.shared.retrieveImage(with: url) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let value):
|
|
||||||
sendMessage(value.image)
|
|
||||||
case .failure:
|
|
||||||
sendMessage(fallbackImage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sendMessage(fallbackImage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UI
|
// MARK: - UI
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,6 @@ class VipRechargeVC: BaseViewController {
|
||||||
requestRechargeInfo()
|
requestRechargeInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func bindViewModel() {
|
private func bindViewModel() {
|
||||||
viewModel.output.sectionedItems
|
viewModel.output.sectionedItems
|
||||||
.bind(to: rootView.expenseCollectionView.rx.items(dataSource: dataSource))
|
.bind(to: rootView.expenseCollectionView.rx.items(dataSource: dataSource))
|
||||||
|
|
@ -107,29 +102,29 @@ class VipRechargeVC: BaseViewController {
|
||||||
let types = viewModel.payType.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
|
let types = viewModel.payType.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
|
||||||
let payType = types[rootView.selectedPayTypeTag]
|
let payType = types[rootView.selectedPayTypeTag]
|
||||||
|
|
||||||
// if payType == "apple" {
|
if payType == "apple" {
|
||||||
// IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in
|
IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in
|
||||||
// guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
// if success {
|
if success {
|
||||||
// self.showPayResultPop(showCloseBtn: false,
|
self.showPayResultPop(showCloseBtn: false,
|
||||||
// status: true,
|
status: true,
|
||||||
// title: "支付成功",
|
title: "支付成功",
|
||||||
// message: "恭喜您!成功开通\(self.viewModel.goodsName)",
|
message: "恭喜您!成功开通\(self.viewModel.goodsName)",
|
||||||
// confirmText: "确定", confirmBlock: {
|
confirmText: "确定", confirmBlock: {
|
||||||
// AppRouter.shared.popOrDismiss()
|
AppRouter.shared.popOrDismiss()
|
||||||
// }, cancelBlock: { })
|
}, cancelBlock: { })
|
||||||
// } else {
|
} else {
|
||||||
// self.showPayResultPop(status: false,
|
self.showPayResultPop(status: false,
|
||||||
// title: "支付失败",
|
title: "支付失败",
|
||||||
// message: msg ?? "很抱歉,请您重新支付!",
|
message: msg ?? "很抱歉,请您重新支付!",
|
||||||
// confirmText: "重新支付", confirmBlock: { },
|
confirmText: "重新支付", confirmBlock: { },
|
||||||
// cancelText: "取消", cancelBlock: {
|
cancelText: "取消", cancelBlock: {
|
||||||
// AppRouter.shared.popOrDismiss()
|
AppRouter.shared.popOrDismiss()
|
||||||
// })
|
})
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
DLToast.showLoading()
|
DLToast.showLoading()
|
||||||
OrderService.orderPayParams(goodsId: viewModel.goodsId,
|
OrderService.orderPayParams(goodsId: viewModel.goodsId,
|
||||||
|
|
@ -138,20 +133,16 @@ class VipRechargeVC: BaseViewController {
|
||||||
|
|
||||||
let payType = types[self.rootView.selectedPayTypeTag]
|
let payType = types[self.rootView.selectedPayTypeTag]
|
||||||
if payType == "weixin" { // 微信
|
if payType == "weixin" { // 微信
|
||||||
guard let partnerId = data["partnerId"] as? String,
|
// WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink)
|
||||||
let prepayId = data["prepayId"] as? String,
|
//
|
||||||
let package = data["package"] as? String,
|
// let request: PayReq = PayReq()
|
||||||
let nonceStr = data["nonceStr"] as? String,
|
// request.partnerId = model.partnerId
|
||||||
let timeStamp = data["timeStamp"] as? String,
|
// request.prepayId = model.prepayId
|
||||||
let sign = data["sign"] as? String else { return }
|
// request.package = model.packageValue
|
||||||
let request: PayReq = PayReq()
|
// request.nonceStr = model.nonceStr
|
||||||
request.partnerId = partnerId
|
// request.timeStamp = UInt32(model.timeStamp) ?? 0
|
||||||
request.prepayId = prepayId
|
// request.sign = model.sign
|
||||||
request.package = package
|
// WXApi.send(request)
|
||||||
request.nonceStr = nonceStr
|
|
||||||
request.timeStamp = UInt32(timeStamp) ?? 0
|
|
||||||
request.sign = sign
|
|
||||||
WXApi.send(request)
|
|
||||||
}
|
}
|
||||||
else if payType == "alipay" { // 支付宝
|
else if payType == "alipay" { // 支付宝
|
||||||
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
|
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class VipRechargeView: UIView {
|
||||||
.heightToWidth(267/375)
|
.heightToWidth(267/375)
|
||||||
|
|
||||||
vipRightsDetailView.layoutChain
|
vipRightsDetailView.layoutChain
|
||||||
.top(36 + kStatusBarHeight)
|
.top(36)
|
||||||
.right()
|
.right()
|
||||||
.height(24)
|
.height(24)
|
||||||
|
|
||||||
|
|
@ -562,7 +562,7 @@ class VipRechargeView: UIView {
|
||||||
let payTypeMap: [(String, String)] = types.compactMap {
|
let payTypeMap: [(String, String)] = types.compactMap {
|
||||||
if $0 == "weixin" { return ("wechat", "微信支付") }
|
if $0 == "weixin" { return ("wechat", "微信支付") }
|
||||||
if $0 == "alipay" { return ("alipay", "支付宝支付") }
|
if $0 == "alipay" { return ("alipay", "支付宝支付") }
|
||||||
// if $0 == "apple" { return ("apple", "苹果支付") }
|
if $0 == "apple" { return ("apple", "苹果支付") }
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard !payTypeMap.isEmpty else { return }
|
guard !payTypeMap.isEmpty else { return }
|
||||||
|
|
|
||||||
|
|
@ -168,11 +168,7 @@ class FullscreenWebView: WKWebView {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
fd_interactivePopDisabled = true
|
fd_interactivePopDisabled = true
|
||||||
if style == "default" {
|
if style == "default" {
|
||||||
let btn = UIButton(type: .system)
|
// setupLeftBarButtonItem(iconColor: "gray")
|
||||||
btn.setImage(UIImage(named: "Common/back"), for: .normal)
|
|
||||||
btn.tintColor = UIColor(hexStr: "#0F2846")
|
|
||||||
btn.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside)
|
|
||||||
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: btn)
|
|
||||||
}
|
}
|
||||||
// setupErrorView()
|
// setupErrorView()
|
||||||
setupView()
|
setupView()
|
||||||
|
|
@ -316,16 +312,16 @@ class FullscreenWebView: WKWebView {
|
||||||
// backItem.tag = 1
|
// backItem.tag = 1
|
||||||
|
|
||||||
let backBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 19, height: 19))
|
let backBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 19, height: 19))
|
||||||
backBtn.setImage(UIImage(named: "Common/back"), for: .normal)
|
backBtn.setImage(UIImage(named: "Common/back_\(iconColor)"), for: .normal)
|
||||||
backBtn.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside)
|
backBtn.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside)
|
||||||
backBtn.tintColor = .black
|
backBtn.tintColor = iconColor=="white" ? .white : .black
|
||||||
backBtn.tag = 1
|
backBtn.tag = 1
|
||||||
let backItem = UIBarButtonItem(customView: backBtn)
|
let backItem = UIBarButtonItem(customView: backBtn)
|
||||||
|
|
||||||
let popBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 19, height: 19))
|
let popBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 19, height: 19))
|
||||||
popBtn.setImage(UIImage(named: "Common/x_black"), for: .normal)
|
popBtn.setImage(UIImage(named: "Common/close_black"), for: .normal)
|
||||||
popBtn.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside)
|
popBtn.addTarget(self, action: #selector(leftBackClicked(_:)), for: .touchUpInside)
|
||||||
popBtn.tintColor = .black
|
popBtn.tintColor = iconColor=="white" ? .white : .black
|
||||||
popBtn.tag = 2
|
popBtn.tag = 2
|
||||||
|
|
||||||
let popItem = UIBarButtonItem(customView: popBtn)
|
let popItem = UIBarButtonItem(customView: popBtn)
|
||||||
|
|
|
||||||