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 */; };
|
||||
30EFF3E52FDAA93400EB35D4 /* PrivacyPolicyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EFF3E42FDAA93300EB35D4 /* PrivacyPolicyVC.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 */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
|
@ -600,10 +598,8 @@
|
|||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
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 */
|
||||
|
|
@ -636,7 +632,6 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
30EFF3E82FCA8C7000227D26 /* AuthenticationServices.framework in Frameworks */,
|
||||
D698E9C56D6F2C152772131D /* Pods_QuickLocation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -1012,7 +1007,6 @@
|
|||
30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */,
|
||||
30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */,
|
||||
30D87CDC2FDFF07500E958FD /* InteractionView.swift */,
|
||||
55BF752C2FFE53F70055DA57 /* LocationPermissionPopView.swift */,
|
||||
30A87A692FEF59E60095E7C6 /* SearchLocation */,
|
||||
30A87A5C2FEE711C0095E7C6 /* Bubble */,
|
||||
30CCDE4F2FE2782700F5214A /* SignIn */,
|
||||
|
|
@ -1654,7 +1648,6 @@
|
|||
47CD8471BE2146A2656CF27E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30EFF3E92FCA8C7000227D26 /* AuthenticationServices.framework */,
|
||||
475D33CAFA1E1911EB1F8D9F /* Pods_QuickLocation.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
|
|
@ -2074,7 +2067,6 @@
|
|||
30BF30102FED0C8E00D9CB52 /* ScheduleDetailVM.swift in Sources */,
|
||||
30DA36BD2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift in Sources */,
|
||||
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
|
||||
55BF752D2FFE53F70055DA57 /* LocationPermissionPopView.swift in Sources */,
|
||||
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */,
|
||||
30D74ABA2FEA37AD0050EB2C /* ScheduleModel.swift in Sources */,
|
||||
30CCDE512FE2785D00F5214A /* SignInVC.swift in Sources */,
|
||||
|
|
@ -2138,11 +2130,9 @@
|
|||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = QuickLocation/QuickLocation.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = LRDLWZ2Y83;
|
||||
DEVELOPMENT_TEAM = XSJJKSY9FB;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = QuickLocation/Info.plist;
|
||||
|
|
@ -2163,10 +2153,9 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuomeng.jisuloca;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisuloca;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = jsdw_dev;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
|
|
@ -2190,11 +2179,9 @@
|
|||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = QuickLocation/QuickLocation.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = LRDLWZ2Y83;
|
||||
DEVELOPMENT_TEAM = XSJJKSY9FB;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = QuickLocation/Info.plist;
|
||||
|
|
@ -2215,10 +2202,9 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuomeng.jisuloca;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisuloca;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = jsdw_dev;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
|
|
|
|||
|
|
@ -70,16 +70,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
|
||||
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)
|
||||
if url.scheme?.hasPrefix("Alipay") == true || url.host == "safepay" {
|
||||
AlipaySDK.defaultService().processOrder(withPaymentResult: url) { resultDic in
|
||||
|
|
@ -91,18 +81,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
}
|
||||
return true
|
||||
}
|
||||
// else if url.host == "pay" {
|
||||
else if url.host == "pay" {
|
||||
return WXApi.handleOpen(url, delegate: self)
|
||||
// }
|
||||
//
|
||||
// return true
|
||||
}
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication,
|
||||
continue userActivity: NSUserActivity,
|
||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||
print("📱 continue userActivity: \(userActivity.webpageURL?.absoluteString ?? "")")
|
||||
return WXApi.handleOpenUniversalLink(userActivity, delegate: self)
|
||||
return true
|
||||
}
|
||||
|
||||
func setupLocation() {
|
||||
|
|
@ -157,37 +140,11 @@ extension AppDelegate {
|
|||
}
|
||||
|
||||
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) {
|
||||
print(req)
|
||||
}
|
||||
|
||||
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),
|
||||
let response: PayResp = resp as? PayResp {
|
||||
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和签名)
|
||||
var isReLogin: DefaultsKey<Bool> { .init("isReLogin", defaultValue: false) }
|
||||
/// 显示引导页
|
||||
var isShowGuide: DefaultsKey<Bool> { .init("isShowGuide", defaultValue: true) }
|
||||
/// 气泡持续时间
|
||||
var bubbleKeepTimes: DefaultsKey<Date?> { .init("bubbleKeepTimes") }
|
||||
var guideShowVersion: DefaultsKey<Int?> { .init("guideShowVersion") }
|
||||
}
|
||||
|
||||
/// 通知常量
|
||||
|
|
@ -37,12 +35,6 @@ extension Notification.Name {
|
|||
static let ShowMemberLocationNotification = Notification.Name("ShowMemberLocationNotification")
|
||||
/// 支付宝/微信支付结果回调
|
||||
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 = "" {
|
||||
didSet {
|
||||
navigationItem.title = navTitle
|
||||
backButton.setTitle(" \(navTitle)", for: .normal)
|
||||
// navigationItem.title = navTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,13 +102,32 @@ class BaseViewController: UIViewController {
|
|||
dismissItem.tintColor = .white
|
||||
navigationItem.leftBarButtonItem = dismissItem
|
||||
} else if let count = navigationController?.viewControllers.count, count > 1 {
|
||||
let leftBackItem = UIBarButtonItem(image: UIImage(named: "Common/back"),
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(leftBackClicked(_:)))
|
||||
navigationItem.leftBarButtonItem = leftBackItem
|
||||
let leftBarBtn = UIBarButtonItem (customView: backButton)
|
||||
|
||||
//用于消除左边空隙,要不然按钮顶不到最前面
|
||||
let spacer = UIBarButtonItem (barButtonSystemItem: .fixedSpace, target: nil, action: nil)
|
||||
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: - 验证码
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ final class MainTabBarController: UITabBarController {
|
|||
|
||||
viewControllers = navControllers
|
||||
tabBar.isHidden = true
|
||||
tabBar.removeFromSuperview()
|
||||
|
||||
for nav in navControllers {
|
||||
nav.delegate = self
|
||||
|
|
@ -72,7 +71,7 @@ final class MainTabBarController: UITabBarController {
|
|||
NSLayoutConstraint.activate([
|
||||
customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
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)
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,20 +92,6 @@ class AppContextManager: NSObject {
|
|||
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() {
|
||||
Defaults[\.loginToken] = nil
|
||||
Defaults[\.userConfig] = nil
|
||||
|
|
|
|||
|
|
@ -68,8 +68,6 @@ struct SystemConfigModel: Mappable {
|
|||
var popupConfig: PopupWindowConfigModel?
|
||||
/// 弹窗时间配置
|
||||
var timeConfig: PopupWindowTimeModel?
|
||||
/// 微信分享配置
|
||||
var shareConfig: WxShareConfigModel?
|
||||
|
||||
/// 聊天界面的警告说明
|
||||
var chatWarning: String = ""
|
||||
|
|
@ -77,12 +75,6 @@ struct SystemConfigModel: Mappable {
|
|||
/// 圈子里的banner
|
||||
var groupBannerList: [String] = []
|
||||
|
||||
/// 引导页显示开关
|
||||
var guideDisplay: Bool = true
|
||||
|
||||
/// 全局控制显示隐藏 弹窗和充值
|
||||
var globalDisplay: Bool = true
|
||||
|
||||
init?(map: Map) {
|
||||
|
||||
}
|
||||
|
|
@ -92,18 +84,15 @@ struct SystemConfigModel: Mappable {
|
|||
groupBannerList <- map["client.team.ad", nested: false]
|
||||
popupConfig <- map["client.popup.display", 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 {
|
||||
/// 是否弹vip弹窗
|
||||
var pay_pop: Bool = true
|
||||
var pay_pop: Bool = false
|
||||
|
||||
/// 搜索位置弹窗
|
||||
var search_phone: Bool = true
|
||||
var search_phone: Bool = false
|
||||
|
||||
|
||||
init?(map: Map) {
|
||||
|
|
@ -143,28 +132,3 @@ struct PopupWindowTimeModel: Mappable {
|
|||
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 RxSwift
|
||||
import CommonCrypto
|
||||
#if !targetEnvironment(simulator)
|
||||
import GeYanSdk
|
||||
#endif
|
||||
|
||||
class ApiManager: NSObject {
|
||||
|
||||
|
|
@ -160,8 +157,6 @@ extension ApiManager {
|
|||
// MARK: - 处理Token过期
|
||||
private func handleTokenExpired(msg: String?) {
|
||||
MainAsync {
|
||||
NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil)
|
||||
GeYanSdk.deletePreResultCache()
|
||||
AppContextManager.shared.deleteAccount()
|
||||
GroupIMService.shared.logout()
|
||||
DLToast.showError(text: msg ?? "登录失效,请重新登录!") {
|
||||
|
|
|
|||
|
|
@ -183,8 +183,6 @@ struct GroupMemberModel: Mappable, Equatable {
|
|||
}
|
||||
return UIImage(named: "VipIcon/\(level)") ?? UIImage()
|
||||
}
|
||||
/// 状态:0、无状态 1、SOS 2、气泡等
|
||||
var status: Int = 0
|
||||
/// 是否在线
|
||||
var is_online: Bool = false
|
||||
/// 设备电量
|
||||
|
|
@ -230,7 +228,6 @@ struct GroupMemberModel: Mappable, Equatable {
|
|||
last_position <- map["last_position"]
|
||||
is_online <- map["is_online"]
|
||||
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">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:smartdrive.zuom8.cn</string>
|
||||
|
|
|
|||
|
|
@ -398,9 +398,9 @@ class CreateGroupView: UIView {
|
|||
lazy var submitBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
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.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
||||
btn.setBackgroundImage(UIImage(named: "Common/gradient_bg"), for: .normal)
|
||||
btn.cornerRadius = 25
|
||||
|
||||
return btn
|
||||
|
|
|
|||
|
|
@ -87,8 +87,6 @@ class CreateGroupViewModel {
|
|||
}
|
||||
}, onError: { (error) in
|
||||
guard let code = error.underlyingError?.code else { return }
|
||||
|
||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
||||
if code == 20009 { // "您创建的圈子个数已达上限,请升级会员等级"
|
||||
CreateGroupVipPopView.show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,10 +49,7 @@ final class GroupChatVC: BaseViewController {
|
|||
|
||||
// 并行加载:业务接口 + IM SDK 互不依赖,同时发起
|
||||
requestGroupInfoByKey()
|
||||
GroupIMService.shared.ensureLogin { [weak self] success in
|
||||
guard let self = self, success else { return }
|
||||
self.viewModel.loadMessages()
|
||||
}
|
||||
viewModel.loadMessages()
|
||||
|
||||
// 处理系统配置
|
||||
guard let config = AppContextManager.shared.systemConfig else { return }
|
||||
|
|
@ -99,7 +96,7 @@ final class GroupChatVC: BaseViewController {
|
|||
// 收起表情/语音面板
|
||||
self.rootView.dismissAllPanels(excludeTextField: true)
|
||||
UIView.animate(withDuration: duration) {
|
||||
self.rootView.bottomBar.layoutChain.bottom(height + 20)
|
||||
self.rootView.bottomBar.layoutChain.bottom(height + kSafeBottomMargin + 20)
|
||||
}
|
||||
self.scrollToBottom()
|
||||
})
|
||||
|
|
@ -154,7 +151,7 @@ final class GroupChatVC: BaseViewController {
|
|||
private func scrollToBottom() {
|
||||
let count = dataSource.sectionModels.first?.items.count ?? 0
|
||||
guard count > 0 else { return }
|
||||
DispatchQueue.main.async {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
self.rootView.tableView.layoutIfNeeded()
|
||||
let indexPath = IndexPath(row: count - 1, section: 0)
|
||||
self.rootView.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
|
||||
|
|
@ -295,7 +292,7 @@ final class GroupChatVC: BaseViewController {
|
|||
let offset: CGFloat = show ? 252 : 0
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
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()
|
||||
}
|
||||
|
|
@ -309,7 +306,7 @@ final class GroupChatVC: BaseViewController {
|
|||
self.rootView.emojiPanelView.isHidden = !show
|
||||
let offset: CGFloat = show ? 220 : 0
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
self.rootView.bottomBar.layoutChain.bottom(20 + offset)
|
||||
self.rootView.bottomBar.layoutChain.bottom(kSafeBottomMargin + 20 + offset)
|
||||
}
|
||||
// completion: { _ in
|
||||
// let offset: CGFloat = self.rootView.tableView.contentSize.height
|
||||
|
|
|
|||
|
|
@ -33,8 +33,6 @@ class GroupMemberListVC: BaseViewController {
|
|||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
||||
rootView.unlockVipMaskView.isHidden = AppContextManager.shared.vip > 1
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -220,9 +220,7 @@ final class GroupViewController: BaseViewController {
|
|||
GroupService.groupInfo().subscribe { response in
|
||||
guard let model = response.model else { return }
|
||||
self.viewModel.groupList = model.groups
|
||||
DispatchQueue.global(qos: .default).async { [weak self] in
|
||||
self?.loadIMData()
|
||||
}
|
||||
self.loadIMData()
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import UIKit
|
|||
import ObjectMapper
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import Kingfisher
|
||||
|
||||
class InviteJoinVC: BaseViewController {
|
||||
|
||||
|
|
@ -29,64 +28,8 @@ class InviteJoinVC: BaseViewController {
|
|||
|
||||
// 分享
|
||||
rootView.shareAppBtn.rx.tap.subscribe(onNext: { _ in
|
||||
SharePopView.show()
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
// 分享邀请码
|
||||
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)
|
||||
}
|
||||
let url = "https://smartdrive.zuom8.cn/jisu/share/user/"
|
||||
SharePopView.show(shareURL: url)
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
guard let model = groupInModel else { return }
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class InviteJoinView: UIView {
|
|||
inviteInfoView.layoutChain
|
||||
.topToBottomOfView(groupIcon, offset: 50)
|
||||
.edgesHorzontal(30)
|
||||
// .bottomToTopOfView(shareAppBtn, offset: -32)
|
||||
.bottomToTopOfView(shareAppBtn, offset: -32)
|
||||
|
||||
inviteInfoTitleLab.layoutChain
|
||||
.top(20)
|
||||
|
|
@ -265,9 +265,10 @@ class InviteJoinView: UIView {
|
|||
lazy var shareCodeBtn: 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.setTitleColor(UIColor(hexStr: "#0F2846"), for: .normal)
|
||||
btn.backgroundColor = .clear
|
||||
btn.setBackgroundImage(UIImage(named: "Common/gradient_bg"), for: .normal)
|
||||
btn.cornerRadius = 25
|
||||
return btn
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import SwiftyUserDefaults
|
||||
|
||||
class CreateBubbleVC: BaseViewController {
|
||||
|
||||
|
|
@ -32,13 +31,6 @@ class CreateBubbleVC: BaseViewController {
|
|||
|
||||
doneBtn.rx.tap.subscribe(onNext: { [weak self] in
|
||||
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 {
|
||||
let hours = self.rootView.createBubbleTiemView.selectedHour.value
|
||||
requestSetBubble(enable: true, keep_time: hours)
|
||||
|
|
@ -57,13 +49,6 @@ class CreateBubbleVC: BaseViewController {
|
|||
self.requestSetBubble(enable: false, keep_time: -1)
|
||||
}, cancelText: "取消")
|
||||
}).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) {
|
||||
|
|
@ -72,17 +57,13 @@ class CreateBubbleVC: BaseViewController {
|
|||
DLToast.dismiss()
|
||||
guard enable else {
|
||||
DLToast.show(text: "气泡已弹出") {
|
||||
Defaults[\.bubbleKeepTimes] = nil
|
||||
AppContextManager.shared.updateAccountStatus(0)
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}
|
||||
return
|
||||
}
|
||||
AppContextManager.shared.updateAccountStatus(2)
|
||||
self.rootView.navTitleLabel.text = "活动气泡"
|
||||
self.rootView.createBubbleDoneView.messageLab.text = self.rootView.createBubbleTipsView.messageText
|
||||
let endDate = Calendar.current.date(byAdding: .hour, value: keep_time, to: Date()) ?? Date()
|
||||
Defaults[\.bubbleKeepTimes] = endDate
|
||||
self.rootView.createBubbleDoneView.startCountdown(endDate: endDate)
|
||||
self.rootView.createBubbleTipsView.isHidden = true
|
||||
self.rootView.createBubbleDoneView.isHidden = false
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import UIKit
|
|||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import Lottie
|
||||
|
||||
class GroupMemberView: UIView {
|
||||
|
||||
|
|
@ -185,13 +184,6 @@ class GroupMemberCell: UITableViewCell {
|
|||
onlineStatusLab.text = model.lastUpdateText
|
||||
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() {
|
||||
|
|
@ -271,7 +263,7 @@ class GroupMemberCell: UITableViewCell {
|
|||
tagView.layoutChain
|
||||
.leftToRightOfView(nameLab, offset: 4)
|
||||
.centerY(nameLab)
|
||||
.height(30)
|
||||
.height(20)
|
||||
|
||||
ownView.layoutChain.height(15)
|
||||
|
||||
|
|
@ -362,10 +354,10 @@ class GroupMemberCell: UITableViewCell {
|
|||
}()
|
||||
|
||||
lazy var tagView: UIStackView = {
|
||||
let view = UIStackView(arrangedSubviews: [ownView, offlineView, sosLottieView, shieldIconView])
|
||||
let view = UIStackView(arrangedSubviews: [ownView, offlineView])
|
||||
view.axis = .horizontal
|
||||
view.alignment = .center
|
||||
view.spacing = 5
|
||||
view.spacing = 0
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
|
@ -409,28 +401,6 @@ class GroupMemberCell: UITableViewCell {
|
|||
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 = {
|
||||
let view = UIView()
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class HomeView: UIView {
|
|||
mv.showsScale = false
|
||||
mv.isRotateEnabled = 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
|
||||
}()
|
||||
#else
|
||||
|
|
@ -92,6 +92,7 @@ class HomeView: UIView {
|
|||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
|
||||
groupMemberView.tableView.rx.setDelegate(self)
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
|
|
@ -99,11 +100,6 @@ class HomeView: UIView {
|
|||
searchLottieView.rx.tapGesture.subscribe { _ in
|
||||
AppRouter.push(Route.searchLocation)
|
||||
}.disposed(by: disposeBag)
|
||||
|
||||
// 感叹号
|
||||
warningIconView.rx.tapGesture.subscribe { _ in
|
||||
LocationPermissionPopView.show()
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func setupUI() {
|
||||
|
|
@ -122,7 +118,6 @@ class HomeView: UIView {
|
|||
addSubview(messageView)
|
||||
addSubview(messageStackView)
|
||||
addSubview(toolsView)
|
||||
addSubview(warningIconView)
|
||||
addSubview(searchLottieView)
|
||||
addSubview(locationView)
|
||||
locationView.addSubview(locationIconView)
|
||||
|
|
@ -188,12 +183,6 @@ class HomeView: UIView {
|
|||
.width(40)
|
||||
.centerY(self, offset: -50)
|
||||
|
||||
warningIconView.layoutChain
|
||||
.bottomToTopOfView(toolsView, offset: -10)
|
||||
.centerX(toolsView)
|
||||
.width(28)
|
||||
.heightToWidth(1)
|
||||
|
||||
searchLottieView.layoutChain
|
||||
.centerY(toolsView)
|
||||
.right()
|
||||
|
|
@ -202,7 +191,7 @@ class HomeView: UIView {
|
|||
|
||||
locationView.layoutChain
|
||||
.right(15)
|
||||
.bottomToView(toolsView, offset: -10)
|
||||
.bottomToView(toolsView)
|
||||
.width(40)
|
||||
.height(40)
|
||||
|
||||
|
|
@ -247,7 +236,6 @@ class HomeView: UIView {
|
|||
quickMessageView.isHidden = false
|
||||
interactionView.isHidden = false
|
||||
toolsView.isHidden = true
|
||||
searchLottieView.isHidden = true
|
||||
quickMessageView.frame.origin.y = kScreenHeight - 384 - 93
|
||||
interactionView.frame.origin.y = kScreenHeight - 384
|
||||
}
|
||||
|
|
@ -258,7 +246,6 @@ class HomeView: UIView {
|
|||
quickMessageView.isHidden = true
|
||||
interactionView.isHidden = true
|
||||
toolsView.isHidden = false
|
||||
searchLottieView.isHidden = AppContextManager.shared.systemConfig?.globalDisplay == false
|
||||
onDismissPanel?()
|
||||
}
|
||||
|
||||
|
|
@ -648,15 +635,6 @@ class HomeView: UIView {
|
|||
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: - 侧边工具栏
|
||||
lazy var toolsView: UIStackView = {
|
||||
let view = UIStackView(arrangedSubviews: [bubbleView, signInView, sosView, scheduleView])
|
||||
|
|
|
|||
|
|
@ -46,14 +46,13 @@ class HomeViewController: BaseViewController {
|
|||
private var subscribedMemberIds: [String] = []
|
||||
/// 底部成员面板是否显示
|
||||
private var isMemberPanelShown = false
|
||||
private var isSOSPlaying = false
|
||||
/// 记录每个成员最后一次收到位置的时间(MQTT userId → Date)
|
||||
private var lastUpdateTimes: [String: Date] = [:]
|
||||
/// 离线检测定时器
|
||||
private var offlineCheckTimer: Timer?
|
||||
private var popupQueue: PopupQueueManager?
|
||||
|
||||
private static var sosPlayerKey: UInt8 = 0
|
||||
private var sosPlayerKey: UInt8 = 0
|
||||
private var groupRefreshWorkItem: DispatchWorkItem?
|
||||
private var groupSwitchWorkItem: DispatchWorkItem?
|
||||
|
||||
|
|
@ -74,6 +73,8 @@ class HomeViewController: BaseViewController {
|
|||
setupHeading()
|
||||
reactiveAction()
|
||||
|
||||
requestUserConfig()
|
||||
|
||||
rootView.quickMessageView.tagListView.delegate = self
|
||||
// MQTT 位置上报
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
|
|
@ -83,13 +84,6 @@ class HomeViewController: BaseViewController {
|
|||
override func viewDidAppear(_ animated: Bool) {
|
||||
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 位置上报
|
||||
|
|
@ -195,27 +189,9 @@ class HomeViewController: BaseViewController {
|
|||
|
||||
// User Config刷新
|
||||
NotificationCenter.default.rx.notification(.RefreshUserConfigNotification, object: nil)
|
||||
.startWith(Notification(name: .RefreshUserConfigNotification))
|
||||
.do(onNext: { [weak self] _ in
|
||||
self?.popupQueue = nil
|
||||
})
|
||||
.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)
|
||||
.subscribe { [weak self] notification in
|
||||
self?.requestUserConfig()
|
||||
}.disposed(by: disposeBag)
|
||||
|
||||
// 圈子刷新
|
||||
NotificationCenter.default.rx.notification(.RefreshGroupInfoNotification, object: nil)
|
||||
|
|
@ -272,8 +248,10 @@ class HomeViewController: BaseViewController {
|
|||
}
|
||||
|
||||
// 分享
|
||||
rootView.interactionView.onShare = {
|
||||
SharePopView.show()
|
||||
rootView.interactionView.onShare = { [weak self] in
|
||||
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
|
||||
|
||||
/// 获取用户配置
|
||||
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
|
||||
func getUserIMToken() {
|
||||
DLToast.showLoading()
|
||||
|
|
@ -469,8 +466,8 @@ class HomeViewController: BaseViewController {
|
|||
let newMembers = others + [me]
|
||||
members = newMembers
|
||||
currentUserMember = me
|
||||
// 从 members 中过滤出在线成员用于地图标注(当前用户坐标无效,由 GPS 回调单独添加)
|
||||
let onlineMembers = newMembers.filter { $0.isOnline && !$0.isCurrentUser }
|
||||
// 从 members 中过滤出在线成员用于地图标注
|
||||
let onlineMembers = newMembers.filter { $0.isOnline }
|
||||
|
||||
guard let mapView = rootView.mapView else { return }
|
||||
|
||||
|
|
@ -480,33 +477,10 @@ class HomeViewController: BaseViewController {
|
|||
|
||||
let toRemove = existing.filter { !onlineIDs.contains($0.member.id) }
|
||||
mapView.removeAnnotations(toRemove)
|
||||
if toRemove.contains(where: { $0 === currentUserAnnotation }) {
|
||||
currentUserAnnotation = nil
|
||||
}
|
||||
|
||||
let toAdd = onlineMembers.filter { !existingIDs.contains($0.id) }
|
||||
let annotations = toAdd.map { MemberAnnotation(member: $0) }
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -664,36 +638,16 @@ 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": // 求助
|
||||
self.requestGroupInfo(isDefaultGroup: false)
|
||||
guard let userId = msg.data?.user_id,
|
||||
userId != AppContextManager.shared.userId,
|
||||
topic.contains(userId) else { return }
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.updateMemberStatus(userId: userId, status: 1)
|
||||
|
||||
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.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
|
||||
}
|
||||
userId != AppContextManager.shared.userId else { return }
|
||||
playSOSAlarm()
|
||||
rootView.sosPopView.isHidden = false
|
||||
rootView.sosPopView.play(toFrame: 30.0) { completed in
|
||||
guard completed else { return }
|
||||
self.rootView.sosPopView.stop()
|
||||
self.rootView.sosPopView.isHidden = true
|
||||
}
|
||||
case "join": // 圈子有成员加入
|
||||
guard let gk = msg.data?.group_key,
|
||||
|
|
@ -844,22 +798,6 @@ extension HomeViewController: MAMapViewDelegate {
|
|||
|
||||
if let ann = currentUserAnnotation {
|
||||
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 {
|
||||
|
|
@ -873,8 +811,8 @@ extension HomeViewController: MAMapViewDelegate {
|
|||
extension HomeViewController {
|
||||
|
||||
private var sosPlayer: AVAudioPlayer? {
|
||||
get { return objc_getAssociatedObject(self, &HomeViewController.sosPlayerKey) as? AVAudioPlayer }
|
||||
set { objc_setAssociatedObject(self, &HomeViewController.sosPlayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
|
||||
get { return objc_getAssociatedObject(self, &sosPlayerKey) as? AVAudioPlayer }
|
||||
set { objc_setAssociatedObject(self, &sosPlayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
|
||||
}
|
||||
|
||||
private func playSOSAlarm() {
|
||||
|
|
@ -897,10 +835,14 @@ extension HomeViewController {
|
|||
return
|
||||
}
|
||||
print("✅ SOS: playing alarm")
|
||||
player.numberOfLoops = -1
|
||||
player.numberOfLoops = 0
|
||||
player.volume = 1.0
|
||||
player.play()
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
init() {
|
||||
output = Output(
|
||||
|
|
|
|||
|
|
@ -108,10 +108,7 @@ class InteractionView: UIView {
|
|||
let pages = (items.count + InteractionView.emojiPerPage - 1) / InteractionView.emojiPerPage
|
||||
self.emojiPageControl.numberOfPages = max(pages, 1)
|
||||
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)
|
||||
}
|
||||
|
|
@ -520,11 +517,6 @@ extension InteractionView: UICollectionViewDelegate {
|
|||
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) {
|
||||
guard collectionView == emojiCollectionView else { return }
|
||||
(cell as? EmojiPanelCell)?.stopAnimation()
|
||||
|
|
@ -536,46 +528,29 @@ final class EmojiPanelCell: UICollectionViewCell {
|
|||
|
||||
private static var animationCache: [String: LottieAnimation] = [:]
|
||||
private static var loadingCompletions: [String: [(LottieAnimation?) -> Void]] = [:]
|
||||
private static let cacheLock = NSLock()
|
||||
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
|
||||
}
|
||||
}()
|
||||
private static let animationQueue = DispatchQueue(label: "com.quicklocation.emoji.animation", qos: .userInitiated)
|
||||
|
||||
static func loadAnimation(for name: String, completion: @escaping (LottieAnimation?) -> Void) {
|
||||
animationQueue.async {
|
||||
Self.cacheLock.lock()
|
||||
if let cached = Self.animationCache[name] {
|
||||
Self.cacheLock.unlock()
|
||||
if let cached = animationCache[name] {
|
||||
DispatchQueue.main.async { completion(cached) }
|
||||
return
|
||||
}
|
||||
|
||||
if Self.loadingCompletions[name] != nil {
|
||||
Self.loadingCompletions[name]?.append(completion)
|
||||
Self.cacheLock.unlock()
|
||||
if loadingCompletions[name] != nil {
|
||||
loadingCompletions[name]?.append(completion)
|
||||
return
|
||||
}
|
||||
|
||||
Self.loadingCompletions[name] = [completion]
|
||||
Self.cacheLock.unlock()
|
||||
|
||||
loadingCompletions[name] = [completion]
|
||||
let animation: LottieAnimation?
|
||||
if let path = Self.emojiPathMap[name] {
|
||||
if let path = Bundle.main.path(forResource: name, ofType: "json") {
|
||||
animation = LottieAnimation.filepath(path)
|
||||
} else {
|
||||
animation = nil
|
||||
}
|
||||
|
||||
Self.cacheLock.lock()
|
||||
Self.animationCache[name] = animation
|
||||
let completions = Self.loadingCompletions.removeValue(forKey: name) ?? []
|
||||
Self.cacheLock.unlock()
|
||||
|
||||
animationCache[name] = animation
|
||||
let completions = loadingCompletions.removeValue(forKey: name) ?? []
|
||||
DispatchQueue.main.async {
|
||||
completions.forEach { $0(animation) }
|
||||
}
|
||||
|
|
@ -583,6 +558,7 @@ final class EmojiPanelCell: UICollectionViewCell {
|
|||
}
|
||||
|
||||
private var animationName: String?
|
||||
private var shouldPlayAnimation = false
|
||||
|
||||
private let lottieView: LottieAnimationView = {
|
||||
let v = LottieAnimationView()
|
||||
|
|
@ -622,28 +598,36 @@ final class EmojiPanelCell: UICollectionViewCell {
|
|||
|
||||
func configure(name: String) {
|
||||
animationName = name
|
||||
shouldPlayAnimation = false
|
||||
lottieView.stop()
|
||||
lottieView.animation = nil
|
||||
lottieView.currentProgress = 0
|
||||
Self.loadAnimation(for: name) { [weak self] animation in
|
||||
guard let self = self, self.animationName == name else { return }
|
||||
self.lottieView.animation = animation
|
||||
self.lottieView.currentProgress = 0
|
||||
self.lottieView.play()
|
||||
if self.shouldPlayAnimation {
|
||||
self.lottieView.play()
|
||||
}
|
||||
}
|
||||
|
||||
lockView.isHidden = AppContextManager.shared.vip > 1
|
||||
}
|
||||
|
||||
func playAnimation() {
|
||||
shouldPlayAnimation = true
|
||||
lottieView.play()
|
||||
}
|
||||
|
||||
func stopAnimation() {
|
||||
shouldPlayAnimation = false
|
||||
lottieView.stop()
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
animationName = nil
|
||||
shouldPlayAnimation = false
|
||||
lottieView.stop()
|
||||
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,27 +23,35 @@ class SOSViewController: BaseViewController {
|
|||
|
||||
// Do any additional setup after loading the view.
|
||||
fd_interactivePopDisabled = true
|
||||
// requestUserInfo()
|
||||
|
||||
rootView.didSendSOS = { status in
|
||||
self.requestSOS(status)
|
||||
}
|
||||
}
|
||||
|
||||
guard AppContextManager.shared.status == 1 else { return }
|
||||
self.rootView.countDownView.alpha = 1
|
||||
self.rootView.countDownFinish = true
|
||||
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
|
||||
self.rootView.countDownLottieView.animation = LottieAnimation.filepath(path)
|
||||
self.rootView.countDownLottieView.loopMode = .loop
|
||||
self.rootView.countDownLottieView.play()
|
||||
self.rootView.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
|
||||
}
|
||||
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.countDownFinish = true
|
||||
if let path = Bundle.main.path(forResource: "red-exclamation", ofType: "json") {
|
||||
self.rootView.countDownLottieView.animation = LottieAnimation.filepath(path)
|
||||
self.rootView.countDownLottieView.loopMode = .loop
|
||||
self.rootView.countDownLottieView.play()
|
||||
self.rootView.countDownTipsLab.text = "已将你的SOS和位置发送到你的圈子和紧急联系人。\n\n您的 SOS 将发送给5个人"
|
||||
}
|
||||
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestSOS(_ enable: Bool) {
|
||||
dl.showLoading()
|
||||
SystemService.sos(enable: enable).subscribe(onNext: { response in
|
||||
self.dl.show(text: enable ? "发送成功" : "取消成功") {
|
||||
AppContextManager.shared.updateAccountStatus(enable ? 1 : 0)
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}
|
||||
}, onError: { (error) in }).disposed(by: disposeBag)
|
||||
|
|
|
|||
|
|
@ -30,29 +30,39 @@ class LaunchViewController: BaseViewController {
|
|||
setupLayout()
|
||||
|
||||
experienceBtn.rx.tap.subscribe(onNext: { _ in
|
||||
Defaults[\.isShowGuide] = false
|
||||
// Defaults[\.guideShowVersion] = self.showVersion
|
||||
AppDelegate.shared.showMainViewController()
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
|
||||
getUserConfig()
|
||||
|
||||
navigateAfterDelay()
|
||||
}
|
||||
|
||||
private func navigateAfterDelay() {
|
||||
Observable.just(())
|
||||
.delay(.milliseconds(2500), scheduler: MainScheduler.instance)
|
||||
.subscribe(onNext: { [weak self] in
|
||||
guard let systemConfig = AppContextManager.shared.systemConfig else { return }
|
||||
if systemConfig.guideDisplay, Defaults[\.isShowGuide] {
|
||||
self?.collectionView.isHidden = false
|
||||
self?.pageControl.isHidden = false
|
||||
self?.experienceBtn.isHidden = false
|
||||
} else {
|
||||
AppDelegate.shared.showMainViewController()
|
||||
}
|
||||
self?.navigateToNext()
|
||||
})
|
||||
.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() {
|
||||
SystemService.userConfig().subscribe(onNext: { response in
|
||||
|
|
@ -72,11 +82,11 @@ class LaunchViewController: BaseViewController {
|
|||
|
||||
/// 获取用户IM Token
|
||||
func getUserIMToken() {
|
||||
DLToast.showLoading()
|
||||
UserService.imToken().subscribe(onNext: { response in
|
||||
guard let data = response.data, let token = data["token"] as? String else { return }
|
||||
AppContextManager.shared.imToken = token
|
||||
GroupIMService.shared.login { _ in }
|
||||
self.navigateAfterDelay()
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,14 +68,13 @@ class LoginView: UIView {
|
|||
addSubview(bgMaskImage)
|
||||
addSubview(backBtn)
|
||||
addSubview(guestLoginButton)
|
||||
addSubview(inputContainerView)
|
||||
inputContainerView.addSubview(welcomeLabel)
|
||||
inputContainerView.addSubview(phoneNumberLabel)
|
||||
inputContainerView.addSubview(carrierLabel)
|
||||
inputContainerView.addSubview(otherPhoneView)
|
||||
addSubview(welcomeLabel)
|
||||
addSubview(phoneNumberLabel)
|
||||
addSubview(carrierLabel)
|
||||
addSubview(otherPhoneView)
|
||||
otherPhoneView.addSubview(phoneInputView)
|
||||
otherPhoneView.addSubview(smsCodeInputView)
|
||||
inputContainerView.addSubview(loginButton)
|
||||
addSubview(loginButton)
|
||||
addSubview(appleLoginBtn)
|
||||
addSubview(wechatLoginBtn)
|
||||
addSubview(phoneLoginBtn)
|
||||
|
|
@ -99,14 +98,8 @@ class LoginView: UIView {
|
|||
.width(85)
|
||||
.height(29)
|
||||
|
||||
inputContainerView.layoutChain
|
||||
.centerX()
|
||||
.centerY(self, offset: -17)
|
||||
.edgesHorzontal()
|
||||
.height(306)
|
||||
|
||||
welcomeLabel.layoutChain
|
||||
.top(0)
|
||||
.top(202 - kNaviHeight)
|
||||
.centerX()
|
||||
.edgesHorzontal()
|
||||
|
||||
|
|
@ -168,7 +161,7 @@ class LoginView: UIView {
|
|||
agreementView.layoutChain
|
||||
.leftToView(loginButton)
|
||||
.rightToView(loginButton)
|
||||
.bottom(kSafeBottomMargin + 20)
|
||||
.bottom(55)
|
||||
.height(30)
|
||||
|
||||
agreementLabel.layoutChain.edges()
|
||||
|
|
@ -185,7 +178,7 @@ class LoginView: UIView {
|
|||
.top()
|
||||
|
||||
phoneLoginBtn.layoutChain
|
||||
.bottomToTopOfView(agreementView, offset: -30)
|
||||
.bottomToTopOfView(agreementView, offset: -81)
|
||||
.centerX()
|
||||
.width(38)
|
||||
.heightToWidth(1)
|
||||
|
|
@ -265,13 +258,6 @@ class LoginView: UIView {
|
|||
return label
|
||||
}()
|
||||
|
||||
// 输入区域容器
|
||||
lazy var inputContainerView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
// 其他手机号登录
|
||||
lazy var otherPhoneView: UIView = {
|
||||
let view = UIView()
|
||||
|
|
@ -408,6 +394,7 @@ class LoginView: UIView {
|
|||
textView.backgroundColor = .clear
|
||||
textView.isEditable = false
|
||||
textView.isScrollEnabled = false
|
||||
textView.isSelectable = false
|
||||
textView.linkTextAttributes = [:]
|
||||
|
||||
return textView
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import UIKit
|
|||
import SnapKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import AuthenticationServices
|
||||
#if !targetEnvironment(simulator)
|
||||
import GeYanSdk
|
||||
#endif
|
||||
|
|
@ -91,67 +90,27 @@ class LoginViewController: BaseViewController {
|
|||
|
||||
rootView.wechatLoginBtn.rx.tap
|
||||
.subscribe(onNext: { [weak self] in
|
||||
guard self?.isAgreed == true else {
|
||||
DLToast.showError(text: "请先同意用户协议")
|
||||
return
|
||||
}
|
||||
self?.viewModel.performWechatLogin()
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
rootView.appleLoginBtn.rx.tap
|
||||
.subscribe(onNext: { [weak self] in
|
||||
guard self?.isAgreed == true else {
|
||||
DLToast.showError(text: "请先同意用户协议")
|
||||
return
|
||||
}
|
||||
self?.viewModel.performAppleLogin()
|
||||
})
|
||||
.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
|
||||
.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)
|
||||
|
||||
// viewModel.loginSuccess
|
||||
// .subscribe(onNext: { [weak self] in
|
||||
//
|
||||
// })
|
||||
// .disposed(by: disposeBag)
|
||||
viewModel.loginSuccess
|
||||
.subscribe(onNext: { [weak self] in
|
||||
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - API 验证码
|
||||
|
|
@ -191,40 +150,3 @@ extension LoginViewController: UITextViewDelegate {
|
|||
|
||||
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 RxSwift
|
||||
import SwiftyUserDefaults
|
||||
import AuthenticationServices
|
||||
#if !targetEnvironment(simulator)
|
||||
import GeYanSdk
|
||||
#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 {
|
||||
|
||||
/// 验证码接口返回值
|
||||
|
|
@ -51,86 +18,47 @@ final class LoginViewModel: BaseViewModel {
|
|||
let oneClickLoginResult = PublishSubject<Bool>()
|
||||
let loginSuccess = PublishSubject<Void>()
|
||||
|
||||
var onAppleLoginRequest: ((ASAuthorizationController) -> Void)?
|
||||
|
||||
#if !targetEnvironment(simulator)
|
||||
lazy var gyAuthVM: GyAuthViewModel = {
|
||||
var gyAuthVM: GyAuthViewModel {
|
||||
let viewModel = GyAuthViewModel()
|
||||
viewModel.statusBarStyle = .default
|
||||
viewModel.userInterfaceStyle = 0
|
||||
viewModel.pullAuthVCStyle = .modal
|
||||
viewModel.pullAuthVCStyle = .push
|
||||
viewModel.viewLifeCycleBlock = { (str, bool) in
|
||||
|
||||
}
|
||||
viewModel.clickAuthButtonBlock = {
|
||||
return true
|
||||
}
|
||||
return viewModel
|
||||
}()
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 登录
|
||||
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() {
|
||||
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() {
|
||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
||||
let req = SendAuthReq()
|
||||
req.scope = "snsapi_userinfo"
|
||||
req.state = "login"
|
||||
WXApi.send(req)
|
||||
// TODO: Integrate WeChat SDK
|
||||
}
|
||||
|
||||
func performAppleLogin() {
|
||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
||||
let provider = ASAuthorizationAppleIDProvider()
|
||||
let request = provider.createRequest()
|
||||
request.requestedScopes = [.fullName, .email]
|
||||
let controller = ASAuthorizationController(authorizationRequests: [request])
|
||||
onAppleLoginRequest?(controller)
|
||||
// TODO: Integrate Sign in with Apple
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,13 @@ class OneTapLoginView: UIView {
|
|||
|
||||
private func setupRx() {
|
||||
backBtn.rx.tap.subscribe(onNext: { _ in
|
||||
#if !targetEnvironment(simulator)
|
||||
GeYanSdk.closeAuthVC(true, completion: nil)
|
||||
#endif
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
checkBox.rx.tap.subscribe(onNext: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.checkBox.isSelected = !self.checkBox.isSelected
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
agreementTV.delegate = self
|
||||
}
|
||||
|
||||
func setupAgreementTextWithCarrier(carrier: String) {
|
||||
|
|
@ -59,16 +55,15 @@ class OneTapLoginView: UIView {
|
|||
private func setupUI() {
|
||||
addSubview(bgMaskImage)
|
||||
addSubview(backBtn)
|
||||
addSubview(inputContainerView)
|
||||
inputContainerView.addSubview(welcomeLabel)
|
||||
inputContainerView.addSubview(phoneNumberLabel)
|
||||
inputContainerView.addSubview(carrierLabel)
|
||||
inputContainerView.addSubview(loginButton)
|
||||
addSubview(welcomeLabel)
|
||||
addSubview(phoneNumberLabel)
|
||||
addSubview(carrierLabel)
|
||||
addSubview(loginButton)
|
||||
|
||||
addSubview(agreementView)
|
||||
agreementView.addSubview(checkBox)
|
||||
agreementView.addSubview(agreementLabel)
|
||||
agreementView.addSubview(agreementTV)
|
||||
agreementView.addSubview(agreementLabel)
|
||||
|
||||
bgMaskImage.layoutChain.edges()
|
||||
|
||||
|
|
@ -78,14 +73,8 @@ class OneTapLoginView: UIView {
|
|||
.width(24)
|
||||
.height(24)
|
||||
|
||||
inputContainerView.layoutChain
|
||||
.centerX()
|
||||
.centerY()
|
||||
.edgesHorzontal()
|
||||
.height(301)
|
||||
|
||||
welcomeLabel.layoutChain
|
||||
.top(0)
|
||||
.top(202 - kNaviHeight)
|
||||
.centerX()
|
||||
.edgesHorzontal()
|
||||
|
||||
|
|
@ -104,26 +93,23 @@ class OneTapLoginView: UIView {
|
|||
.height(50)
|
||||
|
||||
agreementView.layoutChain
|
||||
.edgesHorzontal(30)
|
||||
.bottom(kSafeBottomMargin + 20)
|
||||
.leftToView(loginButton)
|
||||
.rightToView(loginButton)
|
||||
.bottom(55)
|
||||
.height(30)
|
||||
|
||||
agreementLabel.layoutChain.edges()
|
||||
|
||||
checkBox.layoutChain
|
||||
.left()
|
||||
.top(10)
|
||||
.centerY()
|
||||
.width(12)
|
||||
.height(12)
|
||||
|
||||
agreementLabel.layoutChain
|
||||
.leftToRightOfView(checkBox, offset: 0)
|
||||
.right()
|
||||
.top()
|
||||
.bottom()
|
||||
|
||||
agreementTV.layoutChain
|
||||
.leftToRightOfView(checkBox, offset: 0)
|
||||
.right()
|
||||
.top()
|
||||
.bottom()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
|
|
@ -144,12 +130,6 @@ class OneTapLoginView: UIView {
|
|||
}
|
||||
|
||||
// MARK: - UI Components
|
||||
lazy var inputContainerView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var bgMaskImage: UIImageView = {
|
||||
let iv = UIImageView()
|
||||
iv.image = UIImage(named: "Login/bg")
|
||||
|
|
@ -198,12 +178,6 @@ class OneTapLoginView: UIView {
|
|||
return btn
|
||||
}()
|
||||
|
||||
lazy var label: UILabel = {
|
||||
let label = UILabel()
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var agreementView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
|
|
@ -214,16 +188,15 @@ class OneTapLoginView: UIView {
|
|||
let btn = UIButton(type: .custom)
|
||||
btn.setImage(UIImage(named: "Login/checkbox"), for: .normal)
|
||||
btn.setImage(UIImage(named: "Login/selected"), for: .selected)
|
||||
btn.extendEdgeInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 20)
|
||||
btn.isSelected = true
|
||||
btn.extendEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
|
||||
return btn
|
||||
}()
|
||||
|
||||
lazy var agreementLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = .clear
|
||||
label.numberOfLines = 0
|
||||
// label.textAlignment = .center
|
||||
label.numberOfLines = 1
|
||||
label.textAlignment = .center
|
||||
label.font = .systemFont(ofSize: 10)
|
||||
return label
|
||||
}()
|
||||
|
|
@ -234,34 +207,9 @@ class OneTapLoginView: UIView {
|
|||
textView.backgroundColor = .clear
|
||||
textView.isEditable = false
|
||||
textView.isScrollEnabled = false
|
||||
textView.isSelectable = true
|
||||
textView.isSelectable = false
|
||||
textView.linkTextAttributes = [:]
|
||||
|
||||
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 RxCocoa
|
||||
import RxDataSources
|
||||
#if !targetEnvironment(simulator)
|
||||
import GeYanSdk
|
||||
#endif
|
||||
|
||||
class AccountVC: BaseViewController {
|
||||
|
||||
|
|
@ -62,8 +59,6 @@ class AccountVC: BaseViewController {
|
|||
DLToast.showLoading()
|
||||
UserService.logout().subscribe(onNext: { response in
|
||||
DLToast.dismiss()
|
||||
NotificationCenter.default.post(name: .invalidatePopupQueue, object: nil)
|
||||
GeYanSdk.deletePreResultCache()
|
||||
AppContextManager.shared.deleteAccount()
|
||||
GroupIMService.shared.logout()
|
||||
AppDelegate.shared.showMainViewController()
|
||||
|
|
|
|||
|
|
@ -29,11 +29,7 @@ final class MineView: UIView {
|
|||
private let vipBtnTextColor = UIColor(hexStr: "#00343B")
|
||||
|
||||
func setupData(_ model: UserConfigModel) {
|
||||
guard AppContextManager.shared.systemConfig?.globalDisplay == true else {
|
||||
maskBgView.isHidden = false
|
||||
return
|
||||
}
|
||||
maskBgView.isHidden = true
|
||||
|
||||
guard model.vip > 1 else {
|
||||
vipTitleLab.text = "开通会员"
|
||||
vipContentLab.text = "会员尊享10+VIP特权"
|
||||
|
|
@ -125,19 +121,11 @@ final class MineView: UIView {
|
|||
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 = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 18, weight: .bold)
|
||||
label.textColor = .white
|
||||
label.text = " "
|
||||
label.text = "开通会员"
|
||||
return label
|
||||
}()
|
||||
|
||||
|
|
@ -145,7 +133,7 @@ final class MineView: UIView {
|
|||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 12, weight: .medium)
|
||||
label.textColor = .white
|
||||
label.text = " "
|
||||
label.text = "会员尊享10+VIP特权"
|
||||
return label
|
||||
}()
|
||||
|
||||
|
|
@ -200,10 +188,6 @@ final class MineView: UIView {
|
|||
// MARK: - Callbacks
|
||||
var onMenuTap: ((Int) -> Void)?
|
||||
|
||||
// MARK: - Dynamic Height
|
||||
private var settingsCardHeightConstraint: NSLayoutConstraint?
|
||||
private var contentSizeObservation: NSKeyValueObservation?
|
||||
|
||||
// MARK: - Init
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
|
@ -232,7 +216,6 @@ final class MineView: UIView {
|
|||
vipCardView.addSubview(vipActivateButton)
|
||||
vipCardView.addSubview(vipTitleLab)
|
||||
vipCardView.addSubview(vipContentLab)
|
||||
vipCardView.addSubview(maskBgView)
|
||||
|
||||
addSubview(profileBgImageView)
|
||||
addSubview(bottomFillView)
|
||||
|
|
@ -295,8 +278,6 @@ final class MineView: UIView {
|
|||
.right(19).centerY()
|
||||
.width(70).height(28)
|
||||
|
||||
maskBgView.layoutChain.edges()
|
||||
|
||||
// Profile transition background (100pt, overlaps VIP card bottom half)
|
||||
profileBgImageView.layoutChain
|
||||
.topToView(vipCardView, offset: 32)
|
||||
|
|
@ -308,42 +289,14 @@ final class MineView: UIView {
|
|||
.topToBottomOfView(profileBgImageView)
|
||||
.left().right().bottom()
|
||||
|
||||
// Settings Card (dynamic height, scrolls when exceeds area above tabbar)
|
||||
// Settings Card (fixed height, not stretched)
|
||||
settingsCardView.layoutChain
|
||||
.topToBottomOfView(vipCardView, offset: 20)
|
||||
.left(16).right(16)
|
||||
|
||||
settingsCardHeightConstraint = settingsCardView.heightAnchor.constraint(equalToConstant: 312)
|
||||
settingsCardHeightConstraint?.isActive = true
|
||||
.height(312)
|
||||
|
||||
settingsTableView.layoutChain
|
||||
.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 var currentStep = 0
|
||||
private var stepGeneration = 0
|
||||
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) {
|
||||
sourceVC = vc
|
||||
|
|
@ -40,27 +19,7 @@ class PopupQueueManager {
|
|||
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() {
|
||||
guard AppContextManager.shared.systemConfig?.globalDisplay == true else { return }
|
||||
switch currentStep {
|
||||
case 0:
|
||||
tryStartPromotionalActivities()
|
||||
|
|
@ -86,14 +45,12 @@ class PopupQueueManager {
|
|||
skipToNext()
|
||||
return
|
||||
}
|
||||
let gen = stepGeneration
|
||||
startCountdown(seconds: timeConfig.pay_pop_time) { [weak self] in
|
||||
guard let self = self, self.currentStep == 0, self.stepGeneration == gen else { return }
|
||||
let vc = PromotionalActivitiesVC()
|
||||
vc.onDismiss = { [weak self] in
|
||||
self?.skipToNext()
|
||||
}
|
||||
self.push(vc)
|
||||
self?.push(vc)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -109,11 +66,9 @@ class PopupQueueManager {
|
|||
skipToNext()
|
||||
return
|
||||
}
|
||||
let gen = stepGeneration
|
||||
startCountdown(seconds: timeConfig.search_phone) {
|
||||
guard self.currentStep == 1, self.stepGeneration == gen else { return }
|
||||
startCountdown(seconds: timeConfig.search_phone) { [weak self] in
|
||||
PopupWindow.show(popupType: .search) {
|
||||
self.skipToNext()
|
||||
self?.skipToNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -127,17 +82,13 @@ class PopupQueueManager {
|
|||
}
|
||||
|
||||
if AppContextManager.shared.isGuest {
|
||||
let gen = stepGeneration
|
||||
startCountdown(seconds: timeConfig.signup_tip) {
|
||||
guard self.currentStep == 2, self.stepGeneration == gen else { return }
|
||||
startCountdown(seconds: timeConfig.signup_tip) { [weak self] in
|
||||
PopupWindow.show(popupType: .register) {
|
||||
// done
|
||||
}
|
||||
}
|
||||
} else if AppContextManager.shared.vip == 1 {
|
||||
let gen = stepGeneration
|
||||
startCountdown(seconds: timeConfig.buy_vip_tip) {
|
||||
guard self.currentStep == 2, self.stepGeneration == gen else { return }
|
||||
startCountdown(seconds: timeConfig.buy_vip_tip) { [weak self] in
|
||||
PopupWindow.show(popupType: .vip) {
|
||||
// done
|
||||
}
|
||||
|
|
@ -148,7 +99,6 @@ class PopupQueueManager {
|
|||
// MARK: - Helpers
|
||||
|
||||
private func skipToNext() {
|
||||
stepGeneration += 1
|
||||
currentStep += 1
|
||||
executeNextStep()
|
||||
}
|
||||
|
|
@ -172,11 +122,7 @@ class PopupQueueManager {
|
|||
|
||||
private func push(_ vc: UIViewController) {
|
||||
let topVC = topViewController()
|
||||
guard let nav = topVC?.navigationController else { return }
|
||||
if nav.viewControllers.contains(where: { type(of: $0) == type(of: vc) }) {
|
||||
return
|
||||
}
|
||||
nav.pushViewController(vc, animated: true)
|
||||
topVC?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
private func topViewController() -> UIViewController? {
|
||||
|
|
|
|||
|
|
@ -151,12 +151,8 @@ class PopupWindow: UIView {
|
|||
|
||||
registerBtn.rx.tap
|
||||
.subscribe(onNext: { [weak self] _ in
|
||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
||||
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)
|
||||
|
||||
|
|
@ -168,12 +164,8 @@ class PopupWindow: UIView {
|
|||
|
||||
register2Btn.rx.tap
|
||||
.subscribe(onNext: { [weak self] _ in
|
||||
NotificationCenter.default.post(name: .pausePopupQueue, object: nil)
|
||||
self?.slideOutRegister2Popup()
|
||||
let topVC = UIViewController.currentViewController()
|
||||
if !(topVC is LoginViewController) {
|
||||
AppRouter.push(Route.login)
|
||||
}
|
||||
AppRouter.push(Route.login)
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
|
|
@ -186,10 +178,6 @@ class PopupWindow: UIView {
|
|||
vip1UnlockBtn.rx.tap
|
||||
.subscribe(onNext: { [weak self] _ in
|
||||
self?.slideOutUnlockVip1Popup()
|
||||
let topVC = UIViewController.currentViewController()
|
||||
if !(topVC is VipRechargeVC) {
|
||||
AppRouter.push(Route.vipRecharge)
|
||||
}
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
|
|
@ -202,10 +190,6 @@ class PopupWindow: UIView {
|
|||
unlockVip2Btn.rx.tap
|
||||
.subscribe(onNext: { [weak self] _ in
|
||||
self?.slideOutUnlockVip2Popup()
|
||||
let topVC = UIViewController.currentViewController()
|
||||
if !(topVC is VipRechargeVC) {
|
||||
AppRouter.push(Route.vipRecharge)
|
||||
}
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
|
|
@ -218,10 +202,6 @@ class PopupWindow: UIView {
|
|||
unlockVip3Btn.rx.tap
|
||||
.subscribe(onNext: { [weak self] _ in
|
||||
self?.slideOutUnlockVip3Popup()
|
||||
let topVC = UIViewController.currentViewController()
|
||||
if !(topVC is VipRechargeVC) {
|
||||
AppRouter.push(Route.vipRecharge)
|
||||
}
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
|
|
@ -234,10 +214,6 @@ class PopupWindow: UIView {
|
|||
unlockVip4Btn.rx.tap
|
||||
.subscribe(onNext: { [weak self] _ in
|
||||
self?.slideOutUnlockVip4Popup()
|
||||
let topVC = UIViewController.currentViewController()
|
||||
if !(topVC is VipRechargeVC) {
|
||||
AppRouter.push(Route.vipRecharge)
|
||||
}
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class PromotionalActivitiesVC: BaseViewController {
|
|||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
fd_interactivePopDisabled = true
|
||||
|
||||
setupTableView()
|
||||
requestRechargeInfo()
|
||||
|
||||
|
|
@ -103,11 +103,6 @@ class PromotionalActivitiesVC: BaseViewController {
|
|||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
private func requestRechargeInfo() {
|
||||
DLToast.showLoading()
|
||||
|
|
@ -131,29 +126,29 @@ class PromotionalActivitiesVC: BaseViewController {
|
|||
return
|
||||
}
|
||||
|
||||
// if timeConfig.pay_pop_type == 3 {
|
||||
// IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in
|
||||
// guard let self = self else { return }
|
||||
// if success {
|
||||
// self.showPayResultPop(showCloseBtn: false,
|
||||
// status: true,
|
||||
// title: "支付成功",
|
||||
// message: "恭喜您!成功开通!",
|
||||
// confirmText: "确定", confirmBlock: {
|
||||
// AppRouter.shared.popOrDismiss()
|
||||
// }, cancelBlock: { })
|
||||
// } else {
|
||||
// self.showPayResultPop(status: false,
|
||||
// title: "支付失败",
|
||||
// message: msg ?? "很抱歉,请您重新支付!",
|
||||
// confirmText: "重新支付", confirmBlock: { },
|
||||
// cancelText: "取消", cancelBlock: {
|
||||
// AppRouter.shared.popOrDismiss()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
if timeConfig.pay_pop_type == 3 {
|
||||
IAPManager.shared.purchase(goodsId: selectedGoodsId) { [weak self] success, msg in
|
||||
guard let self = self else { return }
|
||||
if success {
|
||||
self.showPayResultPop(showCloseBtn: false,
|
||||
status: true,
|
||||
title: "支付成功",
|
||||
message: "恭喜您!成功开通!",
|
||||
confirmText: "确定", confirmBlock: {
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}, cancelBlock: { })
|
||||
} else {
|
||||
self.showPayResultPop(status: false,
|
||||
title: "支付失败",
|
||||
message: msg ?? "很抱歉,请您重新支付!",
|
||||
confirmText: "重新支付", confirmBlock: { },
|
||||
cancelText: "取消", cancelBlock: {
|
||||
AppRouter.shared.popOrDismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let payType = timeConfig.pay_pop_type == 1 ? "alipay" : "weixin"
|
||||
dl.showLoading()
|
||||
|
|
@ -163,20 +158,16 @@ class PromotionalActivitiesVC: BaseViewController {
|
|||
guard let data = response.data else { return }
|
||||
|
||||
if payType == "weixin" { // 微信
|
||||
guard let partnerId = data["partnerId"] as? String,
|
||||
let prepayId = data["prepayId"] as? String,
|
||||
let package = data["package"] as? String,
|
||||
let nonceStr = data["nonceStr"] as? String,
|
||||
let timeStamp = data["timeStamp"] as? String,
|
||||
let sign = data["sign"] as? String else { return }
|
||||
let request: PayReq = PayReq()
|
||||
request.partnerId = partnerId
|
||||
request.prepayId = prepayId
|
||||
request.package = package
|
||||
request.nonceStr = nonceStr
|
||||
request.timeStamp = UInt32(timeStamp) ?? 0
|
||||
request.sign = sign
|
||||
WXApi.send(request)
|
||||
// WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink)
|
||||
//
|
||||
// let request: PayReq = PayReq()
|
||||
// request.partnerId = model.partnerId
|
||||
// request.prepayId = model.prepayId
|
||||
// request.package = model.packageValue
|
||||
// request.nonceStr = model.nonceStr
|
||||
// request.timeStamp = UInt32(model.timeStamp) ?? 0
|
||||
// request.sign = model.sign
|
||||
// WXApi.send(request)
|
||||
}
|
||||
else if payType == "alipay" { // 支付宝
|
||||
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ class PromotionalRecommendCell: UITableViewCell {
|
|||
|
||||
tipsTitleLab.layoutChain
|
||||
.centerX()
|
||||
.top(7)
|
||||
.top(5)
|
||||
|
||||
priceLab.layoutChain
|
||||
.leftToRightOfView(unitLab, offset: 0)
|
||||
|
|
|
|||
|
|
@ -110,9 +110,6 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
}
|
||||
}, onError: { error in
|
||||
guard let code = error.underlyingError?.code else { return }
|
||||
|
||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
||||
|
||||
if code == 20010 { // "创建的行程数达到上限"
|
||||
CreateScheduleVipPopView.show()
|
||||
}
|
||||
|
|
@ -238,7 +235,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
.observe(on: MainScheduler.asyncInstance)
|
||||
.subscribe(onNext: { [weak self] items in
|
||||
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.refreshMapPoints()
|
||||
})
|
||||
|
|
@ -363,9 +360,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
|
||||
// 缩放至包含所有点
|
||||
if !pointAnnotations.isEmpty {
|
||||
rootView.mapView.showAnnotations(pointAnnotations,
|
||||
edgePadding: UIEdgeInsets(top: kNaviHeight + 20, left: 30, bottom: kScreenHeight / 3 + 20, right: 30),
|
||||
animated: true)
|
||||
rootView.mapView.showAnnotations(pointAnnotations, animated: true)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ class CreateScheduleView: UIView {
|
|||
mv.showsUserLocation = false
|
||||
mv.showsCompass = false
|
||||
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
|
||||
}()
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class ItineraryDetailView: UIView {
|
|||
mv.showsUserLocation = false
|
||||
mv.showsCompass = false
|
||||
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
|
||||
}()
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class LocationPickerView: UIView {
|
|||
bottomView.layoutChain
|
||||
.edgesHorzontal()
|
||||
.bottom()
|
||||
.height(160 + kSafeBottomMargin)
|
||||
.height(160)
|
||||
|
||||
poiNameLab.layoutChain
|
||||
.top(16).left(16).right(16)
|
||||
|
|
@ -81,7 +81,7 @@ class LocationPickerView: UIView {
|
|||
mv.zoomLevel = 18
|
||||
mv.showsUserLocation = 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
|
||||
}()
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -36,9 +36,6 @@ class ScheduleDetailVC: BaseViewController {
|
|||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
guard let systemConfig = AppContextManager.shared.systemConfig, systemConfig.globalDisplay else { return }
|
||||
|
||||
rootView.vipTipsLab.text = AppContextManager.shared.vip > 1 ? "" : "升级 VIP,可查看具体人员与节点"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,16 +55,6 @@ class ScheduleVC: BaseViewController {
|
|||
self.rootView.tableView.refresh(status: status, isEmpty: isEmpty)
|
||||
}).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)
|
||||
.subscribe(viewModel.viewedCellAction.inputs)
|
||||
.disposed(by: disposeBag)
|
||||
|
|
|
|||
|
|
@ -129,12 +129,6 @@ class ScheduleView: UIView {
|
|||
.height(80)
|
||||
.bottom(15)
|
||||
|
||||
// 暂无访客
|
||||
view.addSubview(emptyVisitorsLabel)
|
||||
emptyVisitorsLabel.layoutChain
|
||||
.centerY(collectionView)
|
||||
.centerX()
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
|
|
@ -151,16 +145,6 @@ class ScheduleView: UIView {
|
|||
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 = {
|
||||
let view = UIView()
|
||||
|
|
@ -229,20 +213,9 @@ class ScheduleView: UIView {
|
|||
tableView.showsVerticalScrollIndicator = false
|
||||
tableView.register(ScheduleListPopCell.self)
|
||||
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 97 + kSafeBottomMargin, right: 0)
|
||||
tableView.backgroundView = emptyScheduleLabel
|
||||
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 = {
|
||||
let btn = UIButton()
|
||||
btn.setImage(UIImage(named: "Schedule/create"), for: .normal)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import Kingfisher
|
||||
|
||||
class SharePopView: UIView {
|
||||
|
||||
|
|
@ -15,13 +14,14 @@ class SharePopView: UIView {
|
|||
|
||||
private let contentHeight: CGFloat = 191 + kSafeBottomMargin
|
||||
|
||||
static func show() {
|
||||
static func show(shareURL: String) {
|
||||
guard let superView = kKeyWindow else { return }
|
||||
|
||||
if SharePopView.shared.superview != nil {
|
||||
SharePopView.shared.dismiss(animated: false)
|
||||
}
|
||||
|
||||
SharePopView.shared.shareURL = shareURL
|
||||
SharePopView.shared.bgView.alpha = 0
|
||||
superView.addSubview(SharePopView.shared)
|
||||
superView.bringSubviewToFront(SharePopView.shared)
|
||||
|
|
@ -65,52 +65,19 @@ class SharePopView: UIView {
|
|||
}
|
||||
|
||||
private func shareToWeChat(scene: Int32) {
|
||||
guard let config = AppContextManager.shared.systemConfig,
|
||||
let shareConfig = config.shareConfig else { return }
|
||||
let message = WXMediaMessage()
|
||||
message.title = kAppName
|
||||
let webpage = WXWebpageObject()
|
||||
webpage.webpageUrl = shareURL
|
||||
message.mediaObject = webpage
|
||||
|
||||
DLToast.showLoading()
|
||||
let req = SendMessageToWXReq()
|
||||
req.bText = false
|
||||
req.message = message
|
||||
req.scene = scene
|
||||
WXApi.send(req)
|
||||
|
||||
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()
|
||||
message.title = shareConfig.title
|
||||
message.description = shareConfig.content
|
||||
if let thumb = thumbImage {
|
||||
message.setThumbImage(thumb)
|
||||
}
|
||||
|
||||
let webpage = WXWebpageObject()
|
||||
webpage.webpageUrl = shareConfig.link
|
||||
message.mediaObject = webpage
|
||||
|
||||
let req = SendMessageToWXReq()
|
||||
req.bText = false
|
||||
req.message = message
|
||||
req.scene = scene
|
||||
WXApi.send(req)
|
||||
|
||||
self.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)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
// MARK: - UI
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@ class VipRechargeVC: BaseViewController {
|
|||
requestRechargeInfo()
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
navigationController?.setNavigationBarHidden(true, animated: false)
|
||||
}
|
||||
|
||||
private func bindViewModel() {
|
||||
viewModel.output.sectionedItems
|
||||
.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 payType = types[rootView.selectedPayTypeTag]
|
||||
|
||||
// if payType == "apple" {
|
||||
// IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in
|
||||
// guard let self = self else { return }
|
||||
// if success {
|
||||
// self.showPayResultPop(showCloseBtn: false,
|
||||
// status: true,
|
||||
// title: "支付成功",
|
||||
// message: "恭喜您!成功开通\(self.viewModel.goodsName)",
|
||||
// confirmText: "确定", confirmBlock: {
|
||||
// AppRouter.shared.popOrDismiss()
|
||||
// }, cancelBlock: { })
|
||||
// } else {
|
||||
// self.showPayResultPop(status: false,
|
||||
// title: "支付失败",
|
||||
// message: msg ?? "很抱歉,请您重新支付!",
|
||||
// confirmText: "重新支付", confirmBlock: { },
|
||||
// cancelText: "取消", cancelBlock: {
|
||||
// AppRouter.shared.popOrDismiss()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
if payType == "apple" {
|
||||
IAPManager.shared.purchase(goodsId: viewModel.goodsId) { [weak self] success, msg in
|
||||
guard let self = self else { return }
|
||||
if success {
|
||||
self.showPayResultPop(showCloseBtn: false,
|
||||
status: true,
|
||||
title: "支付成功",
|
||||
message: "恭喜您!成功开通\(self.viewModel.goodsName)",
|
||||
confirmText: "确定", confirmBlock: {
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}, cancelBlock: { })
|
||||
} else {
|
||||
self.showPayResultPop(status: false,
|
||||
title: "支付失败",
|
||||
message: msg ?? "很抱歉,请您重新支付!",
|
||||
confirmText: "重新支付", confirmBlock: { },
|
||||
cancelText: "取消", cancelBlock: {
|
||||
AppRouter.shared.popOrDismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
DLToast.showLoading()
|
||||
OrderService.orderPayParams(goodsId: viewModel.goodsId,
|
||||
|
|
@ -138,20 +133,16 @@ class VipRechargeVC: BaseViewController {
|
|||
|
||||
let payType = types[self.rootView.selectedPayTypeTag]
|
||||
if payType == "weixin" { // 微信
|
||||
guard let partnerId = data["partnerId"] as? String,
|
||||
let prepayId = data["prepayId"] as? String,
|
||||
let package = data["package"] as? String,
|
||||
let nonceStr = data["nonceStr"] as? String,
|
||||
let timeStamp = data["timeStamp"] as? String,
|
||||
let sign = data["sign"] as? String else { return }
|
||||
let request: PayReq = PayReq()
|
||||
request.partnerId = partnerId
|
||||
request.prepayId = prepayId
|
||||
request.package = package
|
||||
request.nonceStr = nonceStr
|
||||
request.timeStamp = UInt32(timeStamp) ?? 0
|
||||
request.sign = sign
|
||||
WXApi.send(request)
|
||||
// WXApi.registerApp(model.appId, universalLink: AppSettings.kAppsUniversalLink)
|
||||
//
|
||||
// let request: PayReq = PayReq()
|
||||
// request.partnerId = model.partnerId
|
||||
// request.prepayId = model.prepayId
|
||||
// request.package = model.packageValue
|
||||
// request.nonceStr = model.nonceStr
|
||||
// request.timeStamp = UInt32(model.timeStamp) ?? 0
|
||||
// request.sign = model.sign
|
||||
// WXApi.send(request)
|
||||
}
|
||||
else if payType == "alipay" { // 支付宝
|
||||
if let payParam = data["payParam"] as? String, let appId = data["payParam"] as? String {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class VipRechargeView: UIView {
|
|||
.heightToWidth(267/375)
|
||||
|
||||
vipRightsDetailView.layoutChain
|
||||
.top(36 + kStatusBarHeight)
|
||||
.top(36)
|
||||
.right()
|
||||
.height(24)
|
||||
|
||||
|
|
@ -562,7 +562,7 @@ class VipRechargeView: UIView {
|
|||
let payTypeMap: [(String, String)] = types.compactMap {
|
||||
if $0 == "weixin" { return ("wechat", "微信支付") }
|
||||
if $0 == "alipay" { return ("alipay", "支付宝支付") }
|
||||
// if $0 == "apple" { return ("apple", "苹果支付") }
|
||||
if $0 == "apple" { return ("apple", "苹果支付") }
|
||||
return nil
|
||||
}
|
||||
guard !payTypeMap.isEmpty else { return }
|
||||
|
|
|
|||
|
|
@ -168,11 +168,7 @@ class FullscreenWebView: WKWebView {
|
|||
super.viewDidLoad()
|
||||
fd_interactivePopDisabled = true
|
||||
if style == "default" {
|
||||
let btn = UIButton(type: .system)
|
||||
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)
|
||||
// setupLeftBarButtonItem(iconColor: "gray")
|
||||
}
|
||||
// setupErrorView()
|
||||
setupView()
|
||||
|
|
@ -316,16 +312,16 @@ class FullscreenWebView: WKWebView {
|
|||
// backItem.tag = 1
|
||||
|
||||
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.tintColor = .black
|
||||
backBtn.tintColor = iconColor=="white" ? .white : .black
|
||||
backBtn.tag = 1
|
||||
let backItem = UIBarButtonItem(customView: backBtn)
|
||||
|
||||
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.tintColor = .black
|
||||
popBtn.tintColor = iconColor=="white" ? .white : .black
|
||||
popBtn.tag = 2
|
||||
|
||||
let popItem = UIBarButtonItem(customView: popBtn)
|
||||
|
|
|
|||