This commit is contained in:
2025-12-11 13:40:32 +08:00
parent d348b35984
commit 45695364e9
11 changed files with 123 additions and 29 deletions

View File

@@ -48,6 +48,7 @@
/// Theme shop /// Theme shop
#define API_THEME_LIST_ALL_STYLES @"/themes/listAllStyles" // 查询所有主题风格 #define API_THEME_LIST_ALL_STYLES @"/themes/listAllStyles" // 查询所有主题风格
#define API_THEME_LIST_BY_STYLE @"/themes/listByStyle" // 按风格查询主题列表 #define API_THEME_LIST_BY_STYLE @"/themes/listByStyle" // 按风格查询主题列表
#define API_THEME_PURCHASED @"/themes/purchased" // 查询已购买主题
/// pay /// pay
#define API_VALIDATE_RECEIPT @"/api/apple/validate-receipt" // 排行榜标签列表 #define API_VALIDATE_RECEIPT @"/api/apple/validate-receipt" // 排行榜标签列表

View File

@@ -195,6 +195,7 @@
"Pay clicked" = "Pay clicked"; "Pay clicked" = "Pay clicked";
"Points Recharge" = "Points Recharge"; "Points Recharge" = "Points Recharge";
"My Points" = "My Points"; "My Points" = "My Points";
"No data" = "No data";
// Example categories/items // Example categories/items
"能力" = "Ability"; "能力" = "Ability";
@@ -250,7 +251,6 @@
// Misc // Misc
"测试" = "Test"; "测试" = "Test";
"暂无数据" = "No data yet";
"这里是设置内容占位" = "Settings content placeholder"; "这里是设置内容占位" = "Settings content placeholder";
"设置" = "Settings"; "设置" = "Settings";
"使用引导" = "Usage Guide"; "使用引导" = "Usage Guide";

View File

@@ -193,6 +193,7 @@
"Pay clicked" = "点击支付"; "Pay clicked" = "点击支付";
"Points Recharge" = "积分充值"; "Points Recharge" = "积分充值";
"My Points" = "我的积分"; "My Points" = "我的积分";
"No data" = "暂无数据";
// 示例商品/分类 // 示例商品/分类
"能力" = "能力"; "能力" = "能力";

View File

