Ver Fonte

fix: 修复 UI 验收问题

陈文艺 há 2 meses atrás
pai
commit
58661e630c
94 ficheiros alterados com 765 adições e 834 exclusões
  1. 34 39
      Lanu.xcodeproj/project.pbxproj
  2. 6 6
      Lanu.xcodeproj/xcshareddata/xcschemes/Lanu_Debug.xcscheme
  3. 5 1
      Lanu/AppDelegate.swift
  4. BIN
      Lanu/Assets.xcassets/IM/ic_im_chat_bubble_me.imageset/ic_im_chat_bubble_me@2x.png
  5. BIN
      Lanu/Assets.xcassets/IM/ic_im_chat_bubble_me.imageset/ic_im_chat_bubble_me@3x.png
  6. BIN
      Lanu/Assets.xcassets/IM/ic_im_chat_bubble_peer.imageset/ic_im_chat_bubble_peer@2x.png
  7. BIN
      Lanu/Assets.xcassets/IM/ic_im_chat_bubble_peer.imageset/ic_im_chat_bubble_peer@3x.png
  8. BIN
      Lanu/Assets.xcassets/Wallet/ic_diamond_bg.imageset/ic_diamond_bg@2x.png
  9. BIN
      Lanu/Assets.xcassets/Wallet/ic_diamond_bg.imageset/ic_diamond_bg@3x.png
  10. 2 2
      Lanu/Assets.xcassets/common/Coin/ic_coin_42.imageset/Contents.json
  11. BIN
      Lanu/Assets.xcassets/common/Coin/ic_coin_42.imageset/ic_coin_42@2x.png
  12. BIN
      Lanu/Assets.xcassets/common/Coin/ic_coin_42.imageset/ic_coin_42@3x.png
  13. 22 0
      Lanu/Assets.xcassets/common/Diamond/ic_diamond_42.imageset/Contents.json
  14. BIN
      Lanu/Assets.xcassets/common/Diamond/ic_diamond_42.imageset/ic_diamond_42@2x.png
  15. BIN
      Lanu/Assets.xcassets/common/Diamond/ic_diamond_42.imageset/ic_diamond_42@3x.png
  16. 0 0
      Lanu/Assets.xcassets/common/Language/Contents.json
  17. 0 0
      Lanu/Assets.xcassets/common/Language/ic_language.imageset/Contents.json
  18. 0 0
      Lanu/Assets.xcassets/common/Language/ic_language.imageset/ic_language@2x.png
  19. 0 0
      Lanu/Assets.xcassets/common/Language/ic_language.imageset/ic_language@3x.png
  20. 22 0
      Lanu/Assets.xcassets/common/Language/ic_language_18.imageset/Contents.json
  21. BIN
      Lanu/Assets.xcassets/common/Language/ic_language_18.imageset/ic_language_18@2x.png
  22. BIN
      Lanu/Assets.xcassets/common/Language/ic_language_18.imageset/ic_language_18@3x.png
  23. BIN
      Lanu/Assets.xcassets/common/Location/ic_location_text_3.imageset/ic_location_text_3@2x.png
  24. BIN
      Lanu/Assets.xcassets/common/Location/ic_location_text_3.imageset/ic_location_text_3@3x.png
  25. 0 0
      Lanu/Assets.xcassets/common/ic_location.imageset/Contents.json
  26. 0 0
      Lanu/Assets.xcassets/common/ic_location.imageset/ic_location@2x.png
  27. 0 0
      Lanu/Assets.xcassets/common/ic_location.imageset/ic_location@3x.png
  28. 19 2
      Lanu/Common/Keyboard/LNKeyboardManager.swift
  29. 4 4
      Lanu/Common/Theme/UIImageView+Theme.swift
  30. 5 0
      Lanu/Common/Views/ImagePreview/LNImagePreviewCell.swift
  31. 7 2
      Lanu/Common/Views/LNPopupView.swift
  32. 2 1
      Lanu/Common/Views/Toast/LNToastView.swift
  33. 0 12
      Lanu/Config_Debug.xcconfig
  34. 0 12
      Lanu/Config_Release.xcconfig
  35. 4 0
      Lanu/GoogleService-Info-Debug.plist
  36. 4 2
      Lanu/GoogleService-Info-Release.plist
  37. 2 6
      Lanu/Info.plist
  38. 62 202
      Lanu/Localizable.xcstrings
  39. 6 1
      Lanu/Manager/Account/LNAccountManager.swift
  40. 0 0
      Lanu/Manager/Account/LNCommonAlertView+Account.swift
  41. 1 0
      Lanu/Manager/GameMate/Network/LNGameMateResponse.swift
  42. 2 1
      Lanu/Manager/Network/LNHttpManager.swift
  43. 1 0
      Lanu/Manager/Profile/Network/LNProfileResponse.swift
  44. 3 0
      Lanu/Manager/Purchase/LNPurchaseManager.swift
  45. 145 88
      Lanu/Views/Game/Category/LNGameCategoryListView.swift
  46. 0 101
      Lanu/Views/Game/Category/LNGameCategoryTabItemView.swift
  47. 86 9
      Lanu/Views/Game/Category/LNGameCategoryTabView.swift
  48. 27 44
      Lanu/Views/Game/MateList/LNGameMateListCell.swift
  49. 2 2
      Lanu/Views/Game/Skill/LNSkillBottomMenuView.swift
  50. 2 1
      Lanu/Views/Game/Skill/LNSkillDetailViewController.swift
  51. 4 4
      Lanu/Views/Game/Skill/LNSkillPhotosView.swift
  52. 6 6
      Lanu/Views/Game/Skill/LNSkillTagView.swift
  53. 20 16
      Lanu/Views/Game/Skill/LNSkillUserInfoView.swift
  54. 21 18
      Lanu/Views/Game/Skill/LNSkillVoiceBarView.swift
  55. 14 6
      Lanu/Views/Home/GameTab/LNHomeActivityTabItemView.swift
  56. 3 2
      Lanu/Views/Home/GameTab/LNHomeActivityTabView.swift
  57. 2 1
      Lanu/Views/Home/GameTab/LNHomeGameTabItemView.swift
  58. 1 1
      Lanu/Views/IM/Chat/Cells/LNIMChatOrderMessageCell.swift
  59. 11 11
      Lanu/Views/IM/Chat/GameMate/LNIMChatGameMateOrderView.swift
  60. 4 2
      Lanu/Views/IM/Chat/GameMate/LNIMChatGameMateSkillCell.swift
  61. 3 6
      Lanu/Views/IM/Chat/GameMate/LNIMChatGameMateSkillView.swift
  62. 2 1
      Lanu/Views/IM/Chat/LNIMChatViewController.swift
  63. 2 3
      Lanu/Views/Login/Setup/LNInterestSetupViewController.swift
  64. 5 0
      Lanu/Views/Main/LNMainTabBar.swift
  65. 6 0
      Lanu/Views/Main/LNMainViewController.swift
  66. 1 0
      Lanu/Views/Order/Create/LNCreateOrderPanel.swift
  67. 4 19
      Lanu/Views/Order/Create/LNCreateOrderViewController.swift
  68. 1 1
      Lanu/Views/Order/Detail/LNOrderDetailViewController.swift
  69. 24 22
      Lanu/Views/Order/OrderList/LNOrderListItemCell.swift
  70. 0 6
      Lanu/Views/Order/OrderList/LNOrderListViewController.swift
  71. 18 6
      Lanu/Views/Order/OrderQR/LNOrderCustomView.swift
  72. 4 6
      Lanu/Views/Order/OrderQR/LNOrderGenerateQRCodePanel.swift
  73. 21 19
      Lanu/Views/Order/OrderQR/LNOrderQRCodeShowView.swift
  74. 4 4
      Lanu/Views/Order/OrderRecords/LNOrderRecordCell.swift
  75. 2 0
      Lanu/Views/Profile/Edit/LNEditGenderPanel.swift
  76. 23 60
      Lanu/Views/Profile/Edit/LNEditProfilePhotoWallView.swift
  77. 3 32
      Lanu/Views/Profile/Edit/LNEditProfileUploadImageView.swift
  78. 5 0
      Lanu/Views/Profile/Edit/LNEditProfileViewController.swift
  79. 9 3
      Lanu/Views/Profile/Mine/LNMineFunctionView.swift
  80. 1 1
      Lanu/Views/Profile/Post/LNPostShareImageGenerator.swift
  81. 1 0
      Lanu/Views/Profile/Post/LNPostShareViewController.swift
  82. 4 4
      Lanu/Views/Profile/Profile/LNProfileScoreFloatingView.swift
  83. 3 7
      Lanu/Views/Profile/Profile/LNProfileSkillListView.swift
  84. 1 0
      Lanu/Views/Profile/Profile/LNProfileUserDetailView.swift
  85. 21 2
      Lanu/Views/Profile/Profile/LNProfileUserInfoView.swift
  86. 1 0
      Lanu/Views/Search/LNUserSearchViewController.swift
  87. 4 4
      Lanu/Views/Settings/LNAboutViewController.swift
  88. 4 3
      Lanu/Views/Settings/LNSettingsViewController.swift
  89. 8 5
      Lanu/Views/Wallet/Coin/LNCoinViewController.swift
  90. 8 5
      Lanu/Views/Wallet/Diamond/LNDiamondViewController.swift
  91. 5 4
      Lanu/Views/Wallet/LNExchangePanel.swift
  92. 4 4
      Lanu/Views/Wallet/LNWalletViewController.swift
  93. 1 1
      Podfile
  94. 1 1
      Podfile.lock

+ 34 - 39
Lanu.xcodeproj/project.pbxproj

@@ -7,7 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		0DE4E86CB596C7E4A75DCD81 /* Pods_Lanu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABE0E79254D94F91C773E929 /* Pods_Lanu.framework */; };
+		314B1B286681A79A6D153299 /* Pods_Gami.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4E0C09312B6CA8A283AD62F /* Pods_Gami.framework */; };
 		FB696C172EC96C0F00FAD639 /* MJRefresh in Frameworks */ = {isa = PBXBuildFile; productRef = FB696C162EC96C0F00FAD639 /* MJRefresh */; };
 		FB9CD1192EC1EEA10033B14B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = FB9CD1182EC1EEA10033B14B /* FirebaseCore */; };
 		FB9CD11B2EC1EEA10033B14B /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = FB9CD11A2EC1EEA10033B14B /* FirebaseCrashlytics */; };
@@ -23,13 +23,15 @@
 /* Begin PBXFileReference section */
 		006A9E8625309678F4BEF8AB /* Pods-Lanu.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lanu.release.xcconfig"; path = "Target Support Files/Pods-Lanu/Pods-Lanu.release.xcconfig"; sourceTree = "<group>"; };
 		036D0D2B175E726D1FD387A9 /* Pods-Lanu.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Lanu.debug.xcconfig"; path = "Target Support Files/Pods-Lanu/Pods-Lanu.debug.xcconfig"; sourceTree = "<group>"; };
-		ABE0E79254D94F91C773E929 /* Pods_Lanu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Lanu.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		B28FA2B72A322804F16B3840 /* Pods-Gami.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Gami.debug.xcconfig"; path = "Target Support Files/Pods-Gami/Pods-Gami.debug.xcconfig"; sourceTree = "<group>"; };
+		B972432FB2D1B371EED40D9C /* Pods-Gami.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Gami.release.xcconfig"; path = "Target Support Files/Pods-Gami/Pods-Gami.release.xcconfig"; sourceTree = "<group>"; };
+		C4E0C09312B6CA8A283AD62F /* Pods_Gami.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Gami.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		FB9EAE7A2F011ACD00E77B7C /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
-		FBFE13C02EBC39B000DCE6E9 /* Lanu.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Lanu.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		FBFE13C02EBC39B000DCE6E9 /* Gami.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gami.app; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
-		FB1A37CF2EBE04EC0063ED8C /* Exceptions for "Lanu" folder in "Lanu" target */ = {
+		FB1A37CF2EBE04EC0063ED8C /* Exceptions for "Lanu" folder in "Gami" target */ = {
 			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
 			membershipExceptions = (
 				"/Localized: InfoPlist.strings",
@@ -94,15 +96,13 @@
 				Common/Voice/LNVoiceRecorder.swift,
 				Common/Voice/LNVoiceResourceManager.swift,
 				Common/Wrapper/LNVisitedTimeWrapper.swift,
-				Config_Debug.xcconfig,
-				Config_Release.xcconfig,
 				"Files/Font/Poppins-SemiBold.ttf",
 				Files/Svga/ic_login_gender_male_anim.svga,
 				"GoogleService-Info-Debug.plist",
 				"GoogleService-Info-Release.plist",
 				Localizable.xcstrings,
 				Manager/Account/LNAccountManager.swift,
-				"Manager/Account/LNCommonAlertView+Settings.swift",
+				"Manager/Account/LNCommonAlertView+Account.swift",
 				"Manager/Account/Network/LNHttpManager+Login.swift",
 				Manager/Account/Network/LNLoginResponse.swift,
 				Manager/Config/LNConfigManager.swift,
@@ -156,7 +156,6 @@
 				SceneDelegate.swift,
 				Views/Game/Category/LNGameCategoryListView.swift,
 				Views/Game/Category/LNGameCategoryListViewController.swift,
-				Views/Game/Category/LNGameCategoryTabItemView.swift,
 				Views/Game/Category/LNGameCategoryTabView.swift,
 				Views/Game/MateFilter/LNGameCategoryFilterPanel.swift,
 				Views/Game/MateFilter/LNGameFilterPanel.swift,
@@ -284,7 +283,7 @@
 				Views/Wallet/LNWalletViewController.swift,
 				Views/Web/LNWebViewController.swift,
 			);
-			target = FBFE13BF2EBC39B000DCE6E9 /* Lanu */;
+			target = FBFE13BF2EBC39B000DCE6E9 /* Gami */;
 		};
 /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
 
@@ -292,13 +291,15 @@
 		FB1A37952EBE04E40063ED8C /* Lanu */ = {
 			isa = PBXFileSystemSynchronizedRootGroup;
 			exceptions = (
-				FB1A37CF2EBE04EC0063ED8C /* Exceptions for "Lanu" folder in "Lanu" target */,
+				FB1A37CF2EBE04EC0063ED8C /* Exceptions for "Lanu" folder in "Gami" target */,
 			);
 			path = Lanu;
 			sourceTree = "<group>";
 		};
 		FBB67E232EC48B440070E686 /* ThirdParty */ = {
 			isa = PBXFileSystemSynchronizedRootGroup;
+			exceptions = (
+			);
 			path = ThirdParty;
 			sourceTree = "<group>";
 		};
@@ -309,7 +310,6 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				0DE4E86CB596C7E4A75DCD81 /* Pods_Lanu.framework in Frameworks */,
 				FBECAA1D2EC1C8860013A5E6 /* CocoaLumberjackSwiftLogBackend in Frameworks */,
 				FBECA9CA2EC1C8240013A5E6 /* CocoaLumberjackSwift in Frameworks */,
 				FB9CD1192EC1EEA10033B14B /* FirebaseCore in Frameworks */,
@@ -320,6 +320,7 @@
 				FBECA9BE2EC1C50F0013A5E6 /* SnapKit in Frameworks */,
 				FB9CD11E2EC1EEF30033B14B /* GoogleSignIn in Frameworks */,
 				FB9EAE7B2F011ACD00E77B7C /* StoreKit.framework in Frameworks */,
+				314B1B286681A79A6D153299 /* Pods_Gami.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -331,6 +332,8 @@
 			children = (
 				036D0D2B175E726D1FD387A9 /* Pods-Lanu.debug.xcconfig */,
 				006A9E8625309678F4BEF8AB /* Pods-Lanu.release.xcconfig */,
+				B28FA2B72A322804F16B3840 /* Pods-Gami.debug.xcconfig */,
+				B972432FB2D1B371EED40D9C /* Pods-Gami.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -339,7 +342,7 @@
 			isa = PBXGroup;
 			children = (
 				FB9EAE7A2F011ACD00E77B7C /* StoreKit.framework */,
-				ABE0E79254D94F91C773E929 /* Pods_Lanu.framework */,
+				C4E0C09312B6CA8A283AD62F /* Pods_Gami.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -358,7 +361,7 @@
 		FBFE13C12EBC39B000DCE6E9 /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				FBFE13C02EBC39B000DCE6E9 /* Lanu.app */,
+				FBFE13C02EBC39B000DCE6E9 /* Gami.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -366,9 +369,9 @@
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		FBFE13BF2EBC39B000DCE6E9 /* Lanu */ = {
+		FBFE13BF2EBC39B000DCE6E9 /* Gami */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = FBFE13D32EBC39B100DCE6E9 /* Build configuration list for PBXNativeTarget "Lanu" */;
+			buildConfigurationList = FBFE13D32EBC39B100DCE6E9 /* Build configuration list for PBXNativeTarget "Gami" */;
 			buildPhases = (
 				AC205607300F610A882B2531 /* [CP] Check Pods Manifest.lock */,
 				FBFE13BC2EBC39B000DCE6E9 /* Sources */,
@@ -381,9 +384,9 @@
 			);
 			dependencies = (
 			);
-			name = Lanu;
+			name = Gami;
 			productName = Lanu;
-			productReference = FBFE13C02EBC39B000DCE6E9 /* Lanu.app */;
+			productReference = FBFE13C02EBC39B000DCE6E9 /* Gami.app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */
@@ -427,7 +430,7 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				FBFE13BF2EBC39B000DCE6E9 /* Lanu */,
+				FBFE13BF2EBC39B000DCE6E9 /* Gami */,
 			);
 		};
 /* End PBXProject section */
@@ -449,19 +452,15 @@
 			files = (
 			);
 			inputFileListPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-Lanu/Pods-Lanu-resources-${CONFIGURATION}-input-files.xcfilelist",
-			);
-			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-resources-${CONFIGURATION}-input-files.xcfilelist",
 			);
 			name = "[CP] Copy Pods Resources";
 			outputFileListPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-Lanu/Pods-Lanu-resources-${CONFIGURATION}-output-files.xcfilelist",
-			);
-			outputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-resources-${CONFIGURATION}-output-files.xcfilelist",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Lanu/Pods-Lanu-resources.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 		AC205607300F610A882B2531 /* [CP] Check Pods Manifest.lock */ = {
@@ -479,7 +478,7 @@
 			outputFileListPaths = (
 			);
 			outputPaths = (
-				"$(DERIVED_FILE_DIR)/Pods-Lanu-checkManifestLockResult.txt",
+				"$(DERIVED_FILE_DIR)/Pods-Gami-checkManifestLockResult.txt",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
@@ -492,19 +491,15 @@
 			files = (
 			);
 			inputFileListPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-Lanu/Pods-Lanu-frameworks-${CONFIGURATION}-input-files.xcfilelist",
-			);
-			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputFileListPaths = (
-				"${PODS_ROOT}/Target Support Files/Pods-Lanu/Pods-Lanu-frameworks-${CONFIGURATION}-output-files.xcfilelist",
-			);
-			outputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Lanu/Pods-Lanu-frameworks.sh\"\n";
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Gami/Pods-Gami-frameworks.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
 /* End PBXShellScriptBuildPhase section */
@@ -522,14 +517,14 @@
 /* Begin XCBuildConfiguration section */
 		FBFE13D42EBC39B100DCE6E9 /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 036D0D2B175E726D1FD387A9 /* Pods-Lanu.debug.xcconfig */;
+			baseConfigurationReference = B28FA2B72A322804F16B3840 /* Pods-Gami.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = Lanu/Lanu.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 16;
+				CURRENT_PROJECT_VERSION = 18;
 				DEVELOPMENT_TEAM = 5H8D98R72W;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -566,19 +561,19 @@
 		};
 		FBFE13D52EBC39B100DCE6E9 /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 006A9E8625309678F4BEF8AB /* Pods-Lanu.release.xcconfig */;
+			baseConfigurationReference = B972432FB2D1B371EED40D9C /* Pods-Gami.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = Lanu/Lanu.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 16;
+				CURRENT_PROJECT_VERSION = 18;
 				DEVELOPMENT_TEAM = 5H8D98R72W;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = Lanu/Info.plist;
-				INFOPLIST_KEY_CFBundleDisplayName = "Gami(Debug)";
+				INFOPLIST_KEY_CFBundleDisplayName = Gami;
 				INFOPLIST_KEY_NSAccessoryTrackingUsageDescription = "";
 				INFOPLIST_KEY_NSCameraUsageDescription = "need permission";
 				INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = 12;
@@ -748,7 +743,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		FBFE13D32EBC39B100DCE6E9 /* Build configuration list for PBXNativeTarget "Lanu" */ = {
+		FBFE13D32EBC39B100DCE6E9 /* Build configuration list for PBXNativeTarget "Gami" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				FBFE13D42EBC39B100DCE6E9 /* Debug */,

+ 6 - 6
Lanu.xcodeproj/xcshareddata/xcschemes/Lanu_Debug.xcscheme

@@ -16,8 +16,8 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "FBFE13BF2EBC39B000DCE6E9"
-               BuildableName = "Lanu.app"
-               BlueprintName = "Lanu"
+               BuildableName = "Gami.app"
+               BlueprintName = "Gami"
                ReferencedContainer = "container:Lanu.xcodeproj">
             </BuildableReference>
          </BuildActionEntry>
@@ -45,8 +45,8 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "FBFE13BF2EBC39B000DCE6E9"
-            BuildableName = "Lanu.app"
-            BlueprintName = "Lanu"
+            BuildableName = "Gami.app"
+            BlueprintName = "Gami"
             ReferencedContainer = "container:Lanu.xcodeproj">
          </BuildableReference>
       </BuildableProductRunnable>
@@ -62,8 +62,8 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "FBFE13BF2EBC39B000DCE6E9"
-            BuildableName = "Lanu.app"
-            BlueprintName = "Lanu"
+            BuildableName = "Gami.app"
+            BlueprintName = "Gami"
             ReferencedContainer = "container:Lanu.xcodeproj">
          </BuildableReference>
       </BuildableProductRunnable>

+ 5 - 1
Lanu/AppDelegate.swift

@@ -55,7 +55,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
 extension AppDelegate {
     private func setupFirebase() {
-        let plistName = Bundle.main.object(forInfoDictionaryKey: "FireBaseConfigPath") as! String
+#if DEBUG
+        let plistName = "GoogleService-Info-Debug"
+#else
+        let plistName = "GoogleService-Info-Release"
+#endif
         let plistPath = Bundle.main.path(forResource: plistName, ofType: "plist")!
         let options = FirebaseOptions(contentsOfFile: plistPath)
         FirebaseApp.configure(options: options!)

BIN
Lanu/Assets.xcassets/IM/ic_im_chat_bubble_me.imageset/ic_im_chat_bubble_me@2x.png


BIN
Lanu/Assets.xcassets/IM/ic_im_chat_bubble_me.imageset/ic_im_chat_bubble_me@3x.png


BIN
Lanu/Assets.xcassets/IM/ic_im_chat_bubble_peer.imageset/ic_im_chat_bubble_peer@2x.png


BIN
Lanu/Assets.xcassets/IM/ic_im_chat_bubble_peer.imageset/ic_im_chat_bubble_peer@3x.png


BIN
Lanu/Assets.xcassets/Wallet/ic_diamond_bg.imageset/ic_diamond_bg@2x.png


BIN
Lanu/Assets.xcassets/Wallet/ic_diamond_bg.imageset/ic_diamond_bg@3x.png


+ 2 - 2
Lanu/Assets.xcassets/common/Location/ic_location_text_3.imageset/Contents.json → Lanu/Assets.xcassets/common/Coin/ic_coin_42.imageset/Contents.json

@@ -5,12 +5,12 @@
       "scale" : "1x"
     },
     {
-      "filename" : "ic_location_text_3@2x.png",
+      "filename" : "ic_coin_42@2x.png",
       "idiom" : "universal",
       "scale" : "2x"
     },
     {
-      "filename" : "ic_location_text_3@3x.png",
+      "filename" : "ic_coin_42@3x.png",
       "idiom" : "universal",
       "scale" : "3x"
     }

BIN
Lanu/Assets.xcassets/common/Coin/ic_coin_42.imageset/ic_coin_42@2x.png


BIN
Lanu/Assets.xcassets/common/Coin/ic_coin_42.imageset/ic_coin_42@3x.png


+ 22 - 0
Lanu/Assets.xcassets/common/Diamond/ic_diamond_42.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ic_diamond_42@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ic_diamond_42@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Lanu/Assets.xcassets/common/Diamond/ic_diamond_42.imageset/ic_diamond_42@2x.png


BIN
Lanu/Assets.xcassets/common/Diamond/ic_diamond_42.imageset/ic_diamond_42@3x.png


+ 0 - 0
Lanu/Assets.xcassets/common/Location/Contents.json → Lanu/Assets.xcassets/common/Language/Contents.json


+ 0 - 0
Lanu/Assets.xcassets/common/ic_language.imageset/Contents.json → Lanu/Assets.xcassets/common/Language/ic_language.imageset/Contents.json


+ 0 - 0
Lanu/Assets.xcassets/common/ic_language.imageset/ic_language@2x.png → Lanu/Assets.xcassets/common/Language/ic_language.imageset/ic_language@2x.png


+ 0 - 0
Lanu/Assets.xcassets/common/ic_language.imageset/ic_language@3x.png → Lanu/Assets.xcassets/common/Language/ic_language.imageset/ic_language@3x.png


+ 22 - 0
Lanu/Assets.xcassets/common/Language/ic_language_18.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "ic_language_18@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "ic_language_18@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
Lanu/Assets.xcassets/common/Language/ic_language_18.imageset/ic_language_18@2x.png


BIN
Lanu/Assets.xcassets/common/Language/ic_language_18.imageset/ic_language_18@3x.png


BIN
Lanu/Assets.xcassets/common/Location/ic_location_text_3.imageset/ic_location_text_3@2x.png


BIN
Lanu/Assets.xcassets/common/Location/ic_location_text_3.imageset/ic_location_text_3@3x.png


+ 0 - 0
Lanu/Assets.xcassets/common/Location/ic_location.imageset/Contents.json → Lanu/Assets.xcassets/common/ic_location.imageset/Contents.json


+ 0 - 0
Lanu/Assets.xcassets/common/Location/ic_location.imageset/ic_location@2x.png → Lanu/Assets.xcassets/common/ic_location.imageset/ic_location@2x.png


+ 0 - 0
Lanu/Assets.xcassets/common/Location/ic_location.imageset/ic_location@3x.png → Lanu/Assets.xcassets/common/ic_location.imageset/ic_location@3x.png


+ 19 - 2
Lanu/Common/Keyboard/LNKeyboardManager.swift

@@ -120,7 +120,7 @@ class LNKeyboardManager {
             object: nil, queue: .main)
         { [weak self] notify in
             guard let self else { return }
-            curInput = notify.object as? UIView
+            curInput = notify.object as? UITextField
         }
         
         // UITextView
@@ -129,7 +129,24 @@ class LNKeyboardManager {
             object: nil, queue: .main)
         { [weak self] notify in
             guard let self else { return }
-            curInput = notify.object as? UIView
+            curInput = notify.object as? UITextView
+        }
+    }
+}
+
+
+private struct LNWeakProxy {
+    weak var obj: UIView?
+}
+private var LNInputViewVisiableViewKey: UInt8 = 0
+extension UITextInput {
+    weak var visiableView: UIView? {
+        get {
+            let proxy = objc_getAssociatedObject(self, &LNInputViewVisiableViewKey) as? LNWeakProxy
+            return proxy?.obj
+        }
+        set {
+            objc_setAssociatedObject(self, &LNInputViewVisiableViewKey, LNWeakProxy(obj: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
         }
     }
 }

+ 4 - 4
Lanu/Common/Theme/UIImageView+Theme.swift

@@ -16,16 +16,16 @@ extension UIImageView {
         return arrow
     }
     
-    static func coinImageView() -> UIImageView {
+    static func coinImageView(_ big: Bool = false) -> UIImageView {
         let coin = UIImageView()
-        coin.image = .init(named: "ic_coin")
+        coin.image = .init(named: big ? "ic_coin_42" : "ic_coin")
         
         return coin
     }
     
-    static func diamondImageView() -> UIImageView {
+    static func diamondImageView(_ big: Bool = false) -> UIImageView {
         let diamond = UIImageView()
-        diamond.image = .init(named: "ic_diamond")
+        diamond.image = .init(named: big ? "ic_diamond_42" : "ic_diamond")
         
         return diamond
     }

+ 5 - 0
Lanu/Common/Views/ImagePreview/LNImagePreviewCell.swift

@@ -243,5 +243,10 @@ extension LNImagePreviewCell {
         pan.maximumNumberOfTouches = 1
         contentView.addGestureRecognizer(pan)
         panGesture = pan
+        
+        contentView.onTap { [weak self] in
+            guard let self else { return }
+            delegate?.onImagePreviewCellDragToDismiss(cell: self)
+        }
     }
 }

+ 7 - 2
Lanu/Common/Views/LNPopupView.swift

@@ -146,8 +146,13 @@ extension LNPopupView {
 
 extension LNPopupView: LNKeyboardNotify {
     func onKeybaordWillShow(curInput: UIView?, keyboardHeight: CGFloat) {
-        guard curInput?.isDescendant(of: self) == true else { return }
-        onKeyboardShowup(keyboardHeight)
+        guard let curInput, curInput.isDescendant(of: self) == true else { return }
+        let view = (curInput as? UITextInput)?.visiableView ?? curInput
+        let frame = view.convert(view.bounds, to: self)
+        let offset = bounds.height - frame.maxY - keyboardHeight - 16
+        if offset < 0 {
+            onKeyboardShowup(-offset)
+        }
     }
     
     func onKeybaordShow(curInput: UIView?, keyboardHeight: CGFloat) {

+ 2 - 1
Lanu/Common/Views/Toast/LNToastView.swift

@@ -39,7 +39,8 @@ class LNToastView: UIView {
         
         allToast.add(self)
         
-        UIView.appKeyWindow?.addSubview(self)
+        guard let window = UIView.appKeyWindow else { return }
+        window.addSubview(self)
         self.snp.makeConstraints { make in
             make.centerX.equalToSuperview()
             make.bottom.equalToSuperview().offset(-331)

+ 0 - 12
Lanu/Config_Debug.xcconfig

@@ -1,12 +0,0 @@
-//
-//  Config_Debug.xcconfig
-//  Lanu
-//
-//  Created by OneeChan on 2025/11/11.
-//
-
-// Configuration settings file format documentation can be found at:
-// https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project
-
-GOOGLE_CLIENT_ID = 981655295954-bvbt69s45q2c85n6auc06t02lpc9g5d7.apps.googleusercontent.com
-FIREBASE_PLIST = GoogleService-Info-Debug

+ 0 - 12
Lanu/Config_Release.xcconfig

@@ -1,12 +0,0 @@
-//
-//  Config_Release.xcconfig
-//  Lanu
-//
-//  Created by OneeChan on 2025/11/11.
-//
-
-// Configuration settings file format documentation can be found at:
-// https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project
-
-GOOGLE_CLIENT_ID = 955524882346-8ma09s04jkalgbdk8s41p25pd37hoijm.apps.googleusercontent.com
-FIREBASE_PLIST = GoogleService-Info-Release

+ 4 - 0
Lanu/GoogleService-Info-Debug.plist

@@ -2,6 +2,10 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>CLIENT_ID</key>
+	<string>981655295954-noc65ii1gfgpq3mrc0r75t7gq66v57bj.apps.googleusercontent.com</string>
+	<key>REVERSED_CLIENT_ID</key>
+	<string>com.googleusercontent.apps.981655295954-noc65ii1gfgpq3mrc0r75t7gq66v57bj</string>
 	<key>ANDROID_CLIENT_ID</key>
 	<string>981655295954-1a4a3e5c6fjmf88t59n98fmpgfv7qh5o.apps.googleusercontent.com</string>
 	<key>API_KEY</key>

+ 4 - 2
Lanu/GoogleService-Info-Release.plist

@@ -3,9 +3,11 @@
 <plist version="1.0">
 <dict>
 	<key>CLIENT_ID</key>
-	<string>955524882346-8ma09s04jkalgbdk8s41p25pd37hoijm.apps.googleusercontent.com</string>
+	<string>955524882346-a7fs1l3798khu5hn058m0veqqcvli7h4.apps.googleusercontent.com</string>
 	<key>REVERSED_CLIENT_ID</key>
-	<string>com.googleusercontent.apps.955524882346-8ma09s04jkalgbdk8s41p25pd37hoijm</string>
+	<string>com.googleusercontent.apps.955524882346-a7fs1l3798khu5hn058m0veqqcvli7h4</string>
+	<key>ANDROID_CLIENT_ID</key>
+	<string>955524882346-6g7cieuusopegikppinm91buveg2vsgj.apps.googleusercontent.com</string>
 	<key>API_KEY</key>
 	<string>AIzaSyCElIjWvr7dOBDAQHh6UxU2ufGZ5RcNz2U</string>
 	<key>GCM_SENDER_ID</key>

+ 2 - 6
Lanu/Info.plist

@@ -9,7 +9,7 @@
 			<string>Editor</string>
 			<key>CFBundleURLSchemes</key>
 			<array>
-				<string>com.googleusercontent.apps.981655295954-bvbt69s45q2c85n6auc06t02lpc9g5d7</string>
+				<string>com.googleusercontent.apps.955524882346-a7fs1l3798khu5hn058m0veqqcvli7h4</string>
 			</array>
 		</dict>
 		<dict>
@@ -17,7 +17,7 @@
 			<string>Editor</string>
 			<key>CFBundleURLSchemes</key>
 			<array>
-				<string>com.googleusercontent.apps.955524882346-8ma09s04jkalgbdk8s41p25pd37hoijm</string>
+				<string>com.googleusercontent.apps.981655295954-noc65ii1gfgpq3mrc0r75t7gq66v57bj</string>
 			</array>
 		</dict>
 		<dict>
@@ -31,10 +31,6 @@
 			</array>
 		</dict>
 	</array>
-	<key>FireBaseConfigPath</key>
-	<string>$(FIREBASE_PLIST)</string>
-	<key>GoogleClientID</key>
-	<string>$(GOOGLE_CLIENT_ID)</string>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>

+ 62 - 202
Lanu/Localizable.xcstrings

@@ -997,27 +997,7 @@
       }
     },
     "A00046" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Order %d"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Pesanan %d"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "订单%d"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00047" : {
       "extractionState" : "manual",
@@ -1054,7 +1034,7 @@
         "id" : {
           "stringUnit" : {
             "state" : "translated",
-            "value" : "Lebih Banyak"
+            "value" : "Lainnya"
           }
         },
         "zh-Hans" : {
@@ -2399,6 +2379,52 @@
         }
       }
     },
+    "A00107" : {
+      "extractionState" : "manual",
+      "localizations" : {
+        "en" : {
+          "stringUnit" : {
+            "state" : "needs_review",
+            "value" : "Select your gender"
+          }
+        },
+        "id" : {
+          "stringUnit" : {
+            "state" : "needs_review",
+            "value" : "Pilih jenis kelamin Anda"
+          }
+        },
+        "zh-Hans" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "选择你的性别"
+          }
+        }
+      }
+    },
+    "A00108" : {
+      "extractionState" : "manual",
+      "localizations" : {
+        "en" : {
+          "stringUnit" : {
+            "state" : "needs_review",
+            "value" : "Cannot be modified after successful registration"
+          }
+        },
+        "id" : {
+          "stringUnit" : {
+            "state" : "needs_review",
+            "value" : "Tidak dapat diubah setelah pendaftaran berhasil"
+          }
+        },
+        "zh-Hans" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "注册成功后不可修改"
+          }
+        }
+      }
+    },
     "A00109" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -4185,7 +4211,7 @@
         "id" : {
           "stringUnit" : {
             "state" : "translated",
-            "value" : "Setelah memilih, kami akan membantu Anda menemukan lebih banyak teman yang berpikiran sama"
+            "value" : "Pilih dulu, dapat teman sependapat"
           }
         },
         "zh-Hans" : {
@@ -4289,188 +4315,28 @@
       }
     },
     "A00192" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "My Interesting Selfies"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Foto Diri Saya yang Menarik"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我的有趣自拍"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00193" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "My Travel Journal"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Jurnal Perjalanan Saya"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我的旅行日志"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00194" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "My Daily Life"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Kehidupan Sehari-hari Saya"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我的日常生活"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00195" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Unique Talents"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Bakat Unik"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "独一无二的才艺"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00196" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "My Best Angles"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Sudut Terbaik Saya"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我的最美角度"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00197" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Sports I Love"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Olahraga yang Saya Cintai"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我热爱的运动"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00198" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Things I Love to Do Most"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Hal yang Paling Saya Suka Lakukan"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我最喜欢做的事"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00199" : {