@@ -115,6 +115,8 @@
0498BD8C2EE69E15006CC1D5 /* KBTagItemModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */; }; 0498BD8C2EE69E15006CC1D5 /* KBTagItemModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */; };
0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; }; 0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; };
0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; }; 0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */; };
550CB2630FA4A7B4B9782EFA /* KBMyTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */; };
471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */; };
0498BDDA2EE7ECEA006CC1D5 /* WJXEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */; }; 0498BDDA2EE7ECEA006CC1D5 /* WJXEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */; };
0498BDDE2EE81508006CC1D5 /* KBShopVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDDD2EE81508006CC1D5 /* KBShopVM.m */; }; 0498BDDE2EE81508006CC1D5 /* KBShopVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDDD2EE81508006CC1D5 /* KBShopVM.m */; };
0498BDE12EEA87C9006CC1D5 /* KBShopStyleModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDE02EEA87C8006CC1D5 /* KBShopStyleModel.m */; }; 0498BDE12EEA87C9006CC1D5 /* KBShopStyleModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BDE02EEA87C8006CC1D5 /* KBShopStyleModel.m */; };
@@ -409,6 +411,8 @@
0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBTagItemModel.m; sourceTree = "<group>"; }; 0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBTagItemModel.m; sourceTree = "<group>"; };
0498BD8D2EE6A3BD006CC1D5 /* KBMyMainModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyMainModel.h; sourceTree = "<group>"; }; 0498BD8D2EE6A3BD006CC1D5 /* KBMyMainModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyMainModel.h; sourceTree = "<group>"; };
0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyMainModel.m; sourceTree = "<group>"; }; 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyMainModel.m; sourceTree = "<group>"; };
7ECBD0E320F971D0FBEDD7BC /* KBMyTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBMyTheme.h; sourceTree = "<group>"; };
180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyTheme.m; sourceTree = "<group>"; };
0498BDD72EE7ECEA006CC1D5 /* WJXEventSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WJXEventSource.h; sourceTree = "<group>"; }; 0498BDD72EE7ECEA006CC1D5 /* WJXEventSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WJXEventSource.h; sourceTree = "<group>"; };
0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WJXEventSource.m; sourceTree = "<group>"; }; 0498BDD82EE7ECEA006CC1D5 /* WJXEventSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WJXEventSource.m; sourceTree = "<group>"; };
0498BDDC2EE81508006CC1D5 /* KBShopVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopVM.h; sourceTree = "<group>"; }; 0498BDDC2EE81508006CC1D5 /* KBShopVM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopVM.h; sourceTree = "<group>"; };
@@ -1175,6 +1179,8 @@
0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */, 0498BD8A2EE69E15006CC1D5 /* KBTagItemModel.m */,
0498BD8D2EE6A3BD006CC1D5 /* KBMyMainModel.h */, 0498BD8D2EE6A3BD006CC1D5 /* KBMyMainModel.h */,
0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */, 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */,
7ECBD0E320F971D0FBEDD7BC /* KBMyTheme.h */,
180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */,
); );
path = M; path = M;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1752,6 +1758,7 @@
04FC95672EB0546C007BD342 /* KBKey.m in Sources */, 04FC95672EB0546C007BD342 /* KBKey.m in Sources */,
A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */, A1B2C3F42EB35A9900000001 /* KBFullAccessGuideView.m in Sources */,
0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */, 0498BD8F2EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */,
550CB2630FA4A7B4B9782EFA /* KBMyTheme.m in Sources */,
0498BDDA2EE7ECEA006CC1D5 /* WJXEventSource.m in Sources */, 0498BDDA2EE7ECEA006CC1D5 /* WJXEventSource.m in Sources */,
04D1F6B22EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */, 04D1F6B22EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */,
A1B2C4002EB4A0A100000003 /* KBAuthManager.m in Sources */, A1B2C4002EB4A0A100000003 /* KBAuthManager.m in Sources */,
@@ -1796,6 +1803,7 @@
04A9FE1B2EB892460020DB6D /* KBLocalizationManager.m in Sources */, 04A9FE1B2EB892460020DB6D /* KBLocalizationManager.m in Sources */,
048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */, 048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */,
0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */, 0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */,
471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */,
04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */, 04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */,
0498BD712EE02A41006CC1D5 /* KBForgetPwdNewPwdVC.m in Sources */, 0498BD712EE02A41006CC1D5 /* KBForgetPwdNewPwdVC.m in Sources */,
048908EF2EBF861800FABA60 /* KBSkinSectionTitleCell.m in Sources */, 048908EF2EBF861800FABA60 /* KBSkinSectionTitleCell.m in Sources */,

View File