-      "extractionState" : "manual",
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "My Favorite Outfits"
-          }
-        },
-        "id" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Gaun Favorit Saya"
-          }
-        },
-        "zh-Hans" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "我喜欢的穿搭"
-          }
-        }
-      }
+      "extractionState" : "manual"
     },
     "A00200" : {
       "extractionState" : "manual",
@@ -4489,7 +4355,7 @@
         },
         "zh-Hans" : {
           "stringUnit" : {
-            "state" : "needs_review",
+            "state" : "translated",
             "value" : "有什么想说给朋友听"
           }
         }
@@ -4912,21 +4778,15 @@
     "A00219" : {
       "extractionState" : "manual",
       "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "translated",
-            "value" : "Scan the code to play with me"
-          }
-        },
         "id" : {
           "stringUnit" : {
-            "state" : "translated",
+            "state" : "needs_review",
             "value" : "Scan & main"
           }
         },
         "zh-Hans" : {
           "stringUnit" : {
-            "state" : "translated",
+            "state" : "needs_review",
             "value" : "扫码跟我一起玩"
           }
         }
@@ -5105,7 +4965,7 @@
         "id" : {
           "stringUnit" : {
             "state" : "translated",
-            "value" : "Berikan Kesan Baik"
+            "value" : "Kesan Baik"
           }
         },
         "zh-Hans" : {
@@ -6099,7 +5959,7 @@
         },
         "zh-Hans" : {
           "stringUnit" : {
-            "state" : "needs_review",
+            "state" : "translated",
             "value" : "1. 充值成功后,账号到账可能会有延迟,请耐心等待;\n2. 若充值遇到问题,请点击帮助中心"
           }
         }

+ 6 - 1
Lanu/Manager/Account/LNAccountManager.swift

@@ -47,7 +47,12 @@ class LNAccountManager {
     }
     
     private init() {
-        let clientID = Bundle.main.object(forInfoDictionaryKey: "GoogleClientID") as! String
+        let clientID = if LNAppConfig.shared.curEnv == .test {
+            "981655295954-noc65ii1gfgpq3mrc0r75t7gq66v57bj.apps.googleusercontent.com"
+        } else {
+            "955524882346-a7fs1l3798khu5hn058m0veqqcvli7h4.apps.googleusercontent.com"
+        }
+        
         GIDSignIn.sharedInstance.configuration = GIDConfiguration(clientID: clientID)
     }
     

+ 0 - 0
Lanu/Manager/Account/LNCommonAlertView+Settings.swift → Lanu/Manager/Account/LNCommonAlertView+Account.swift


+ 1 - 0
Lanu/Manager/GameMate/Network/LNGameMateResponse.swift

@@ -124,6 +124,7 @@ class LNGameMateSkillDetailVO: Decodable {
     var orderCount: Int = 0
     var online: Bool = false
     var follow: Bool = false
+    var distance: Double = 0
 }
 
 @AutoCodable

+ 2 - 1
Lanu/Manager/Network/LNHttpManager.swift

@@ -31,7 +31,7 @@ enum LNHttpError: Error, LocalizedError {
         case .networkError(let error): error.localizedDescription
         case .parsingError(let error): error.localizedDescription
         case .statusCode(let code): "请求失败,状态码: \(code)"
-        case .serverError(let code, let error): error
+        case .serverError(_, let error): error
         }
     }
 }