@@ -53,7 +53,7 @@
_useEmptyDataSet = YES; // _useEmptyDataSet = YES; //
_emptyShouldAllowScroll = YES; // _emptyShouldAllowScroll = YES; //
_emptyVerticalOffset = 0; // _emptyVerticalOffset = 0; //
_emptyTitleText = KBLocalized(@"暂无数据"); // _emptyTitleText = KBLocalized(@"No data"); //
#if KB_HAS_DZN #if KB_HAS_DZN
self.emptyDataSetSource = self; self.emptyDataSetSource = self;
@@ -148,7 +148,7 @@
self.useEmptyDataSet = NO; self.useEmptyDataSet = NO;
// //
NSString *t = title ?: KBLocalized(@"暂无数据"); NSString *t = title ?: KBLocalized(@"No data");
LYEmptyView *ev = nil; LYEmptyView *ev = nil;
if (buttonTitle.length > 0) { if (buttonTitle.length > 0) {

View File

@@ -22,7 +22,7 @@
tapHandler:(KBEmptyAction)tapHandler tapHandler:(KBEmptyAction)tapHandler
buttonHandler:(KBEmptyAction)buttonHandler { buttonHandler:(KBEmptyAction)buttonHandler {
#if KB_HAS_LY #if KB_HAS_LY
NSString *t = title ?: KBLocalized(@"暂无数据"); NSString *t = title ?: KBLocalized(@"No data");
LYEmptyView *ev = nil; LYEmptyView *ev = nil;
if (buttonTitle.length > 0) { if (buttonTitle.length > 0) {

View File

@@ -0,0 +1,25 @@
//
// KBMyTheme.h
// keyBoard
//
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
NS_ASSUME_NONNULL_BEGIN
/// “我的皮肤”页已购主题模型
@interface KBMyTheme : NSObject
@property (nonatomic, copy, nullable) NSString *themeId;
@property (nonatomic, copy, nullable) NSString *themeName;
@property (nonatomic, assign) CGFloat themePrice;
@property (nonatomic, copy, nullable) NSArray<NSString *> *themeTag;
@property (nonatomic, copy, nullable) NSString *themeDownload;
@property (nonatomic, assign) NSInteger themeStyle;
@property (nonatomic, assign) BOOL themeStatus;
@property (nonatomic, assign) NSInteger themePurchasesNumber;
@property (nonatomic, assign) NSInteger sort;
@property (nonatomic, assign) BOOL isFree;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,17 @@
//
// KBMyTheme.m
// keyBoard
//
#import "KBMyTheme.h"
#import <MJExtension/MJExtension.h>
@implementation KBMyTheme
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
return @{
@"themeId": @"id"
};
}
@end

View File

@@ -11,6 +11,9 @@
#import "UIScrollView+KBEmptyView.h" // LYEmptyView #import "UIScrollView+KBEmptyView.h" // LYEmptyView
#import "MySkinCell.h" #import "MySkinCell.h"
#import "KBMyVM.h"
#import "KBMyTheme.h"
#import "KBHUD.h"
static NSString * const kMySkinCellId = @"kMySkinCellId"; static NSString * const kMySkinCellId = @"kMySkinCellId";
@@ -20,8 +23,8 @@ static NSString * const kMySkinCellId = @"kMySkinCellId";
@property (nonatomic, strong) UILabel *selectedLabel; // @property (nonatomic, strong) UILabel *selectedLabel; //
@property (nonatomic, strong) UIButton *deleteButton; // @property (nonatomic, strong) UIButton *deleteButton; //
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *data; // @property (nonatomic, strong) NSMutableArray<KBMyTheme *> *data; //
@property (nonatomic, assign) NSInteger loadCount; // / @property (nonatomic, strong) KBMyVM *viewModel;
@property (nonatomic, assign, getter=isEditingMode) BOOL editingMode; // @property (nonatomic, assign, getter=isEditingMode) BOOL editingMode; //
@end @end
@@ -62,9 +65,9 @@ static NSString * const kMySkinCellId = @"kMySkinCellId";
// LYEmptyView + // LYEmptyView +
KBWeakSelf KBWeakSelf
[self.collectionView kb_makeDefaultEmptyViewWithImage:nil [self.collectionView kb_makeDefaultEmptyViewWithImage:nil
title:KBLocalized(@"No skins yet") title:KBLocalized(@"No data")
detail:KBLocalized(@"Pull down to refresh") detail:KBLocalized(@"Pull down to refresh")
buttonTitle:KBLocalized(@"Retry") buttonTitle:KBLocalized(@"")
tapHandler:nil tapHandler:nil
buttonHandler:^{ [weakSelf.collectionView.mj_header beginRefreshing]; }]; buttonHandler:^{ [weakSelf.collectionView.mj_header beginRefreshing]; }];
[self.collectionView kb_setLYAutoShowEnabled:NO]; // [self.collectionView kb_setLYAutoShowEnabled:NO]; //
@@ -73,7 +76,7 @@ static NSString * const kMySkinCellId = @"kMySkinCellId";
[self.collectionView kb_endLoadingForEmpty]; [self.collectionView kb_endLoadingForEmpty];
// + // +
self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(fetchData)]; self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(fetchPurchasedThemes)];
// //
[self.collectionView.mj_header beginRefreshing]; [self.collectionView.mj_header beginRefreshing];
@@ -82,25 +85,25 @@ static NSString * const kMySkinCellId = @"kMySkinCellId";
self.bottomView.hidden = YES; self.bottomView.hidden = YES;
} }
#pragma mark - Data - (void)fetchPurchasedThemes {
KBWeakSelf
- (void)fetchData { [self.viewModel fetchPurchasedThemesWithCompletion:^(NSArray<KBMyTheme *> * _Nullable themes, NSError * _Nullable error) {
// 1.0s dispatch_async(dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if ([weakSelf.collectionView.mj_header isRefreshing]) {
self.loadCount += 1; [weakSelf.collectionView.mj_header endRefreshing];
// /
[self.data removeAllObjects];
if (self.loadCount % 2 == 0) {
for (int i = 0; i < 8; i++) {
[self.data addObject:@{ @"title": @"Dopamine" }];
} }
} if (error) {
[weakSelf.collectionView kb_endLoadingForEmpty];
[self.collectionView reloadData]; return;
[self.collectionView kb_endLoadingForEmpty]; // / emptyView }
[self.collectionView.mj_header endRefreshing]; [weakSelf.data removeAllObjects];
}); if (themes.count > 0) {
[weakSelf.data addObjectsFromArray:themes];
}
[weakSelf.collectionView reloadData];
[weakSelf.collectionView kb_endLoadingForEmpty];
});
}];
} }
#pragma mark - Actions #pragma mark - Actions
@@ -190,8 +193,8 @@ static NSString * const kMySkinCellId = @"kMySkinCellId";
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MySkinCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kMySkinCellId forIndexPath:indexPath]; MySkinCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kMySkinCellId forIndexPath:indexPath];
NSDictionary *d = self.data[indexPath.item]; KBMyTheme *theme = self.data[indexPath.item];
[cell configWithTitle:d[@"title"] image:nil]; [cell configWithTitle:theme.themeName image:nil];
cell.editing = self.isEditingMode; // cell.editing = self.isEditingMode; //
// //
BOOL selected = [[collectionView indexPathsForSelectedItems] containsObject:indexPath]; BOOL selected = [[collectionView indexPathsForSelectedItems] containsObject:indexPath];
@@ -306,4 +309,11 @@ static NSString * const kMySkinCellId = @"kMySkinCellId";
return _deleteButton; return _deleteButton;
} }
- (KBMyVM *)viewModel {
if (!_viewModel) {
_viewModel = [[KBMyVM alloc] init];
}
return _viewModel;
}
@end @end

View File

@@ -7,6 +7,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "KBCharacter.h" #import "KBCharacter.h"
#import "KBMyTheme.h"
@class KBUser; @class KBUser;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@@ -22,6 +23,7 @@ typedef void(^KBUpLoadAvatarCompletion)(BOOL success, NSError * _Nullable error)
typedef void(^KBUpdateUserInfoCompletion)(BOOL success, NSError * _Nullable error); typedef void(^KBUpdateUserInfoCompletion)(BOOL success, NSError * _Nullable error);
typedef void(^KBUpdateCharacterSortCompletion)(BOOL success, NSError * _Nullable error); typedef void(^KBUpdateCharacterSortCompletion)(BOOL success, NSError * _Nullable error);
typedef void(^KBDeleteUserCharacterCompletion)(BOOL success, NSError * _Nullable error); typedef void(^KBDeleteUserCharacterCompletion)(BOOL success, NSError * _Nullable error);
typedef void(^KBMyPurchasedThemesCompletion)(NSArray<KBMyTheme *> *_Nullable themes, NSError *_Nullable error);
@interface KBMyVM : NSObject @interface KBMyVM : NSObject
@@ -30,6 +32,8 @@ typedef void(^KBDeleteUserCharacterCompletion)(BOOL success, NSError * _Nullable
/// 用户人设列表(/character/listByUser /// 用户人设列表(/character/listByUser
- (void)fetchCharacterListByUserWithCompletion:(KBCharacterListCompletion)completion; - (void)fetchCharacterListByUserWithCompletion:(KBCharacterListCompletion)completion;
/// 已购买主题列表(/themes/purchased
- (void)fetchPurchasedThemesWithCompletion:(KBMyPurchasedThemesCompletion)completion;
/// 更新用户人设排序 /// 更新用户人设排序
- (void)updateUserCharacterSortWithSortArray:(NSArray<NSNumber *> *)sortArray - (void)updateUserCharacterSortWithSortArray:(NSArray<NSNumber *> *)sortArray
completion:(KBUpdateCharacterSortCompletion)completion; completion:(KBUpdateCharacterSortCompletion)completion;

View File

@@ -86,6 +86,34 @@ NSString * const KBUserCharacterDeletedNotification = @"KBUserCharacterDeletedNo
}]; }];
} }
- (void)fetchPurchasedThemesWithCompletion:(KBMyPurchasedThemesCompletion)completion {
[[KBNetworkManager shared] GET:API_THEME_PURCHASED
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSString *msg = KBBizMessageFromJSONObject(jsonOrData) ?: error.localizedDescription ?: KBLocalized(@"Network error");
[KBHUD showInfo:msg];
if (completion) completion(nil, error);
return;
}
id dataObj = jsonOrData[KBData] ?: jsonOrData[@"data"];
if (![dataObj isKindOfClass:[NSArray class]]) {
NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain
code:KBNetworkErrorInvalidResponse
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid response")}];
[KBHUD showInfo:e.localizedDescription];
if (completion) completion(nil, e);
return;
}
NSArray<KBMyTheme *> *themes = [KBMyTheme mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
if (completion) completion(themes, nil);
}];
}
/// ///
- (void)updateUserCharacterSortWithSortArray:(NSArray<NSNumber *> *)sortArray - (void)updateUserCharacterSortWithSortArray:(NSArray<NSNumber *> *)sortArray
completion:(KBUpdateCharacterSortCompletion)completion { completion:(KBUpdateCharacterSortCompletion)completion {