@@ -134,6 +134,7 @@ class LNHttpManager {
         mergedHeader.forEach { key, value in
             request.addValue(value, forHTTPHeaderField: key)
         }
+        request.addValue(LNAppConfig.shared.curLang.bundleName, forHTTPHeaderField: "Accept-Language")
         
         // 设置请求体(POST, PUT等方法)
         if method != .get, let parameters = parameters {

+ 1 - 0
Lanu/Manager/Profile/Network/LNProfileResponse.swift

@@ -54,6 +54,7 @@ class LNUserProfileVO: Decodable, Copyable {
     var birthday: Int = 0
     var rated: Bool = false
     var interests: [LNUserInterestVO] = []
+    var distance: Double = 0
     
     var isAvailable: Bool {
         gender != .unknow

+ 3 - 0
Lanu/Manager/Purchase/LNPurchaseManager.swift

@@ -198,6 +198,9 @@ extension LNPurchaseManager: LNAccountManagerNotify {
         loadGoodsList(currencyType: .coin) { _ in }
         loadGoodsList(currencyType: .diamond) { _ in }
         
+        // 拉取转换配置
+        getExchangeConfig()
+
 //        restoreCompletedTransactions()
 //        startObservingTransactionUpdates()
     }

+ 145 - 88
Lanu/Views/Game/Category/LNGameCategoryListView.swift

@@ -17,43 +17,57 @@ protocol LNGameCategoryListViewDelegate: NSObject {
 
 class LNGameCategoryListView: UIView {
     private let columns = 3
-    private let scrollView = UIScrollView()
-    private let stackView = UIStackView()
-    private var categoryViews: [UIView] = []
+    private let collectionViewLayout = UICollectionViewFlowLayout()
+    private let collectionView: UICollectionView
     
-    private var curCategories: [LNGameTypeItemVO] = []
+    private var categories: [LNGameTypeItemVO] = []
     
     weak var delegate: LNGameCategoryListViewDelegate?
     
     override init(frame: CGRect) {
+        collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
         super.init(frame: frame)
         
         setupViews()
     }
     
     func update(categories: [LNGameTypeItemVO]) {
-        let old = stackView.arrangedSubviews
-        old.forEach {
-            stackView.removeArrangedSubview($0)
-            $0.removeFromSuperview()
-        }
+        self.categories = categories
+        collectionView.reloadData()
         
-        categoryViews.removeAll()
-        for category in categories {
-            let view = buildCategoryPanel(category: category)
-            categoryViews.append(view)
-            stackView.addArrangedSubview(view)
+        DispatchQueue.main.async { [weak self] in
+            guard let self else { return }
+            fixBottomSpace()
         }
-        curCategories = categories
     }
     
     func scrollTo(category: LNGameTypeItemVO) {
-        guard let index = curCategories.firstIndex(where: { $0.code == category.code }),
-              index < categoryViews.count else {
+        guard let index = categories.firstIndex(where: { $0.code == category.code }) else {
             return
         }
-        let view = categoryViews[index]
-        scrollView.scrollRectToVisible(view.frame, animated: true)
+        DispatchQueue.main.async { [weak self] in
+            guard let self else { return }
+            if let headerAttributes = collectionView.layoutAttributesForSupplementaryElement(
+                ofKind: UICollectionView.elementKindSectionHeader,
+                at: IndexPath(item: 0, section: index))
+            {
+                collectionView.setContentOffset(.init(x: 0, y: headerAttributes.frame.origin.y), animated: true)
+            } else {
+                collectionView.scrollToItem(at: .init(row: 0, section: index), at: .top, animated: true)
+            }
+        }
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        let width = (bounds.width - collectionViewLayout.minimumInteritemSpacing) / CGFloat(columns) - collectionViewLayout.minimumInteritemSpacing
+        collectionViewLayout.itemSize = .init(width: width, height: 68)
+        
+        DispatchQueue.main.async { [weak self] in
+            guard let self else { return }
+            fixBottomSpace()
+        }
     }
     
     required init?(coder: NSCoder) {
@@ -62,100 +76,143 @@ class LNGameCategoryListView: UIView {
 }
 
 extension LNGameCategoryListView: UIScrollViewDelegate {
-    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
-        checkPosition(scrollView)
-    }
-    
-    private func checkPosition(_ scrollView: UIScrollView) {
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
         let offsetY = scrollView.contentOffset.y
         
-        let index = categoryViews.firstIndex {
-            CGRectGetMinY($0.frame) > offsetY
+        var section = 0
+        for index in 0..<categories.count {
+            if let headerAttributes = collectionView.layoutAttributesForSupplementaryElement(
+                ofKind: UICollectionView.elementKindSectionHeader,
+                at: IndexPath(item: 0, section: index)) {
+                if offsetY >= headerAttributes.frame.origin.y {
+                    section = index
+                } else {
+                    break
+                }
+            }
         }
         
-        let fixedIndex = if let index, index > 0 {
-            index - 1
-        } else {
-            0
+        delegate?.onCategoryListView(view: self, didScrollTo: categories[section])
+    }
+}
+
+extension LNGameCategoryListView: UICollectionViewDataSource, UICollectionViewDelegate {
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        categories[section].children.count
+    }
+    
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        categories.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LNGameCategoryListCell.className, for: indexPath) as! LNGameCategoryListCell
+        
+        let item = categories[indexPath.section].children[indexPath.row]
+        cell.tabItemView.update(item)
+        
+        return cell
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+        guard kind == UICollectionView.elementKindSectionHeader else {
+            return UICollectionReusableView()
         }
         
-        delegate?.onCategoryListView(view: self, didScrollTo: curCategories[fixedIndex])
+        let view = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: LNGameCategoryListHeader.className, for: indexPath) as! LNGameCategoryListHeader
+        
+        let section = categories[indexPath.section]
+        view.update(section.name)
+        
+        return view
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let category = categories[indexPath.section]
+        let game = category.children[indexPath.row]
+        pushToGameMateList(topCategory: category, category: game, filter: LNGameMateFilter())
     }
 }
 
 extension LNGameCategoryListView {
-    private func setupViews() {
-        scrollView.delegate = self
-        scrollView.showsVerticalScrollIndicator = false
-        scrollView.showsHorizontalScrollIndicator = false
-        addSubview(scrollView)
-        scrollView.snp.makeConstraints { make in
-            make.directionalEdges.equalToSuperview()
+    private func fixBottomSpace() {
+        guard !categories.isEmpty else { return }
+        if let headerAttributes = collectionView.layoutAttributesForSupplementaryElement(
+            ofKind: UICollectionView.elementKindSectionHeader,
+            at: IndexPath(item: 0, section: categories.count - 1)) {
+            let offset = collectionView.contentSize.height - headerAttributes.frame.origin.y
+            collectionView.contentInset = .init(top: 0, left: 0, bottom: bounds.height - offset, right: 0)
         }
+    }
+    
+    private func setupViews() {
+        collectionViewLayout.itemSize = .init(width: 77, height: 68)
+        collectionViewLayout.minimumLineSpacing = 10
+        collectionViewLayout.minimumInteritemSpacing = 12
+        collectionViewLayout.headerReferenceSize = .init(width: 100, height: LNGameCategoryListHeader.height)
+        collectionViewLayout.sectionInset = .init(top: 0, left: 12, bottom: 40, right: 12)
         
-        let fakeView = UIView()
-        scrollView.addSubview(fakeView)
-        fakeView.snp.makeConstraints { make in
-            make.leading.top.trailing.equalToSuperview()
-            make.width.equalToSuperview()
-            make.height.equalTo(0)
+        collectionView.backgroundColor = .clear
+        collectionView.dataSource = self
+        collectionView.delegate = self
+        collectionView.contentInsetAdjustmentBehavior = .never
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.register(LNGameCategoryListCell.self, forCellWithReuseIdentifier: LNGameCategoryListCell.className)
+        collectionView.register(LNGameCategoryListHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: LNGameCategoryListHeader.className)
+        addSubview(collectionView)
+        collectionView.snp.makeConstraints { make in
+            make.directionalEdges.equalToSuperview()
         }
+    }
+}
+
+private class LNGameCategoryListCell: UICollectionViewCell {
+    let tabItemView = LNHomeGameTabItemView()
+    override init(frame: CGRect) {
+        super.init(frame: frame)
         
-        stackView.axis = .vertical
-        stackView.spacing = 26
-        scrollView.addSubview(stackView)
-        stackView.snp.makeConstraints { make in
-            make.leading.equalToSuperview().offset(10)
-            make.trailing.equalToSuperview().offset(-10)
-            make.top.equalToSuperview().offset(18)
-            make.bottom.equalToSuperview()
+        contentView.addSubview(tabItemView)
+        tabItemView.snp.makeConstraints { make in
+            make.directionalEdges.equalToSuperview()
         }
     }
     
-    private func buildCategoryPanel(category: LNGameTypeItemVO) -> UIView {
-        let container = UIStackView()
-        container.axis = .vertical
-        container.spacing = 10
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+}
+
+private class LNGameCategoryListHeader: UICollectionReusableView {
+    static let height = 36
+    private let titleLabel = UILabel()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
         
-        let titleLabel = UILabel()
-        titleLabel.text = category.name
-        titleLabel.font = .heading_h3
-        titleLabel.textColor = .text_5
-        container.addArrangedSubview(titleLabel)
-        
-        var stackView = buildLineStackView()
-        container.addArrangedSubview(stackView)
-        for game in category.children {
-            let itemView = LNHomeGameTabItemView()
-            itemView.update(game)
-            itemView.onTap { [weak self] in
-                guard let self else { return }
-                pushToGameMateList(topCategory: category, category: game, filter: LNGameMateFilter())
-            }
-            stackView.addArrangedSubview(itemView)
-            if stackView.arrangedSubviews.count == columns {
-                stackView = buildLineStackView()
-                container.addArrangedSubview(stackView)
-            }
-        }
-        if stackView.arrangedSubviews.count < columns {
-            for _ in stackView.arrangedSubviews.count..<columns {
-                stackView.addArrangedSubview(UIView())
-            }
+        snp.makeConstraints { make in
+            make.height.equalTo(Self.height)
         }
         
-        return container
+        titleLabel.font = .heading_h2
+        titleLabel.textColor = .text_5
+        addSubview(titleLabel)
+        titleLabel.snp.makeConstraints { make in
+            make.bottom.equalToSuperview().offset(-8)
+            make.leading.equalToSuperview().offset(10)
+        }
     }
     
-    private func buildLineStackView() -> UIStackView {
-        let stackView = UIStackView()
-        stackView.axis = .horizontal
-        stackView.distribution = .fillEqually
-        stackView.spacing = 15
-        return stackView
+    func update(_ title: String) {
+        titleLabel.text = title
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
     }
 }
 
+
 #if DEBUG
 
 import SwiftUI

+ 0 - 101
Lanu/Views/Game/Category/LNGameCategoryTabItemView.swift

@@ -1,101 +0,0 @@
-//
-//  LNGameCategoryTabItemView.swift
-//  Lanu
-//
-//  Created by OneeChan on 2025/11/16.
-//
-
-import Foundation
-import UIKit
-import SnapKit
-
-
-class LNGameCategoryTabItemView: UIView {
-    private let stackView = UIStackView()
-    private let nameLabel = UILabel()
-    private let selectedIc = UIImageView()
-    
-    private(set) var item: LNGameTypeItemVO?
-    private(set) var isSelected: Bool = false
-    
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        
-        setupViews()
-    }
-    
-    func update(_ item: LNGameTypeItemVO) {
-        nameLabel.text = item.name
-        self.item = item
-    }
-    
-    func setSelected(_ selected: Bool) {
-        if selected, selectedIc.superview == nil {
-            stackView.addArrangedSubview(selectedIc)
-        } else if !selected, selectedIc.superview != nil {
-            stackView.removeArrangedSubview(selectedIc)
-            selectedIc.removeFromSuperview()
-        }
-        nameLabel.textColor = selected ? .text_1 : .text_4
-        isSelected = selected
-    }
-    
-    required init?(coder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-}
-
-extension LNGameCategoryTabItemView {
-    private func setupViews() {
-        stackView.axis = .horizontal
-        stackView.spacing = 0
-        addSubview(stackView)
-        stackView.snp.makeConstraints { make in
-            make.leading.equalToSuperview().offset(8)
-            make.trailing.equalToSuperview().offset(-8)
-            make.top.equalToSuperview().offset(4)
-            make.bottom.equalToSuperview().offset(-4)
-        }
-        
-        nameLabel.textAlignment = .center
-        nameLabel.font = .heading_h5
-        nameLabel.textColor = .text_4
-        addSubview(nameLabel)
-        stackView.addArrangedSubview(nameLabel)
-        
-        let config = UIImage.SymbolConfiguration(pointSize: 12)
-        selectedIc.image = .init(systemName: "chevron.right", withConfiguration: config)
-        selectedIc.tintColor = .text_1
-    }
-}
-
-
-#if DEBUG
-
-import SwiftUI
-
-struct LNGameCategoryTabItemViewPreview: UIViewRepresentable {
-    func makeUIView(context: Context) -> some UIView {
-        let container = UIView()
-        container.backgroundColor = .lightGray
-        
-        let view = LNGameCategoryTabItemView()
-        view.backgroundColor = .red
-        container.addSubview(view)
-        view.snp.makeConstraints { make in
-            make.leading.equalToSuperview()
-            make.centerY.equalToSuperview()
-        }
-        
-        view.setSelected(true)
-        
-        return container
-    }
-    
-    func updateUIView(_ uiView: UIViewType, context: Context) { }
-}
-
-#Preview(body: {
-    LNGameCategoryTabItemViewPreview()
-})
-#endif

+ 86 - 9
Lanu/Views/Game/Category/LNGameCategoryTabView.swift

@@ -55,7 +55,7 @@ class LNGameCategoryTabView: UIView {
     func select(_ category: LNGameTypeItemVO) {
         let view = itemViews.first { $0.item?.code == category.code }
         guard let view else { return }
-        selectCategory(view)
+        selectCategory(view, notify: false)
     }
     
     required init?(coder: NSCoder) {
@@ -64,7 +64,7 @@ class LNGameCategoryTabView: UIView {
 }
 
 extension LNGameCategoryTabView {
-    private func selectCategory(_ itemView: LNGameCategoryTabItemView) {
+    private func selectCategory(_ itemView: LNGameCategoryTabItemView, notify: Bool = true) {
         guard !itemView.isSelected,
               let item = itemView.item else { return }
         
@@ -73,7 +73,9 @@ extension LNGameCategoryTabView {
         }
         
         indicator.snp.remakeConstraints { make in
-            make.directionalEdges.equalTo(itemView)
+            make.centerY.equalTo(itemView)
+            make.height.equalTo(24)
+            make.directionalHorizontalEdges.equalToSuperview()
         }
         
         UIView.animate(withDuration: 0.3) { [weak self] in
@@ -81,7 +83,9 @@ extension LNGameCategoryTabView {
             self.layoutIfNeeded()
         }
         
-        delegate?.onGameCategoryTabView(view: self, didSelect: item)
+        if notify {
+            delegate?.onGameCategoryTabView(view: self, didSelect: item)
+        }
     }
 }
 
@@ -103,11 +107,6 @@ extension LNGameCategoryTabView {
             make.height.equalTo(0)
         }
         
-        indicator.image = UIImage.primary_7
-        indicator.layer.cornerRadius = 11
-        indicator.clipsToBounds = true
-        scrollView.addSubview(indicator)
-        
         stackView.axis = .vertical
         stackView.spacing = 20
         stackView.alignment = .center
@@ -118,6 +117,59 @@ extension LNGameCategoryTabView {
             make.top.equalToSuperview().offset(16)
             make.bottom.equalToSuperview()
         }
+        
+        indicator.image = UIImage.primary_7
+        indicator.layer.cornerRadius = 12
+        indicator.clipsToBounds = true
+        stackView.addSubview(indicator)
+    }
+}
+
+private class LNGameCategoryTabItemView: UIView {
+    private let stackView = UIStackView()
+    private let nameLabel = UILabel()
+    
+    private(set) var item: LNGameTypeItemVO?
+    private(set) var isSelected: Bool = false
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        setupViews()
+    }
+    
+    func update(_ item: LNGameTypeItemVO) {
+        nameLabel.text = item.name
+        self.item = item
+    }
+    
+    func setSelected(_ selected: Bool) {
+        nameLabel.textColor = selected ? .text_1 : .text_4
+        isSelected = selected
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+}
+
+extension LNGameCategoryTabItemView {
+    private func setupViews() {
+        stackView.axis = .horizontal
+        stackView.spacing = 0
+        addSubview(stackView)
+        stackView.snp.makeConstraints { make in
+            make.leading.equalToSuperview().offset(8)
+            make.trailing.equalToSuperview().offset(-8)
+            make.top.equalToSuperview().offset(4)
+            make.bottom.equalToSuperview().offset(-4)
+        }
+        
+        nameLabel.textAlignment = .center
+        nameLabel.font = .heading_h5
+        nameLabel.textColor = .text_4
+        addSubview(nameLabel)
+        stackView.addArrangedSubview(nameLabel)
     }
 }
 
@@ -126,6 +178,31 @@ extension LNGameCategoryTabView {
 
 import SwiftUI
 
+struct LNGameCategoryTabItemViewPreview: UIViewRepresentable {
+    func makeUIView(context: Context) -> some UIView {
+        let container = UIView()
+        container.backgroundColor = .lightGray
+        
+        let view = LNGameCategoryTabItemView()
+        view.backgroundColor = .red
+        container.addSubview(view)
+        view.snp.makeConstraints { make in
+            make.leading.equalToSuperview()
+            make.centerY.equalToSuperview()
+        }
+        
+        view.setSelected(true)
+        
+        return container
+    }
+    
+    func updateUIView(_ uiView: UIViewType, context: Context) { }
+}
+
+#Preview(body: {
+    LNGameCategoryTabItemViewPreview()
+})
+
 struct LNGameCategoryTabViewPreview: UIViewRepresentable {
     func makeUIView(context: Context) -> some UIView {
         let container = UIView()

+ 27 - 44
Lanu/Views/Game/MateList/LNGameMateListCell.swift

@@ -36,8 +36,8 @@ class LNGameMateListCell: UITableViewCell {
     
     private let bioLabel = UILabel()
     
-    private let photoSize = 72
-    private let photoStackView = UIStackView()
+    private let maxShow = 3
+    private var photoViews: [UIImageView] = []
     
     private var curItem: LNGameMateListItemVO?
     
@@ -126,26 +126,12 @@ extension LNGameMateListCell: LNVoicePlayerNotify {
 
 extension LNGameMateListCell {
     private func updatePhotos(_ photos: [String]) {
-        var old = photoStackView.arrangedSubviews
-        old.forEach {
-            photoStackView.removeArrangedSubview($0)
-            $0.removeFromSuperview()
-        }
-        
-        if old.count < photos.count {
-            for _ in old.count..<photos.count {
-                old.append(UIImageView())
-            }
-        }
-        for i in 0..<photos.count {
-            guard let view = old[i] as? UIImageView else { return }
-            view.sd_setImage(with: URL(string: photos[i]))
-            view.layer.cornerRadius = 12
-            view.clipsToBounds = true
-            view.snp.remakeConstraints { make in
-                make.width.height.equalTo(photoSize)
+        for index in 0..<maxShow {
+            if index < photos.count {
+                photoViews[index].sd_setImage(with: URL(string: photos[index]))
+            } else {
+                photoViews[index].sd_setImage(with: nil)
             }
-            photoStackView.addArrangedSubview(view)
         }
     }
     
@@ -201,7 +187,7 @@ extension LNGameMateListCell {
         container.addSubview(extra)
         extra.snp.makeConstraints { make in
             make.leading.equalTo(avatar.snp.trailing).offset(14)
-            make.trailing.lessThanOrEqualToSuperview().offset(-12)
+            make.trailing.equalToSuperview().offset(-12)
             make.top.equalTo(game.snp.bottom).offset(3)
         }
         
@@ -248,7 +234,7 @@ extension LNGameMateListCell {
         container.addSubview(playButton)
         playButton.snp.makeConstraints { make in
             make.centerX.equalToSuperview()
-            make.bottom.equalTo(avatar).offset(2)
+            make.bottom.equalTo(avatar).offset(10)
             make.width.equalTo(64)
             make.height.equalTo(22)
         }
@@ -288,7 +274,7 @@ extension LNGameMateListCell {
         price.addSubview(coin)
         coin.snp.makeConstraints { make in
             make.leading.centerY.equalToSuperview()
-            make.width.height.equalTo(14)
+            make.width.height.equalTo(18)
         }
         
         priceLabel.font = .heading_h5
@@ -296,7 +282,7 @@ extension LNGameMateListCell {
         price.addSubview(priceLabel)
         priceLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()
-            make.leading.equalTo(coin.snp.trailing).offset(4)
+            make.leading.equalTo(coin.snp.trailing)
         }
         
         unitLabel.font = .body_s
@@ -342,7 +328,7 @@ extension LNGameMateListCell {
         locationIc.snp.makeConstraints { make in
             make.trailing.equalTo(locationLabel.snp.leading).offset(-2)
             make.centerY.equalTo(nameLabel)
-            make.leading.greaterThanOrEqualTo(genderView.snp.trailing).offset(4)
+            make.leading.greaterThanOrEqualTo(genderView.snp.trailing).offset(16)
         }
         
         container.addSubview(scoreView)
@@ -429,7 +415,7 @@ extension LNGameMateListCell {
         bg.snp.makeConstraints { make in
             make.directionalEdges.equalToSuperview()
             if let image = bg.image {
-                make.size.equalTo(image.size)
+                make.height.equalTo(image.size.height)
             }
         }
         
@@ -446,27 +432,24 @@ extension LNGameMateListCell {
     }
     
     private func buildPhotos() -> UIView {
-        let scrollView = UIScrollView()
-        scrollView.showsVerticalScrollIndicator = false
-        scrollView.showsHorizontalScrollIndicator = false
-        
-        let fakeView = UIView()
-        scrollView.addSubview(fakeView)
-        fakeView.snp.makeConstraints { make in
-            make.leading.top.bottom.equalToSuperview()
-            make.width.equalTo(0)
-            make.height.equalToSuperview()
-            make.height.equalTo(photoSize)
-        }
-        
+        let photoStackView = UIStackView()
         photoStackView.axis = .horizontal
         photoStackView.spacing = 6
-        scrollView.addSubview(photoStackView)
-        photoStackView.snp.makeConstraints { make in
-            make.directionalEdges.equalToSuperview()
+        photoStackView.distribution = .fillEqually
+        photoStackView.alignment = .leading
+        
+        for _ in 0..<maxShow {
+            let icon = UIImageView()
+            icon.layer.cornerRadius = 12
+            icon.clipsToBounds = true
+            photoStackView.addArrangedSubview(icon)
+            icon.snp.makeConstraints { make in
+                make.height.equalTo(icon.snp.width)
+            }
+            photoViews.append(icon)
         }
         
-        return scrollView
+        return photoStackView
     }
 }
 

+ 2 - 2
Lanu/Views/Game/Skill/LNSkillBottomMenuView.swift

@@ -115,11 +115,11 @@ extension LNSkillBottomMenuView {
         priceView.addSubview(coin)
         coin.snp.makeConstraints { make in
             make.leading.centerY.equalToSuperview()
-            make.width.height.equalTo(16)
+            make.width.height.equalTo(20)
         }
         
         priceLabel.font = .body_s
-        priceLabel.textColor = .text_4
+        priceLabel.textColor = .text_5
         priceView.addSubview(priceLabel)
         priceLabel.snp.makeConstraints { make in
             make.verticalEdges.trailing.equalToSuperview()

+ 2 - 1
Lanu/Views/Game/Skill/LNSkillDetailViewController.swift

@@ -225,7 +225,8 @@ extension LNSkillDetailViewController {
         
         container.addSubview(photosView)
         photosView.snp.makeConstraints { make in
-            make.directionalHorizontalEdges.equalToSuperview().inset(16)
+            make.leading.equalToSuperview().inset(16)
+            make.trailing.equalToSuperview().offset(-29)
             make.top.equalTo(tagView.snp.bottom).offset(16)
             make.bottom.equalToSuperview()
         }

+ 4 - 4
Lanu/Views/Game/Skill/LNSkillPhotosView.swift

@@ -40,16 +40,16 @@ class LNSkillPhotosView: UIView {
             }
             container.addSubview(imageView)
             imageView.snp.makeConstraints { make in
-                make.center.equalToSuperview()
-                make.height.equalToSuperview()
-                make.width.equalToSuperview().inset(5)
+                make.leading.equalToSuperview()
+                make.trailing.equalToSuperview().offset(-10)
+                make.verticalEdges.equalToSuperview()
             }
             imageView.sd_setImage(with: URL(string: url))
             
             if index == 0, !detail.voiceBar.isEmpty {
                 container.addSubview(voiceBar)
                 voiceBar.snp.makeConstraints { make in
-                    make.trailing.equalToSuperview().offset(-10)
+                    make.trailing.equalToSuperview().offset(-20)
                     make.bottom.equalToSuperview().offset(-10)
                 }
                 voiceBar.setVoice(detail.voiceBar)

+ 6 - 6
Lanu/Views/Game/Skill/LNSkillTagView.swift

@@ -30,12 +30,12 @@ class LNSkillTagView: UIView {
                 make.height.equalTo(31)
             }
             
-            let title = UILabel()
-            title.text = $0
-            title.font = .body_s
-            title.textColor = .text_4
-            container.addSubview(title)
-            title.snp.makeConstraints { make in
+            let titleLabel = UILabel()
+            titleLabel.text = $0
+            titleLabel.font = .body_s
+            titleLabel.textColor = .text_4
+            container.addSubview(titleLabel)
+            titleLabel.snp.makeConstraints { make in
                 make.centerY.equalToSuperview()
                 make.directionalHorizontalEdges.equalToSuperview().inset(10)
             }

+ 20 - 16
Lanu/Views/Game/Skill/LNSkillUserInfoView.swift

@@ -31,18 +31,10 @@ class LNSkillUserInfoView: UIView {
     func update(_ detail: LNGameMateSkillDetailVO) {
         userNameLabel.text = detail.nickname
         genderView.update(detail.gender, detail.age)
-        
         scoreLabel.text = "\(detail.star)"
-        
-        let orderText: String = .init(key: "A00046", detail.orderCount)
-        let attStr = NSMutableAttributedString(string: orderText)
-        let range = (orderText as NSString).range(of: "\(detail.orderCount)")
-        attStr.addAttributes([.font: UIFont.heading_h5], range: range)
-        orderCountLabel.attributedText = attStr
-        
-        locationLabel.text = detail.area
+        orderCountLabel.text = "\(detail.orderCount)"
+        locationLabel.text = "\(detail.distance)km"
         idLabel.text = "ID \(detail.userNo)"
-        
         languageLabel.text = detail.languageNames.joined(separator: " / ")
         
         curDetail = detail
@@ -75,6 +67,7 @@ extension LNSkillUserInfoView {
         let userView = UIStackView()
         userView.axis = .vertical
         userView.spacing = 2
+        userView.alignment = .leading
         
         let nameView = UIView()
         userView.addArrangedSubview(nameView)
@@ -98,7 +91,7 @@ extension LNSkillUserInfoView {
         userView.addArrangedSubview(idView)
         
         let locationIc = UIImageView()
-        locationIc.image = .init(named: "ic_location")
+        locationIc.image = .init(named: "ic_location")?.withRenderingMode(.alwaysTemplate)
         locationIc.tintColor = .text_2
         idView.addSubview(locationIc)
         locationIc.snp.makeConstraints { make in
@@ -145,7 +138,8 @@ extension LNSkillUserInfoView {
         userView.addArrangedSubview(languageView)
         
         let languageIc = UIImageView()
-        languageIc.image = .init(named: "ic_language")
+        languageIc.image = .init(named: "ic_language")?.withRenderingMode(.alwaysTemplate)
+        languageIc.tintColor = .text_2
         languageView.addSubview(languageIc)
         languageIc.snp.makeConstraints { make in
             make.leading.verticalEdges.equalToSuperview()
@@ -184,14 +178,24 @@ extension LNSkillUserInfoView {
             make.trailing.equalTo(scoreLabel.snp.leading).offset(-4)
         }
         
-        orderCountLabel.font = .body_xs
+        let orderLabel = UILabel()
+        orderLabel.text = .init(key: "A00041")
+        orderLabel.font = .body_xs
+        orderLabel.textColor = .text_1
+        container.addSubview(orderLabel)
+        orderLabel.snp.makeConstraints { make in
+            make.leading.equalToSuperview()
+            make.bottom.equalToSuperview()
+            make.top.equalTo(scoreLabel.snp.bottom)
+        }
+        
+        orderCountLabel.font = .heading_h5
         orderCountLabel.textColor = .text_1
         orderCountLabel.textAlignment = .center
         container.addSubview(orderCountLabel)
         orderCountLabel.snp.makeConstraints { make in
-            make.directionalHorizontalEdges.equalToSuperview()
-            make.bottom.equalToSuperview()
-            make.top.equalTo(scoreLabel.snp.bottom)
+            make.leading.equalTo(orderLabel.snp.trailing)
+            make.centerY.equalTo(orderLabel)
         }
         
         return container

+ 21 - 18
Lanu/Views/Game/Skill/LNSkillVoiceBarView.swift

@@ -72,6 +72,10 @@ extension LNSkillVoiceBarView: LNVoicePlayerNotify {
 extension LNSkillVoiceBarView {
     private func setupViews() {
         isHidden = true
+        snp.makeConstraints { make in
+            make.width.equalTo(86)
+            make.height.equalTo(31)
+        }
         
         let button = UIButton()
         button.setBackgroundImage(.primary_7, for: .normal)
@@ -89,40 +93,39 @@ extension LNSkillVoiceBarView {
         addSubview(button)
         button.snp.makeConstraints { make in
             make.directionalEdges.equalToSuperview()
-            make.height.equalTo(31)
-        }
-        
-        let container = UIView()
-        container.isUserInteractionEnabled = false
-        addSubview(container)
-        container.snp.makeConstraints { make in
-            make.centerY.equalToSuperview()
-            make.directionalHorizontalEdges.equalToSuperview().inset(8)
         }
         
         playStateIc.image = .init(named: "ic_voice_play")
-        container.addSubview(playStateIc)
+        addSubview(playStateIc)
         playStateIc.snp.makeConstraints { make in
-            make.leading.equalToSuperview()
-            make.verticalEdges.equalToSuperview()
+            make.leading.equalToSuperview().offset(8)
+            make.centerY.equalToSuperview()
         }
         
         voiceWaveView.build()
-        container.addSubview(voiceWaveView)
+        voiceWaveView.isUserInteractionEnabled = false
+        addSubview(voiceWaveView)
         voiceWaveView.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.leading.equalTo(playStateIc.snp.trailing).offset(6)
-            make.width.equalTo(19)
+            make.leading.equalTo(playStateIc.snp.trailing).offset(5)
+            make.width.equalTo(18.5)
             make.height.equalTo(11)
         }
         
+        let container = UIView()
+        container.isUserInteractionEnabled = false
+        addSubview(container)
+        container.snp.makeConstraints { make in
+            make.verticalEdges.equalToSuperview()
+            make.trailing.equalToSuperview().offset(-7)
+            make.leading.equalTo(voiceWaveView.snp.trailing).offset(4)
+        }
+        
         durationLabel.font = .heading_h5
         durationLabel.textColor = .text_1
         container.addSubview(durationLabel)
         durationLabel.snp.makeConstraints { make in
-            make.centerY.equalToSuperview()
-            make.leading.equalTo(voiceWaveView.snp.trailing).offset(4)
-            make.trailing.equalToSuperview()
+            make.center.equalToSuperview()
         }
     }
 }

+ 14 - 6
Lanu/Views/Home/GameTab/LNHomeActivityTabItemView.swift

@@ -31,15 +31,24 @@ class LNHomeActivityTabItemView: UIView {
     
     func setSelected(_ selected: Bool) {
         selectedIc.image = .init(named: selected ? "ic_home_activity_tab_selected" : "ic_home_activity_tab")
+        nameLabel.font = selected ? .heading_h5 : .body_s
     }
     
     func toBeMoreTab() {
-        selectedIc.snp.updateConstraints { make in
+        selectedIc.snp.remakeConstraints { make in
+            make.leading.equalToSuperview()
+            make.verticalEdges.equalToSuperview().inset(4)
             make.trailing.equalToSuperview().offset(-3)
         }
         cover.image = .init(named: "ic_home_game_tab_more")
         cover.contentMode = .center
+        cover.snp.remakeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(selectedIc)
+        }
+        
         nameLabel.text = .init(key: "A00048")
+        nameLabel.font = .heading_h5
         nameLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
         nameLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
     }
@@ -55,14 +64,13 @@ extension LNHomeActivityTabItemView {
         addSubview(selectedIc)
         selectedIc.snp.makeConstraints { make in
             make.leading.equalToSuperview()
-            make.verticalEdges.equalToSuperview().inset(5)
-            make.trailing.equalToSuperview().offset(-20)
+            make.verticalEdges.equalToSuperview().inset(4)
         }
         
         addSubview(cover)
         cover.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.trailing.equalToSuperview()
+            make.trailing.equalTo(selectedIc).offset(15)
             make.width.height.equalTo(67)
         }
         
@@ -74,8 +82,8 @@ extension LNHomeActivityTabItemView {
         addSubview(nameLabel)
         nameLabel.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.leading.equalToSuperview().offset(10)
-            make.trailing.lessThanOrEqualTo(cover.snp.trailing).offset(-10)
+            make.leading.equalToSuperview().offset(9)
+            make.width.lessThanOrEqualTo(73)
         }
     }
 }

+ 3 - 2
Lanu/Views/Home/GameTab/LNHomeActivityTabView.swift

@@ -82,12 +82,13 @@ extension LNHomeActivityTabView {
         }
         
         stackView.axis = .horizontal
-        stackView.distribution = .equalSpacing
+        stackView.distribution = .fillEqually
+        stackView.spacing = 2
         addSubview(stackView)
         stackView.snp.makeConstraints { make in
             make.leading.equalToSuperview()
             make.top.equalToSuperview()
-            make.trailing.equalTo(moreView.snp.leading).offset(-20)
+            make.trailing.equalTo(moreView.snp.leading).offset(-2)
         }
     }
 }

+ 2 - 1
Lanu/Views/Home/GameTab/LNHomeGameTabItemView.swift

@@ -31,6 +31,7 @@ class LNHomeGameTabItemView: UIView {
     
     func setSelected(_ selected: Bool) {
         selectedBorder.image = selected ? .primary_7 : nil
+        nameLabel.font = selected ? .heading_h5 : .body_s
     }
     
     func toBeMoreTab() {
@@ -62,7 +63,7 @@ extension LNHomeGameTabItemView {
             make.leading.greaterThanOrEqualToSuperview()
         }
         
-        cover.backgroundColor = .fill.withAlphaComponent(0.8)
+        cover.backgroundColor = .fill.withAlphaComponent(0.7)
         cover.layer.cornerRadius = 25
         cover.clipsToBounds = true
         addSubview(cover)

+ 1 - 1
Lanu/Views/IM/Chat/Cells/LNIMChatOrderMessageCell.swift

@@ -123,7 +123,7 @@ class LNIMChatOrderMessageCell: UITableViewCell {
                 tipsLabel.text = "A00059"
             }
         case .cancelled:
-            statusIc.image = .init(named: "ic_im_chat_order_success")
+            statusIc.image = .init(named: "ic_im_chat_order_cancel")
             statusLabel.text = .init(key: "A00071")
             
             if isCreator {

+ 11 - 11
Lanu/Views/IM/Chat/GameMate/LNIMChatGameMateOrderView.swift

@@ -90,18 +90,20 @@ extension LNIMChatGameMateOrderView {
         let isCreator = order.buyerUserNo.isMyUid
         
         gameIc.sd_setImage(with: URL(string: order.categoryIcon))
-        descLabel.text = "\(order.bizCategoryName)x\(order.purchaseQty)\(order.unit)"
+        descLabel.text = "\(order.bizCategoryName) x \(order.purchaseQty)\(order.unit)"
         extraLabel.text = .init(key: "A00056", order.customerRemark)
         
         actionView.isHidden = true
         extraView.isHidden = true
+        replyView.isHidden = true
+        titleArrow.isHidden = true
+        contentArrow.isHidden = true
         stopCountDown()
         
         switch order.status {
         case .created, .waitingForAccept:
             titleLabel.text = .init(key: "A00078")
             titleArrow.isHidden = false
-            contentArrow.isHidden = true
             replyView.isHidden = false
             let remain = 3600 - (Int(curTime) - order.createTime / 1_000)
             replyRemainLabel.text = String(format: "%02d:%02d", remain/60, remain%60)
@@ -111,16 +113,14 @@ extension LNIMChatGameMateOrderView {
             }
         case .accepted:
             titleLabel.text = .init(key: "A00079")
-            titleArrow.isHidden = !isCreator
-            contentArrow.isHidden = isCreator
-            if !isCreator {
+            contentArrow.isHidden = false
+            if !isCreator, !order.customerRemark.isEmpty {
                 extraView.isHidden = false
             }
         case .servicing:
             titleLabel.text = .init(key: "A00080")
-            titleArrow.isHidden = !isCreator
-            contentArrow.isHidden = isCreator
-            if !isCreator {
+            contentArrow.isHidden = false
+            if !isCreator, !order.customerRemark.isEmpty {
                 extraView.isHidden = false
             }
         case .completed, .refunded, .rejected, .serviceDone, .cancelled:
@@ -143,7 +143,8 @@ extension LNIMChatGameMateOrderView {
         addSubview(container)
         container.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(16)
-            make.verticalEdges.equalToSuperview()
+            make.bottom.equalToSuperview()
+            make.top.equalToSuperview().offset(8)
         }
         
         let stackView = UIStackView()
@@ -203,7 +204,6 @@ extension LNIMChatGameMateOrderView {
         replyTitleLabel.font = .body_s
         replyTitleLabel.text = .init(key: "A00081")
         replyTitleLabel.textColor = .text_5
-        replyTitleLabel.textAlignment = .center
         replyView.addSubview(replyTitleLabel)
         replyTitleLabel.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview()
@@ -214,7 +214,7 @@ extension LNIMChatGameMateOrderView {
         replyRemainLabel.textColor = .text_5
         replyView.addSubview(replyRemainLabel)
         replyRemainLabel.snp.makeConstraints { make in
-            make.directionalHorizontalEdges.equalToSuperview()
+            make.centerX.equalToSuperview()
             make.bottom.equalToSuperview()
             make.top.equalTo(replyTitleLabel.snp.bottom).offset(1)
         }

+ 4 - 2
Lanu/Views/IM/Chat/GameMate/LNIMChatGameMateSkillCell.swift

@@ -40,7 +40,7 @@ class LNIMChatGameMateSkillCell: UIView {
         
         gameIc.sd_setImage(with: URL(string: skill.icon))
         gameNameLabel.text = skill.name
-        gamePriceLabel.text = "\(skill.price.toDisplay)/\(skill.unit)"
+        gamePriceLabel.text = "\(skill.price.toDisplay) / \(skill.unit)"
         curItem = skill
     }
     
@@ -65,12 +65,14 @@ extension LNIMChatGameMateSkillCell {
         background.layer.cornerRadius = 11
         background.clipsToBounds = true
         background.image = .primary_6
+        background.alpha = 0.8
         container.addSubview(background)
         background.snp.makeConstraints { make in
             make.directionalEdges.equalToSuperview().inset(1).priority(.medium)
         }
         
         gameIc.layer.cornerRadius = 25
+        gameIc.backgroundColor = .fill.withAlphaComponent(0.7)
         gameIc.clipsToBounds = true
         container.addSubview(gameIc)
         gameIc.snp.makeConstraints { make in
@@ -143,7 +145,7 @@ extension LNIMChatGameMateSkillCell {
             make.leading.equalToSuperview()
             make.bottom.equalToSuperview()
             make.top.equalTo(gameNameLabel.snp.bottom).offset(5)
-            make.width.height.equalTo(14.5)
+            make.width.height.equalTo(18)
         }
         
         gamePriceLabel.text = "123"

+ 3 - 6
Lanu/Views/IM/Chat/GameMate/LNIMChatGameMateSkillView.swift

@@ -124,10 +124,6 @@ extension LNIMChatGameMateSkillView: UIScrollViewDelegate {
  
 extension LNIMChatGameMateSkillView {
     private func buildSkillViews() {
-        snp.updateConstraints{ make in
-            make.height.equalTo(skills.isEmpty ? 0 : 72)
-        }
-        
         if skills.count < 3 {
             scrollView.setContentOffset(.init(x: 0, y: 0), animated: false)
             for (index, skill) in skills.enumerated() {
@@ -152,7 +148,7 @@ extension LNIMChatGameMateSkillView {
     private func setupViews() {
         clipsToBounds = true
         snp.makeConstraints { make in
-            make.height.equalTo(0)
+            make.height.equalTo(72).priority(.medium)
         }
         
         scrollView.backgroundColor = .clear
@@ -164,7 +160,8 @@ extension LNIMChatGameMateSkillView {
         addSubview(scrollView)
         scrollView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(14)
-            make.directionalVerticalEdges.equalToSuperview()
+            make.bottom.equalToSuperview()
+            make.top.equalToSuperview().offset(8)
         }
         
         let fakeView = UIView()

+ 2 - 1
Lanu/Views/IM/Chat/LNIMChatViewController.swift

@@ -203,7 +203,7 @@ extension LNIMChatViewController {
         view.addSubview(stackView)
         stackView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview()
-            make.top.equalTo(topMenu.snp.bottom).offset(8)
+            make.top.equalTo(topMenu.snp.bottom)
         }
         
         skillView.viewModel = viewModel
@@ -229,6 +229,7 @@ extension LNIMChatViewController {
         tableView.register(LNIMChatOrderMessageCell.self, forCellReuseIdentifier: LNIMChatOrderMessageCell.className)
         tableView.dataSource = self
         tableView.delegate = self
+        tableView.contentInset = .init(top: 8, left: 0, bottom: 0, right: 0)
         view.addSubview(tableView)
         tableView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview()

+ 2 - 3
Lanu/Views/Login/Setup/LNInterestSetupViewController.swift

@@ -215,6 +215,7 @@ extension LNInterestSetupViewController {
         descLabel.font = .body_m
         descLabel.textColor = .text_3
         descLabel.text = .init(key: "A00112")
+        descLabel.numberOfLines = 0
         stackView.addArrangedSubview(descLabel)
         
         return stackView
@@ -227,7 +228,7 @@ extension LNInterestSetupViewController {
         collectionViewLayout.minimumLineSpacing = lineSpace
         collectionViewLayout.minimumInteritemSpacing = itemSpacing
         collectionViewLayout.headerReferenceSize = .init(width: view.bounds.width - horizentalSpace * 2, height: Self.headerHeight)
-        collectionViewLayout.sectionHeadersPinToVisibleBounds = true
+//        collectionViewLayout.sectionHeadersPinToVisibleBounds = true
         
         collectionView.backgroundColor = .clear
         collectionView.dataSource = self
@@ -350,8 +351,6 @@ private class LNInterestGroupHeader: UICollectionReusableView {
     override init(frame: CGRect) {
         super.init(frame: frame)
         
-        backgroundColor = .primary_1
-        
         snp.makeConstraints { make in
             make.height.equalTo(LNInterestSetupViewController.headerHeight)
         }

+ 5 - 0
Lanu/Views/Main/LNMainTabBar.swift

@@ -28,6 +28,11 @@ class LNMainTabBar: UITabBar {
         gradientLayer.endPoint = CGPoint(x: 0, y: 1)
         layer.addSublayer(gradientLayer)
         
+        container.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
+        container.layer.shadowOffset = .init(width: 0, height: 2)
+        container.layer.shadowRadius = 6
+        container.layer.shadowOpacity = 0.8
+        container.layer.masksToBounds = false
         addSubview(container)
         container.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(42)

+ 6 - 0
Lanu/Views/Main/LNMainViewController.swift

@@ -15,6 +15,12 @@ class LNMainViewController: UITabBarController {
         LNEventDeliver.addObserver(self)
         
         if #available(iOS 26, *) {
+            let container = tabBar.subviews.first
+            container?.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
+            container?.layer.shadowOffset = .init(width: 0, height: 2)
+            container?.layer.shadowRadius = 6
+            container?.layer.shadowOpacity = 0.8
+            container?.layer.masksToBounds = false
         } else {
             let tabBar = LNMainTabBar()
             tabBar.delegate = self

+ 1 - 0
Lanu/Views/Order/Create/LNCreateOrderPanel.swift

@@ -409,6 +409,7 @@ extension LNCreateOrderPanel {
         extraInput.textColor = .text_5
         extraInput.clearButtonMode = .whileEditing
         extraInput.delegate = self
+        extraInput.visiableView = extraView
         extraView.addSubview(extraInput)
         extraInput.snp.makeConstraints { make in
             make.centerY.equalToSuperview()

+ 4 - 19
Lanu/Views/Order/Create/LNCreateOrderViewController.swift

@@ -32,7 +32,6 @@ class LNCreateOrderViewController: LNViewController {
     private let avatar = UIImageView()
     private let nameLabel = UILabel()
     private let starLabel = UILabel()
-    private let orderCountLabel = UILabel()
     
     private let skillIc = UIImageView()
     private let skillNameLabel = UILabel()
@@ -91,7 +90,6 @@ extension LNCreateOrderViewController {
         avatar.sd_setImage(with: URL(string: detail.avatar))
         nameLabel.text = detail.nickname
         starLabel.text = "\(detail.star)"
-//        orderCountLabel.text = "(\(detail.orderCount))"
         
         let skill = LNGameMateManager.shared.gameCategory(for: detail.categoryCode)
         skillIc.sd_setImage(with: URL(string: skill?.icon ?? ""))
@@ -107,7 +105,6 @@ extension LNCreateOrderViewController {
         avatar.sd_setImage(with: URL(string: detail.avatar))
         nameLabel.text = detail.nickname
         starLabel.text = "\(detail.star)"
-//        orderCountLabel.text = "(\(detail.purchaseQty))"
         
         skillIc.sd_setImage(with: URL(string: detail.bizCategoryIcon))
         skillNameLabel.text = detail.bizCategoryName
@@ -126,14 +123,14 @@ extension LNCreateOrderViewController {
         if let skill {
             let cost = skill.price * Double(curCount)
             let text: String = .init(key: "A00121", cost.toDisplay, curCount, skill.unit)
-            let attrStr = NSMutableAttributedString(string: text)
+            let attrStr = NSMutableAttributedString(string: text, attributes: [.font: UIFont.body_s])
             let range = (text as NSString).range(of: "\(cost.toDisplay)")
             attrStr.addAttribute(.font, value: UIFont.heading_h2, range: range)
             costLabel.attributedText = attrStr
         } else if let qrDetail {
             let cost = qrDetail.price * Double(curCount)
             let text: String = .init(key: "A00121", cost.toDisplay, curCount, qrDetail.unit)
-            let attrStr = NSMutableAttributedString(string: text)
+            let attrStr = NSMutableAttributedString(string: text, attributes: [.font: UIFont.body_s])
             let range = (text as NSString).range(of: "\(cost.toDisplay)")
             attrStr.addAttribute(.font, value: UIFont.heading_h2, range: range)
             costLabel.attributedText = attrStr
@@ -204,15 +201,6 @@ extension LNCreateOrderViewController {
         starLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()
             make.leading.equalTo(star.snp.trailing).offset(4)
-        }
-        
-        orderCountLabel.isHidden = true
-        orderCountLabel.font = .body_xs
-        orderCountLabel.textColor = .text_5
-        starView.addSubview(orderCountLabel)
-        orderCountLabel.snp.makeConstraints { make in
-            make.centerY.equalToSuperview()
-            make.leading.equalTo(starLabel.snp.trailing).offset(4)
             make.trailing.equalToSuperview()
         }
         
@@ -304,8 +292,8 @@ extension LNCreateOrderViewController {
         priceView.addSubview(coinIc)
         coinIc.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.trailing.equalTo(skillPriceLabel.snp.leading).offset(-4)
-            make.width.height.equalTo(16)
+            make.trailing.equalTo(skillPriceLabel.snp.leading).offset(-2)
+            make.width.height.equalTo(20)
         }
         
         let countView = UIView()
@@ -443,8 +431,6 @@ extension LNCreateOrderViewController {
     
     private func buildMenu() -> UIView {
         let holder = UIView()
-        holder.backgroundColor = .fill
-        holder.layer.cornerRadius = 12
         
         let container = UIView()
         container.backgroundColor = .init(hex: "#ECF6FF")
@@ -506,7 +492,6 @@ extension LNCreateOrderViewController {
             make.width.height.equalTo(16)
         }
         
-        costLabel.font = .body_s
         costLabel.textColor = .text_4
         priceView.addSubview(costLabel)
         costLabel.snp.makeConstraints { make in

+ 1 - 1
Lanu/Views/Order/Detail/LNOrderDetailViewController.swift

@@ -198,7 +198,7 @@ extension LNOrderDetailViewController {
         let bottomMenu = buildBottomMenu()
         view.addSubview(bottomMenu)
         bottomMenu.snp.makeConstraints { make in
-            make.directionalHorizontalEdges.equalToSuperview()
+            make.directionalHorizontalEdges.equalToSuperview().inset(16)
             make.bottom.equalToSuperview().offset(-view.safeBottomInset - 10)
             make.height.equalTo(0).priority(.low)
         }

+ 24 - 22
Lanu/Views/Order/OrderList/LNOrderListItemCell.swift

@@ -60,8 +60,7 @@ extension LNOrderListItemCell {
         guard let orderItem = curItem else { return }
         switch orderItem.status {
         case .created, .waitingForAccept: // 可以取消
-            LNCommonAlertView.showCancelOrderAlert(orderId: orderItem.orderId) { [weak self] success in
-                guard let self else { return }
+            LNCommonAlertView.showCancelOrderAlert(orderId: orderItem.orderId) { success in
                 guard success else { return }
                 orderItem.status = .cancelled
             }
@@ -69,15 +68,13 @@ extension LNOrderListItemCell {
         case .completed: // 可以评价
             let panel = LNOrderCommentPanel()
             panel.update(orderItem.avatar, orderId: orderItem.orderId)
-            panel.handler = { [weak self] star, comment in
-                guard let self else { return }
+            panel.handler = { star, comment in
                 orderItem.star = star
             }
             panel.showIn()
             break
         case .serviceDone: // 可以点击完成
-            LNCommonAlertView.showFinishOrderAlert(orderId: orderItem.orderId) { [weak self] success in
-                guard let self else { return }
+            LNCommonAlertView.showFinishOrderAlert(orderId: orderItem.orderId) { success in
                 guard success else { return }
                 orderItem.status = .completed
             }
@@ -159,6 +156,11 @@ extension LNOrderListItemCell {
     
     private func setupViews() {
         backgroundColor = .clear
+        onTap { [weak self] in
+            guard let self else { return }
+            guard let curItem else { return }
+            pushToOrderDetail(orderId: curItem.orderId)
+        }
         
         let container = UIView()
         container.backgroundColor = .fill
@@ -255,7 +257,7 @@ extension LNOrderListItemCell {
             make.height.equalTo(38)
         }
         
-        totalLabel.font = .heading_h4
+        totalLabel.font = .heading_h3
         totalLabel.textColor = .text_5
         container.addSubview(totalLabel)
         totalLabel.snp.makeConstraints { make in
@@ -268,7 +270,7 @@ extension LNOrderListItemCell {
         coin.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
             make.trailing.equalTo(totalLabel.snp.leading).offset(-2)
-            make.width.height.equalTo(16)
+            make.width.height.equalTo(20)
         }
         
         let text = UILabel()
@@ -349,31 +351,31 @@ extension LNOrderListItemCell {
     private func buildPriceView() -> UIView {
         let container = UIView()
         
+        let coin = UIImageView.coinImageView()
+        container.addSubview(coin)
+        coin.snp.makeConstraints { make in
+            make.top.equalToSuperview()
+            make.leading.equalToSuperview()
+            make.width.height.equalTo(16)
+        }
+        
         priceLabel.font = .body_s
         priceLabel.textColor = .text_5
         container.addSubview(priceLabel)
         priceLabel.snp.makeConstraints { make in
-            make.top.trailing.equalToSuperview()
-        }
-        
-        let coin = UIImageView.coinImageView()
-        container.addSubview(coin)
-        coin.snp.makeConstraints { make in
-            make.centerY.equalTo(priceLabel)
-            make.leading.greaterThanOrEqualToSuperview()
-            make.trailing.equalTo(priceLabel.snp.leading)
-            make.width.height.equalTo(14)
+            make.trailing.equalToSuperview()
+            make.leading.equalTo(coin.snp.trailing)
+            make.centerY.equalTo(coin)
         }
         
-        countLabel.font = .body_s
-        countLabel.textColor = .text_5
+        countLabel.font = .body_xs
+        countLabel.textColor = .text_4
         countLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
         countLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
         container.addSubview(countLabel)
         countLabel.snp.makeConstraints { make in
             make.trailing.bottom.equalToSuperview()
-            make.top.equalTo(priceLabel.snp.bottom)
-            make.leading.equalToSuperview()
+            make.top.equalTo(coin.snp.bottom).offset(2)
         }
         
         return container

+ 0 - 6
Lanu/Views/Order/OrderList/LNOrderListViewController.swift

@@ -89,12 +89,6 @@ extension LNOrderListViewController: UITableViewDelegate, UITableViewDataSource
         
         return cell
     }
-    
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        tableView.deselectRow(at: indexPath, animated: true)
-        let item = orders[indexPath.row]
-        view.pushToOrderDetail(orderId: item.orderId)
-    }
 }
 
 extension LNOrderListViewController: LNOrderManagerNotify {

+ 18 - 6
Lanu/Views/Order/OrderQR/LNOrderCustomView.swift

@@ -20,7 +20,9 @@ class LNOrderCustomView: UIView {
     private let unitLabel = UILabel()
     private let countLabel = UILabel()
     private let addButton = UIButton()
+    
     private let costLabel = UILabel()
+    private let costUnitLabel = UILabel()
     
     private var curSkill: LNGameMateSkillVO?
     
@@ -63,11 +65,13 @@ extension LNOrderCustomView {
     private func updateEditCost() {
         guard let skill = curSkill else { return }
         let cost = skill.price * Double(customCount)
-        let text: String = .init(key: "A00121", cost.toDisplay, customCount, skill.unit)
-        let attrStr = NSMutableAttributedString(string: text)
-        let range = (text as NSString).range(of: "\(cost.toDisplay)")
-        attrStr.addAttribute(.font, value: UIFont.heading_h2, range: range)
-        costLabel.attributedText = attrStr
+        
+        costLabel.text = cost.toDisplay
+        costUnitLabel.text = if customCount <= 1 {
+            "/\(skill.unit)"
+        } else {
+            "/\(customCount) \(skill.unit)"
+        }
     }
     
     private func setupViews() {
@@ -248,12 +252,20 @@ extension LNOrderCustomView {
             make.centerY.equalToSuperview()
         }
         
-        costLabel.font = .body_m
+        costLabel.font = .heading_h2
         costLabel.textColor = .text_5
         container.addSubview(costLabel)
         costLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()
             make.leading.equalTo(coin.snp.trailing).offset(6)
+        }
+        
+        costUnitLabel.font = .body_m
+        costUnitLabel.textColor = .text_5
+        container.addSubview(costUnitLabel)
+        costUnitLabel.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.leading.equalTo(costLabel.snp.trailing)
             make.trailing.equalToSuperview()
         }
         

+ 4 - 6
Lanu/Views/Order/OrderQR/LNOrderGenerateQRCodePanel.swift

@@ -13,7 +13,7 @@ import SnapKit
 class LNOrderGenerateQRCodePanel: LNPopupView {
     private let curSkillIc = UIImageView()
     private let curSkillNameLabel = UILabel()
-    private let skillArrow = UIImageView()
+    private let skillArrow = UIImageView.arrowImageView(size: 14)
     
     private let tabView = LNOrderQRTabView()
     
@@ -114,8 +114,8 @@ extension LNOrderGenerateQRCodePanel {
         container.addSubview(curSkillIc)
         curSkillIc.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.leading.equalToSuperview().offset(22)
-            make.width.height.equalTo(20)
+            make.leading.equalToSuperview().offset(16)
+            make.width.height.equalTo(26)
         }
         
         curSkillNameLabel.font = .heading_h5
@@ -126,13 +126,11 @@ extension LNOrderGenerateQRCodePanel {
             make.leading.equalTo(curSkillIc.snp.trailing).offset(4)
         }
         
-        let config = UIImage.SymbolConfiguration(pointSize: 10)
-        skillArrow.image = .init(systemName: "chevron.forward", withConfiguration: config)
         skillArrow.tintColor = .text_4
         container.addSubview(skillArrow)
         skillArrow.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.leading.equalTo(curSkillNameLabel.snp.trailing).offset(16)
+            make.leading.equalTo(curSkillNameLabel.snp.trailing).offset(4)
         }
         
         container.onTap { [weak self] in

+ 21 - 19
Lanu/Views/Order/OrderQR/LNOrderQRCodeShowView.swift

@@ -14,7 +14,8 @@ class LNOrderQRCodeShowView: UIView {
     private let qrCodeSize: CGFloat = 145
     
     private let qrCodeIc = UIImageView()
-    private let costLabel = UILabel()
+    private let priceLabel = UILabel()
+    private let unitLabel = UILabel()
     
     private let saveButton = UIButton()
     private let copyButton = UIButton()
@@ -34,20 +35,12 @@ class LNOrderQRCodeShowView: UIView {
         let count = customCount ?? 1
         if skill.id == curSkill?.id, count == self.count { return }
         
-        let price = skill.price
-        
-        let type: LNOrderSource = customCount != nil ? .custom : .normal
-        
-        let cost = price * Double(count)
-        let text: String = if count <= 1 {
-            .init(key: "A00040", cost.toDisplay, skill.unit)
+        priceLabel.text = skill.price.toDisplay
+        unitLabel.text = if count <= 1 {
+            "/\(skill.unit)"
         } else {
-            .init(key: "A00121", cost.toDisplay, count, skill.unit)
+            "/\(count) \(skill.unit)"
         }
-        let attrStr = NSMutableAttributedString(string: text)
-        let range = (text as NSString).range(of: "\(cost.toDisplay)")
-        attrStr.addAttribute(.font, value: UIFont.heading_h1, range: range)
-        costLabel.attributedText = attrStr
         
         qrCodeIc.image = nil
         
@@ -57,6 +50,7 @@ class LNOrderQRCodeShowView: UIView {
         saveButton.isEnabled = false
         copyButton.isEnabled = false
         
+        let type: LNOrderSource = customCount != nil ? .custom : .normal
         LNOrderManager.shared.createOrderQR(
             skillId: skill.id,
             count: count,
@@ -107,15 +101,23 @@ extension LNOrderQRCodeShowView {
         coin.snp.makeConstraints { make in
             make.leading.equalToSuperview()
             make.centerY.equalToSuperview()
-            make.width.height.equalTo(19)
+            make.width.height.equalTo(22)
         }
         
-        costLabel.font = .body_m
-        costLabel.textColor = .text_5
-        priceView.addSubview(costLabel)
-        costLabel.snp.makeConstraints { make in
+        priceLabel.font = .heading_h1
+        priceLabel.textColor = .text_5
+        priceView.addSubview(priceLabel)
+        priceLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()
-            make.leading.equalTo(coin.snp.trailing).offset(6)
+            make.leading.equalTo(coin.snp.trailing)
+        }
+        
+        unitLabel.font = .body_m
+        unitLabel.textColor = .text_5
+        priceView.addSubview(unitLabel)
+        unitLabel.snp.makeConstraints { make in
+            make.leading.equalTo(priceLabel.snp.trailing)
+            make.centerY.equalToSuperview()
             make.trailing.equalToSuperview()
         }
         

+ 4 - 4
Lanu/Views/Order/OrderRecords/LNOrderRecordCell.swift

@@ -211,13 +211,13 @@ extension LNOrderRecordCell {
         container.addSubview(price)
         price.snp.makeConstraints { make in
             make.centerY.equalToSuperview()
-            make.trailing.equalToSuperview().offset(-12)
+            make.trailing.equalToSuperview()
         }
         
         let infoView = buildGameInfoView()
         container.addSubview(infoView)
         infoView.snp.makeConstraints { make in
-            make.leading.equalToSuperview().offset(12)
+            make.leading.equalToSuperview()
             make.centerY.equalToSuperview().inset(4)
             make.trailing.lessThanOrEqualTo(price.snp.leading).offset(-12)
         }
@@ -268,7 +268,7 @@ extension LNOrderRecordCell {
             make.width.height.equalTo(14)
         }
         
-        requestLine.backgroundColor = .fill_2
+        requestLine.backgroundColor = .fill_3
         requestLine.snp.makeConstraints { make in
             make.height.equalTo(0.5)
         }
@@ -445,7 +445,7 @@ extension LNOrderRecordCell {
         gameIc.snp.makeConstraints { make in
             make.leading.equalToSuperview()
             make.verticalEdges.equalToSuperview()
-            make.width.height.equalTo(40)
+            make.width.height.equalTo(50)
         }
         
         let textInfo = UIView()

+ 2 - 0
Lanu/Views/Profile/Edit/LNEditGenderPanel.swift

@@ -89,6 +89,7 @@ extension LNEditGenderPanel {
         maleTitleLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()
             make.trailing.equalToSuperview()
+            make.leading.equalTo(maleIc.snp.trailing).offset(6)
         }
         
         femaleButton.layer.cornerRadius = 23
@@ -126,6 +127,7 @@ extension LNEditGenderPanel {
         femaleTitleLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()
             make.trailing.equalToSuperview()
+            make.leading.equalTo(femaleIc.snp.trailing).offset(6)
         }
         
         confirmButton.setTitle(.init(key: "A00185"), for: .normal)

+ 23 - 60
Lanu/Views/Profile/Edit/LNEditProfilePhotoWallView.swift

@@ -85,77 +85,40 @@ extension LNEditProfilePhotoWallView {
             make.top.equalTo(photoWallCountLabel.snp.bottom).offset(4)
         }
         
-        let space = 7
-        
-        let line1 = UIStackView()
-        line1.axis = .horizontal
-        line1.distribution = .equalSpacing
-        for text in [
-            String.init(key: "A00192"),
-            .init(key: "A00193"),
-            .init(key: "A00193")
-        ] {
-            let view = LNEditProfileUploadImageView()
-            view.update(text)
-            view.delegate = self
-            view.onTap { [weak self] in
-                guard let self else { return }
-                handlePhotoClick()
-            }
-            line1.addArrangedSubview(view)
-            photoWall.append(view)
-        }
-        addSubview(line1)
-        line1.snp.makeConstraints { make in
+        let stackView = UIStackView()
+        stackView.axis = .vertical
+        stackView.spacing = 7
+        addSubview(stackView)
+        stackView.snp.makeConstraints { make in
             make.horizontalEdges.equalToSuperview()
             make.top.equalTo(descLabel.snp.bottom).offset(10)
+            make.bottom.equalToSuperview()
         }
-        let line2 = UIStackView()
-        line2.axis = .horizontal
-        line2.distribution = .equalSpacing
-        for text in [
-            String.init(key: "A00194"),
-            .init(key: "A00195"),
-            .init(key: "A00196")
-        ] {
+        
+        stackView.addArrangedSubview(buildPhotoLine())
+        stackView.addArrangedSubview(buildPhotoLine())
+        stackView.addArrangedSubview(buildPhotoLine())
+    }
+    
+    private func buildPhotoLine() -> UIView {
+        let stackView = UIStackView()
+        stackView.axis = .horizontal
+        stackView.distribution = .fillEqually
+        stackView.spacing = 7
+        for _ in 0..<3 {
             let view = LNEditProfileUploadImageView()
-            view.update(text)
             view.delegate = self
             view.onTap { [weak self] in
                 guard let self else { return }
                 handlePhotoClick()
             }
-            line2.addArrangedSubview(view)
-            photoWall.append(view)
-        }
-        addSubview(line2)
-        line2.snp.makeConstraints { make in
-            make.horizontalEdges.equalToSuperview()
-            make.top.equalTo(line1.snp.bottom).offset(space)
-        }
-        let line3 = UIStackView()
-        line3.axis = .horizontal
-        line3.distribution = .equalSpacing
-        for text in [
-            String.init(key: "A00197"),
-            .init(key: "A00198"),
-            .init(key: "A00199")
-        ] {
-            let view = LNEditProfileUploadImageView()
-            view.update(text)
-            view.delegate = self
-            view.onTap { [weak self] in
-                guard let self else { return }
-                handlePhotoClick()
+            stackView.addArrangedSubview(view)
+            view.snp.makeConstraints { make in
+                make.height.equalTo(view.snp.width)
             }
-            line3.addArrangedSubview(view)
             photoWall.append(view)
         }
-        addSubview(line3)
-        line3.snp.makeConstraints { make in
-            make.horizontalEdges.equalToSuperview()
-            make.top.equalTo(line2.snp.bottom).offset(space)
-            make.bottom.equalToSuperview()
-        }
+        
+        return stackView
     }
 }

+ 3 - 32
Lanu/Views/Profile/Edit/LNEditProfileUploadImageView.swift

@@ -11,10 +11,7 @@ import SnapKit
 
 
 class LNEditProfileUploadImageView: LNUploadImageView {
-    static let size: CGFloat = 110
-    
-    private let defaultView = UIView()
-    private let descLabel = UILabel()
+    private let defaultView = UIImageView()
     
     override var image: UIImage? {
         didSet {
@@ -28,10 +25,6 @@ class LNEditProfileUploadImageView: LNUploadImageView {
         setupViews()
     }
     
-    func update(_ text: String) {
-        descLabel.text = text
-    }
-    
     required init?(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
@@ -44,39 +37,17 @@ extension LNEditProfileUploadImageView {
         clipsToBounds = true
         showClearButton = true
         
-        snp.makeConstraints { make in
-            make.width.height.equalTo(Self.size)
-        }
-        
         let defaultView = buildDefault()
         insertSubview(defaultView, at: 0)
         defaultView.snp.makeConstraints { make in
             make.center.equalToSuperview()
-            make.width.lessThanOrEqualTo(50)
         }
     }
     
     private func buildDefault() -> UIView {
         let config = UIImage.SymbolConfiguration(pointSize: 17)
-        let plus = UIImageView()
-        plus.image = .init(systemName: "plus", withConfiguration: config)
-        plus.tintColor = .text_3
-        defaultView.addSubview(plus)
-        plus.snp.makeConstraints { make in
-            make.centerX.equalToSuperview()
-            make.top.equalToSuperview()
-        }
-        
-        descLabel.font = .body_xs
-        descLabel.textColor = .text_4
-        descLabel.numberOfLines = 0
-        descLabel.textAlignment = .center
-        defaultView.addSubview(descLabel)
-        descLabel.snp.makeConstraints { make in
-            make.directionalHorizontalEdges.equalToSuperview()
-            make.bottom.equalToSuperview()
-            make.top.equalTo(plus.snp.bottom).offset(6)
-        }
+        defaultView.image = .init(systemName: "plus", withConfiguration: config)
+        defaultView.tintColor = .text_3
         
         return defaultView
     }

+ 5 - 0
Lanu/Views/Profile/Edit/LNEditProfileViewController.swift

@@ -282,10 +282,13 @@ extension LNEditProfileViewController {
             }
         }
         container.addSubview(profilePhoto)
+        let width = (view.bounds.width - 32 + 7) / 3 - 7 // 保持与图片墙宽度一致
         profilePhoto.snp.makeConstraints { make in
             make.leading.equalToSuperview()
             make.top.equalTo(titleLabel.snp.bottom).offset(10)
             make.bottom.equalToSuperview()
+            make.width.equalTo(width)
+            make.height.equalTo(profilePhoto.snp.width)
         }
         
         return container
@@ -409,6 +412,8 @@ extension LNEditProfileViewController {
         titleLabel.text = title
         titleLabel.font = .body_m
         titleLabel.textColor = .text_4
+        titleLabel.setContentHuggingPriority(.required, for: .horizontal)
+        titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
         container.addSubview(titleLabel)
         titleLabel.snp.makeConstraints { make in
             make.verticalEdges.equalToSuperview()

+ 9 - 3
Lanu/Views/Profile/Mine/LNMineFunctionView.swift

@@ -11,6 +11,7 @@ import SnapKit
 
 
 class LNMineFunctionView: UIView {
+    private let stackView = UIStackView()
     private var orderRecordView: UIView?
     
     override init(frame: CGRect) {
@@ -32,6 +33,11 @@ extension LNMineFunctionView: LNProfileManagerNotify {
     func onUserInfoChanged(userInfo: LNUserProfileVO) {
         guard userInfo.userNo.isMyUid else { return }
         orderRecordView?.isHidden = !userInfo.playmate
+        if userInfo.playmate {
+            stackView.distribution = .equalCentering
+        } else {
+            stackView.distribution = .fillEqually
+        }
     }
 }
 
@@ -50,12 +56,11 @@ extension LNMineFunctionView {
             make.top.equalToSuperview().offset(12)
         }
         
-        let stackView = UIStackView()
         stackView.axis = .horizontal
-        stackView.distribution = .fillEqually
+        stackView.distribution = .equalCentering
         addSubview(stackView)
         stackView.snp.makeConstraints { make in
-            make.directionalHorizontalEdges.equalToSuperview()
+            make.directionalHorizontalEdges.equalToSuperview().inset(16)
             make.top.equalTo(titleLabel.snp.bottom).offset(22)
             make.bottom.equalToSuperview().offset(-16)
         }
@@ -107,6 +112,7 @@ extension LNMineFunctionView {
         titleLabel.font = .body_s
         titleLabel.textColor = .text_4
         titleLabel.textAlignment = .center
+        titleLabel.numberOfLines = 0
         container.addSubview(titleLabel)
         titleLabel.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview()

+ 1 - 1
Lanu/Views/Profile/Post/LNPostShareImageGenerator.swift

@@ -42,7 +42,7 @@ class LNPostShareImageGenerator {
     }
     
     func setOrderInfo(desc: String?) {
-        newsLabel.text = desc?.isEmpty != false ? desc : .init(key: "A00218")
+        newsLabel.text = desc?.isEmpty == false ? desc : .init(key: "A00218")
     }
     
     func setSkills(skills: [LNGameMateSkillVO]) {

+ 1 - 0
Lanu/Views/Profile/Post/LNPostShareViewController.swift

@@ -31,6 +31,7 @@ class LNPostShareViewController: LNViewController {
         
         setupViews()
         prefetchSkillIcon()
+        loadLastOrder()
         
         selectedSkills = Array(myGameMateInfo?.skills.prefix(3) ?? [])
         updateSkillsView()

+ 4 - 4
Lanu/Views/Profile/Profile/LNProfileScoreFloatingView.swift

@@ -81,7 +81,7 @@ extension LNProfileScoreFloatingView {
         ic.snp.makeConstraints { make in
             make.centerX.equalToSuperview()
             make.top.equalToSuperview()
-            make.leading.lessThanOrEqualToSuperview()
+            make.leading.greaterThanOrEqualToSuperview()
         }
         
         background.image = .primary_7
@@ -91,19 +91,19 @@ extension LNProfileScoreFloatingView {
         background.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview()
             make.bottom.equalToSuperview()
-            make.bottom.equalTo(ic.snp.bottom).offset(8)
+            make.top.equalTo(ic.snp.bottom).offset(-12)
             make.width.lessThanOrEqualTo(74)
         }
         
         let label = UILabel()
         label.text = .init(key: "A00227")
-        label.font = .heading_h5
+        label.font = .init(name: UIFont.boldFontName, size: 11)
         label.textColor = .text_1
         label.textAlignment = .center
         label.numberOfLines = 0
         background.addSubview(label)
         label.snp.makeConstraints { make in
-            make.verticalEdges.equalToSuperview().inset(4)
+            make.verticalEdges.equalToSuperview().inset(2)
             make.directionalHorizontalEdges.equalToSuperview().inset(6)
         }
     }

+ 3 - 7
Lanu/Views/Profile/Profile/LNProfileSkillListView.swift

@@ -73,12 +73,7 @@ private class LNProfileSkillListItemView: UIView {
         bg.sd_setImage(with: URL(string: skill.cover))
         icon.sd_setImage(with: URL(string: skill.icon))
         nameLabel.text = skill.name
-        
-        let text: String = .init(key: "A00040", skill.price.toDisplay, skill.unit)
-        let attrStr = NSMutableAttributedString(string: text)
-        let range = (text as NSString).range(of: "\(skill.price.toDisplay)")
-        attrStr.addAttribute(.font, value: UIFont.heading_h4, range: range)
-        priceLabel.attributedText = attrStr
+        priceLabel.text = .init(key: "A00040", skill.price.toDisplay, skill.unit)
     }
     
     override init(frame: CGRect) {
@@ -143,9 +138,10 @@ extension LNProfileSkillListItemView {
         coin.snp.makeConstraints { make in
             make.leading.bottom.equalToSuperview()
             make.top.equalTo(nameLabel.snp.bottom).offset(4)
+            make.width.height.equalTo(16)
         }
         
-        priceLabel.font = .body_s
+        priceLabel.font = .heading_h5
         priceLabel.textColor = .text_1
         priceLabel.baselineAdjustment = .alignCenters
         textView.addSubview(priceLabel)

+ 1 - 0
Lanu/Views/Profile/Profile/LNProfileUserDetailView.swift

@@ -40,6 +40,7 @@ class LNProfileUserDetailView: UIView {
     
     func update(_ detail: LNUserProfileVO) {
         languageLabel.text = detail.languageNames.joined(separator: ",")
+        languageLabel.superview?.isHidden = detail.languageNames.isEmpty
         
         descLabel.superview?.isHidden = detail.intro.isEmpty
         descLabel.text = detail.intro

+ 21 - 2
Lanu/Views/Profile/Profile/LNProfileUserInfoView.swift

@@ -14,6 +14,7 @@ class LNProfileUserInfoView: UIView {
     private let userNameLabel = UILabel()
     private let genderView = LNGenderView()
     
+    private let locationLabel = UILabel()
     private let idLabel = UILabel()
     private let followCountLabel = UILabel()
     
@@ -30,7 +31,7 @@ class LNProfileUserInfoView: UIView {
     func update(_ detail: LNUserProfileVO) {
         userNameLabel.text = detail.nickname
         genderView.update(detail.gender, detail.age)
-        
+        locationLabel.text = "\(detail.distance)km"
         idLabel.text = "ID \(detail.userNo)"
         followCountLabel.text = .init(key: "A00234", detail.fansCount)
         
@@ -85,11 +86,29 @@ extension LNProfileUserInfoView {
         let idView = UIView()
         userView.addArrangedSubview(idView)
         
+        let locationIc = UIImageView()
+        locationIc.image = .init(named: "ic_location")?.withRenderingMode(.alwaysTemplate)
+        locationIc.tintColor = .text_2
+        idView.addSubview(locationIc)
+        locationIc.snp.makeConstraints { make in
+            make.leading.equalToSuperview()
+            make.centerY.equalToSuperview()
+            make.width.height.equalTo(16)
+        }
+        
+        locationLabel.font = .body_s
+        locationLabel.textColor = .text_2
+        idView.addSubview(locationLabel)
+        locationLabel.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.leading.equalTo(locationIc.snp.trailing).offset(4)
+        }
+        
         idLabel.font = .body_s
         idLabel.textColor = .text_2
         idView.addSubview(idLabel)
         idLabel.snp.makeConstraints { make in
-            make.leading.equalToSuperview()
+            make.leading.equalTo(locationLabel.snp.trailing).offset(13)
             make.directionalVerticalEdges.equalToSuperview()
         }
         

+ 1 - 0
Lanu/Views/Search/LNUserSearchViewController.swift

@@ -249,6 +249,7 @@ extension LNUserSearchViewController {
         searchInput.returnKeyType = .search
         searchInput.enablesReturnKeyAutomatically = true
         searchInput.delegate = self
+        searchInput.resignFirstResponder()
         searchInput.addAction(UIAction(handler: { [weak self] _ in
             guard let self else { return }
             if searchInput.text?.isEmpty != false {

+ 4 - 4
Lanu/Views/Settings/LNAboutViewController.swift

@@ -101,12 +101,12 @@ extension LNAboutViewController {
         let newVersion = LNNewVersionView()
         newVersion.isHidden = !LNAppConfig.shared.hasNewVersion
         let version = buildFunctionItem(title: .init(key: "A00252"), infoView: newVersion)
-        if LNAppConfig.shared.hasNewVersion {
-            version.onTap {
+        version.onTap {
+            if LNAppConfig.shared.hasNewVersion {
                 LNAppConfig.shared.jumpToAppStore()
+            } else {
+                showToast(.init(key: "A00253"))
             }
-        } else {
-            showToast(.init(key: "A00253"))
         }
         stackView.addArrangedSubview(version)
     }

+ 4 - 3
Lanu/Views/Settings/LNSettingsViewController.swift

@@ -50,7 +50,7 @@ extension LNSettingsViewController {
             make.directionalEdges.equalToSuperview().inset(16)
         }
         
-        let language = buildFunctionItem(icName: "ic_language", title: .init(key: "A00256"), infoView: curLanguageLabel)
+        let language = buildFunctionItem(icName: "ic_language_18", title: .init(key: "A00256"), infoView: curLanguageLabel)
         curLanguageLabel.font = .body_s
         curLanguageLabel.textColor = .text_5
         curLanguageLabel.text = LNAppConfig.shared.curLang.text
@@ -105,11 +105,12 @@ extension LNSettingsViewController {
         
         let ic = UIImageView()
         ic.image = .init(named: icName)?.withRenderingMode(.alwaysTemplate)
-        ic.tintColor = .init(hex: "#17171A")
+        ic.tintColor = .text_4
         container.addSubview(ic)
         ic.snp.makeConstraints { make in
             make.leading.equalToSuperview()
             make.centerY.equalToSuperview()
+            make.width.height.equalTo(18)
         }
         
         let arrow = UIImageView.arrowImageView(size: 14)
@@ -129,7 +130,7 @@ extension LNSettingsViewController {
         }
         
         let titleLabel = UILabel()
-        titleLabel.font = .body_s
+        titleLabel.font = .body_m
         titleLabel.textColor = .text_5
         titleLabel.text = title
         container.addSubview(titleLabel)

+ 8 - 5
Lanu/Views/Wallet/Coin/LNCoinViewController.swift

@@ -134,7 +134,9 @@ extension LNCoinViewController {
         bg.addSubview(jumpButton)
         jumpButton.snp.makeConstraints { make in
             make.top.equalToSuperview()
-            make.trailing.equalToSuperview().offset(-3)
+            make.trailing.equalToSuperview()
+            make.width.equalTo(189)
+            make.height.equalTo(30)
         }
         
         let contentView = UIView()
@@ -142,25 +144,26 @@ extension LNCoinViewController {
         jumpButton.addSubview(contentView)
         contentView.snp.makeConstraints { make in
             make.center.equalToSuperview()
-            make.leading.greaterThanOrEqualToSuperview().offset(8)
         }
         
         let jumpTitleLabel = UILabel()
         jumpTitleLabel.text = .init(key: "A00264")
         jumpTitleLabel.font = .heading_h4
         jumpTitleLabel.textColor = .text_1
+        jumpTitleLabel.textAlignment = .center
         contentView.addSubview(jumpTitleLabel)
         jumpTitleLabel.snp.makeConstraints { make in
-            make.leading.verticalEdges.equalToSuperview()
+            make.leading.equalToSuperview()
+            make.verticalEdges.equalToSuperview()
         }
         
-        let arrow = UIImageView.arrowImageView(size: 16)
+        let arrow = UIImageView.arrowImageView(size: 14)
         arrow.tintColor = .white
         contentView.addSubview(arrow)
         arrow.snp.makeConstraints { make in
-            make.leading.equalTo(jumpTitleLabel.snp.trailing).offset(2)
             make.centerY.equalToSuperview()
             make.trailing.equalToSuperview()
+            make.leading.equalTo(jumpTitleLabel.snp.trailing).offset(4)
         }
         
         return bg

+ 8 - 5
Lanu/Views/Wallet/Diamond/LNDiamondViewController.swift

@@ -134,7 +134,9 @@ extension LNDiamondViewController {
         bg.addSubview(jumpButton)
         jumpButton.snp.makeConstraints { make in
             make.top.equalToSuperview()
-            make.trailing.equalToSuperview().offset(-3)
+            make.trailing.equalToSuperview()
+            make.width.equalTo(165)
+            make.height.equalTo(30)
         }
         
         let contentView = UIView()
@@ -142,25 +144,26 @@ extension LNDiamondViewController {
         jumpButton.addSubview(contentView)
         contentView.snp.makeConstraints { make in
             make.center.equalToSuperview()
-            make.leading.greaterThanOrEqualToSuperview().offset(8)
         }
         
         let jumpTitleLabel = UILabel()
         jumpTitleLabel.text = .init(key: "A00273")
         jumpTitleLabel.font = .heading_h4
         jumpTitleLabel.textColor = .text_1
+        jumpTitleLabel.textAlignment = .center
         contentView.addSubview(jumpTitleLabel)
         jumpTitleLabel.snp.makeConstraints { make in
-            make.leading.verticalEdges.equalToSuperview()
+            make.leading.equalToSuperview()
+            make.verticalEdges.equalToSuperview()
         }
         
-        let arrow = UIImageView.arrowImageView(size: 16)
+        let arrow = UIImageView.arrowImageView(size: 14)
         arrow.tintColor = .white
         contentView.addSubview(arrow)
         arrow.snp.makeConstraints { make in
-            make.leading.equalTo(jumpTitleLabel.snp.trailing).offset(2)
             make.centerY.equalToSuperview()
             make.trailing.equalToSuperview()
+            make.leading.equalTo(jumpTitleLabel.snp.trailing).offset(4)
         }
         
         return bg

+ 5 - 4
Lanu/Views/Wallet/LNExchangePanel.swift

@@ -89,9 +89,9 @@ extension LNExchangePanel {
             make.top.equalToSuperview()
         }
         
-        let diamondView = buildRemain()
-        container.addSubview(diamondView)
-        diamondView.snp.makeConstraints { make in
+        let remainView = buildRemain()
+        container.addSubview(remainView)
+        remainView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview()
             make.top.equalTo(titleView.snp.bottom).offset(4)
         }
@@ -102,7 +102,7 @@ extension LNExchangePanel {
         container.addSubview(exchangeView)
         exchangeView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(16)
-            make.top.equalTo(diamondView.snp.bottom).offset(4)
+            make.top.equalTo(remainView.snp.bottom).offset(4)
         }
         
         let diamondExchange = buildExchangeView()
@@ -118,6 +118,7 @@ extension LNExchangePanel {
         line.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(10)
             make.top.equalTo(diamondExchange.snp.bottom).offset(4)
+            make.height.equalTo(1)
         }
         
         let coinView = buildExpectView()

+ 4 - 4
Lanu/Views/Wallet/LNWalletViewController.swift

@@ -162,7 +162,7 @@ extension LNWalletViewController {
         container.addSubview(coinView)
         coinView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(16)
-            make.top.equalTo(titleLabel.snp.bottom)
+            make.top.equalTo(titleView.snp.bottom)
             make.height.equalTo(64)
         }
         
@@ -187,7 +187,7 @@ extension LNWalletViewController {
             view.pushToCoinView()
         }
         
-        let coin = UIImageView.coinImageView()
+        let coin = UIImageView.coinImageView(true)
         container.addSubview(coin)
         coin.snp.makeConstraints { make in
             make.leading.equalToSuperview().offset(14)
@@ -274,7 +274,7 @@ extension LNWalletViewController {
             view.pushToDiamondView()
         }
         
-        let diamond = UIImageView.diamondImageView()
+        let diamond = UIImageView.diamondImageView(true)
         container.addSubview(diamond)
         diamond.snp.makeConstraints { make in
             make.leading.equalToSuperview().offset(14)
@@ -379,7 +379,7 @@ extension LNWalletViewController {
         container.addSubview(beanView)
         beanView.snp.makeConstraints { make in
             make.directionalHorizontalEdges.equalToSuperview().inset(16)
-            make.top.equalTo(titleLabel.snp.bottom)
+            make.top.equalTo(titleView.snp.bottom)
             make.height.equalTo(64)
             make.bottom.equalToSuperview().offset(-10)
         }

+ 1 - 1
Podfile

@@ -3,7 +3,7 @@ platform :ios, '15.0'
 
 source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
 
-target 'Lanu' do
+target 'Gami' do
   # Comment the next line if you don't want to use dynamic frameworks
   use_frameworks!
 

+ 1 - 1
Podfile.lock

@@ -58,6 +58,6 @@ SPEC CHECKSUMS:
   TUIChat: 696bca6e2a6cfd2bc22f624425b425b68bd9506c
   TXIMSDK_Plus_iOS_XCFramework: 3b435eae84c639f35ae8dc9c8b92c399a8b0a67f
 
-PODFILE CHECKSUM: 868153081aaa382b55c91fb34418d216583530a5
+PODFILE CHECKSUM: 1a199606bd3b6c3dfb1395b7670112711dc34a86
 
 COCOAPODS: 1.16.2