2025-11-08 22:25:57 +08:00
|
|
|
|
//
|
|
|
|
|
|
// KBSkinDetailVC.m
|
|
|
|
|
|
// keyBoard
|
|
|
|
|
|
|
|
|
|
|
|
#import "KBSkinDetailVC.h"
|
|
|
|
|
|
#import <Masonry/Masonry.h>
|
|
|
|
|
|
#import "UIColor+Extension.h"
|
|
|
|
|
|
#import "UICollectionViewLeftAlignedLayout.h"
|
|
|
|
|
|
#import "KBSkinDetailHeaderCell.h"
|
|
|
|
|
|
#import "KBSkinTagsContainerCell.h"
|
2025-11-10 15:29:21 +08:00
|
|
|
|
#import "KBSkinCardCell.h"
|
2025-11-08 22:25:57 +08:00
|
|
|
|
#import "KBSkinSectionTitleCell.h"
|
2025-11-10 15:29:21 +08:00
|
|
|
|
#import "KBSkinBottomActionView.h"
|
2025-12-11 15:00:58 +08:00
|
|
|
|
#import "KBShopVM.h"
|
2025-12-11 15:19:23 +08:00
|
|
|
|
#import "KBShopThemeTagModel.h"
|
2025-12-11 17:51:00 +08:00
|
|
|
|
#import "KBShopThemeModel.h"
|
2025-12-11 16:39:22 +08:00
|
|
|
|
#import "KBHUD.h"
|
|
|
|
|
|
#import "KBSkinService.h"
|
2025-11-08 22:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
static NSString * const kHeaderCellId = @"kHeaderCellId";
|
|
|
|
|
|
static NSString * const kTagsContainerCellId = @"kTagsContainerCellId";
|
|
|
|
|
|
static NSString * const kSectionTitleCellId = @"kSectionTitleCellId";
|
|
|
|
|
|
static NSString * const kGridCellId = @"kGridCellId"; // 直接复用 KBSkinCardCell
|
|
|
|
|
|
|
|
|
|
|
|
typedef NS_ENUM(NSInteger, KBSkinDetailSection) {
|
|
|
|
|
|
KBSkinDetailSectionHeader = 0,
|
|
|
|
|
|
KBSkinDetailSectionTags,
|
|
|
|
|
|
KBSkinDetailSectionTitle,
|
|
|
|
|
|
KBSkinDetailSectionGrid,
|
|
|
|
|
|
KBSkinDetailSectionCount
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@interface KBSkinDetailVC () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
|
|
|
|
|
@property (nonatomic, strong) UICollectionView *collectionView; // 主列表
|
2025-11-10 15:29:21 +08:00
|
|
|
|
@property (nonatomic, strong) KBSkinBottomActionView *bottomBar; // 底部操作条
|
2025-11-08 22:25:57 +08:00
|
|
|
|
@property (nonatomic, copy) NSArray<NSString *> *tags; // 标签数据
|
2025-12-11 17:51:00 +08:00
|
|
|
|
@property (nonatomic, copy) NSArray<KBShopThemeModel *> *recommendedThemes; // 底部网格数据
|
2025-12-11 15:00:58 +08:00
|
|
|
|
@property (nonatomic, strong) KBShopVM *shopVM;
|
2025-12-11 15:19:23 +08:00
|
|
|
|
@property (nonatomic, strong, nullable) KBShopThemeDetailModel *detailModel;
|
2025-12-11 16:59:14 +08:00
|
|
|
|
//@property (nonatomic, assign) BOOL isProcessingAction;
|
2025-11-08 22:25:57 +08:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation KBSkinDetailVC
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
|
|
|
|
[super viewDidLoad];
|
|
|
|
|
|
self.view.backgroundColor = [UIColor whiteColor];
|
|
|
|
|
|
|
2025-12-11 15:19:23 +08:00
|
|
|
|
self.tags = @[];
|
2025-12-11 17:51:00 +08:00
|
|
|
|
self.recommendedThemes = @[];
|
2025-11-08 22:25:57 +08:00
|
|
|
|
|
2025-11-10 15:29:21 +08:00
|
|
|
|
// 1. 列表
|
2025-11-08 22:25:57 +08:00
|
|
|
|
[self.view addSubview:self.collectionView];
|
2025-11-10 15:29:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 2. 底部操作条(左右 15,高 45,可点击)
|
|
|
|
|
|
[self.view addSubview:self.bottomBar];
|
|
|
|
|
|
[self.bottomBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.left.equalTo(self.view).offset(15);
|
|
|
|
|
|
make.right.equalTo(self.view).offset(-15);
|
|
|
|
|
|
make.height.mas_equalTo([KBSkinBottomActionView preferredHeight]);
|
2025-11-17 20:07:39 +08:00
|
|
|
|
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(-10);
|
|
|
|
|
|
|
2025-11-10 15:29:21 +08:00
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
// 列表底部距离操作条顶部 10
|
2025-11-08 22:25:57 +08:00
|
|
|
|
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
|
2025-11-17 14:53:23 +08:00
|
|
|
|
make.left.right.equalTo(self.view);
|
|
|
|
|
|
make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT);
|
2025-11-10 15:29:21 +08:00
|
|
|
|
make.bottom.equalTo(self.bottomBar.mas_top).offset(-10);
|
2025-11-08 22:25:57 +08:00
|
|
|
|
}];
|
2025-12-11 15:00:58 +08:00
|
|
|
|
|
2025-12-11 16:39:22 +08:00
|
|
|
|
[self updateBottomBarAppearance];
|
2025-12-11 15:00:58 +08:00
|
|
|
|
[self fetchThemeDetailIfNeeded];
|
2025-12-11 17:51:00 +08:00
|
|
|
|
[self fetchRecommendedThemes];
|
2025-11-08 22:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - UICollectionView DataSource
|
|
|
|
|
|
|
|
|
|
|
|
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
|
|
|
|
|
return KBSkinDetailSectionCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
|
|
|
|
|
switch (section) {
|
|
|
|
|
|
case KBSkinDetailSectionHeader: return 1; // 顶部大卡片
|
2025-12-11 15:19:23 +08:00
|
|
|
|
case KBSkinDetailSectionTags: return self.tags.count > 0 ? 1 : 0;
|
2025-11-08 22:25:57 +08:00
|
|
|
|
case KBSkinDetailSectionTitle: return 1; // 标题
|
2025-12-11 17:51:00 +08:00
|
|
|
|
case KBSkinDetailSectionGrid: return self.recommendedThemes.count; // 2 列网格
|
2025-11-08 22:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
|
|
switch (indexPath.section) {
|
|
|
|
|
|
case KBSkinDetailSectionHeader: {
|
|
|
|
|
|
KBSkinDetailHeaderCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kHeaderCellId forIndexPath:indexPath];
|
2025-12-11 15:39:33 +08:00
|
|
|
|
[cell configWithDetail:self.detailModel];
|
2025-11-08 22:25:57 +08:00
|
|
|
|
return cell;
|
|
|
|
|
|
}
|
|
|
|
|
|
case KBSkinDetailSectionTags: {
|
|
|
|
|
|
KBSkinTagsContainerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kTagsContainerCellId forIndexPath:indexPath];
|
|
|
|
|
|
[cell configWithTags:self.tags];
|
|
|
|
|
|
return cell;
|
|
|
|
|
|
}
|
|
|
|
|
|
case KBSkinDetailSectionTitle: {
|
|
|
|
|
|
KBSkinSectionTitleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kSectionTitleCellId forIndexPath:indexPath];
|
|
|
|
|
|
[cell config:@"Recommended Skin"];
|
|
|
|
|
|
return cell;
|
|
|
|
|
|
}
|
|
|
|
|
|
case KBSkinDetailSectionGrid: {
|
|
|
|
|
|
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kGridCellId forIndexPath:indexPath];
|
2025-12-11 17:51:00 +08:00
|
|
|
|
KBShopThemeModel *model = self.recommendedThemes[indexPath.item];
|
|
|
|
|
|
NSString *priceText = [self priceTextForTheme:model];
|
|
|
|
|
|
[cell configWithTitle:model.themeName ?: @""
|
|
|
|
|
|
imageURL:model.themePreviewImageUrl
|
|
|
|
|
|
price:priceText];
|
2025-11-08 22:25:57 +08:00
|
|
|
|
return cell;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return [UICollectionViewCell new];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-17 20:43:16 +08:00
|
|
|
|
#pragma mark - UICollectionView Delegate
|
|
|
|
|
|
|
|
|
|
|
|
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
|
|
if (indexPath.section != KBSkinDetailSectionGrid) { return; }
|
|
|
|
|
|
if (indexPath.item >= self.recommendedThemes.count) { return; }
|
|
|
|
|
|
KBShopThemeModel *model = self.recommendedThemes[indexPath.item];
|
|
|
|
|
|
NSString *nextThemeId = [model.themeId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
|
|
|
if (nextThemeId.length == 0) { return; }
|
|
|
|
|
|
if ([nextThemeId isEqualToString:self.themeId ?: @""]) { return; }
|
|
|
|
|
|
|
|
|
|
|
|
// 不跳转新页面:直接切换 themeId,重新请求详情并刷新当前页面
|
|
|
|
|
|
self.themeId = nextThemeId;
|
|
|
|
|
|
self.detailModel = nil;
|
|
|
|
|
|
self.tags = @[];
|
|
|
|
|
|
[self updateBottomBarAppearance];
|
|
|
|
|
|
[self.collectionView setContentOffset:CGPointZero animated:NO];
|
|
|
|
|
|
[self.collectionView reloadData];
|
|
|
|
|
|
[self fetchThemeDetailIfNeeded];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-08 22:25:57 +08:00
|
|
|
|
#pragma mark - UICollectionView DelegateFlowLayout
|
|
|
|
|
|
|
|
|
|
|
|
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
|
|
CGFloat W = collectionView.bounds.size.width;
|
|
|
|
|
|
CGFloat insetLR = 16.0;
|
|
|
|
|
|
CGFloat contentW = W - insetLR * 2;
|
|
|
|
|
|
switch (indexPath.section) {
|
|
|
|
|
|
case KBSkinDetailSectionHeader: {
|
|
|
|
|
|
// 高度 = 图片 0.58W + 文案与上下留白(≈56)
|
2025-11-17 15:39:03 +08:00
|
|
|
|
CGFloat h = KBFit(264) + 30;
|
2025-11-08 22:25:57 +08:00
|
|
|
|
return CGSizeMake(contentW, h);
|
|
|
|
|
|
}
|
|
|
|
|
|
case KBSkinDetailSectionTags: {
|
|
|
|
|
|
CGFloat h = [KBSkinTagsContainerCell heightForTags:self.tags width:W];
|
2025-12-11 15:19:23 +08:00
|
|
|
|
return CGSizeMake(contentW, MAX(h, 0.1));
|
2025-11-08 22:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
case KBSkinDetailSectionTitle: {
|
|
|
|
|
|
return CGSizeMake(contentW, 44);
|
|
|
|
|
|
}
|
|
|
|
|
|
case KBSkinDetailSectionGrid: {
|
|
|
|
|
|
// 2 列
|
|
|
|
|
|
CGFloat spacing = 12.0;
|
|
|
|
|
|
CGFloat itemW = floor((contentW - spacing) / 2.0);
|
2025-11-17 15:06:05 +08:00
|
|
|
|
// CGFloat itemH = itemW * 0.75 + 56;
|
|
|
|
|
|
return CGSizeMake(itemW, KBFit(197));
|
2025-11-08 22:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return CGSizeZero;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
|
2025-11-17 15:39:03 +08:00
|
|
|
|
// 控制各区上下留白,缩小 Header 与 Tags 之间的间距
|
|
|
|
|
|
switch (section) {
|
|
|
|
|
|
case KBSkinDetailSectionHeader:
|
|
|
|
|
|
// 顶部与导航之间 12,底部稍微缩小
|
|
|
|
|
|
return UIEdgeInsetsMake(12, 16, 4, 16);
|
|
|
|
|
|
case KBSkinDetailSectionTags:
|
|
|
|
|
|
// 与 Header 更贴近一些
|
|
|
|
|
|
return UIEdgeInsetsMake(14, 16, 5, 16);
|
|
|
|
|
|
default:
|
|
|
|
|
|
return UIEdgeInsetsMake(0, 16, 12, 16);
|
|
|
|
|
|
}
|
2025-11-08 22:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
|
|
|
|
|
|
return 12.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
|
|
|
|
|
|
// 网格区需要这个间距,其它区也保持统一
|
|
|
|
|
|
return 12.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Lazy
|
|
|
|
|
|
|
|
|
|
|
|
- (UICollectionView *)collectionView {
|
|
|
|
|
|
if (!_collectionView) {
|
|
|
|
|
|
UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
|
|
|
|
|
|
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
|
|
|
|
|
|
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
|
|
|
|
|
_collectionView.backgroundColor = [UIColor whiteColor];
|
|
|
|
|
|
_collectionView.dataSource = self;
|
|
|
|
|
|
_collectionView.delegate = self;
|
|
|
|
|
|
|
|
|
|
|
|
// 注册 cell
|
|
|
|
|
|
[_collectionView registerClass:KBSkinDetailHeaderCell.class forCellWithReuseIdentifier:kHeaderCellId];
|
|
|
|
|
|
[_collectionView registerClass:KBSkinTagsContainerCell.class forCellWithReuseIdentifier:kTagsContainerCellId];
|
|
|
|
|
|
[_collectionView registerClass:KBSkinSectionTitleCell.class forCellWithReuseIdentifier:kSectionTitleCellId];
|
|
|
|
|
|
[_collectionView registerClass:KBSkinCardCell.class forCellWithReuseIdentifier:kGridCellId];
|
|
|
|
|
|
}
|
|
|
|
|
|
return _collectionView;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 15:29:21 +08:00
|
|
|
|
#pragma mark - Lazy (Bottom Bar)
|
|
|
|
|
|
|
|
|
|
|
|
- (KBSkinBottomActionView *)bottomBar {
|
|
|
|
|
|
if (!_bottomBar) {
|
|
|
|
|
|
_bottomBar = [[KBSkinBottomActionView alloc] init];
|
|
|
|
|
|
// 中文注释:配置文案与图标,可根据业务传入金币图/价格
|
|
|
|
|
|
[_bottomBar configWithTitle:@"Download" price:@"20" icon:nil];
|
2025-11-10 15:38:30 +08:00
|
|
|
|
KBWeakSelf
|
2025-11-10 15:29:21 +08:00
|
|
|
|
_bottomBar.tapHandler = ^{
|
|
|
|
|
|
// 示例:点击下载/购买
|
|
|
|
|
|
// [KBHUD showText:@"点击了下载"];
|
|
|
|
|
|
// TODO: 在此处触发下载/购买逻辑
|
|
|
|
|
|
[weakSelf handleDownloadAction];
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
return _bottomBar;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Actions
|
|
|
|
|
|
- (void)handleDownloadAction {
|
2025-12-11 16:59:14 +08:00
|
|
|
|
// if (self.isProcessingAction) { return; }
|
2025-12-11 16:39:22 +08:00
|
|
|
|
if (self.themeId.length == 0) {
|
|
|
|
|
|
[KBHUD showInfo:KBLocalized(@"主题信息缺失")];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!self.detailModel) {
|
|
|
|
|
|
[KBHUD showInfo:KBLocalized(@"正在加载主题详情")];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (self.detailModel.isPurchased) {
|
|
|
|
|
|
[self requestDownload];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
[self purchaseCurrentTheme];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)purchaseCurrentTheme {
|
2025-12-11 16:59:14 +08:00
|
|
|
|
// if (self.isProcessingAction) { return; }
|
|
|
|
|
|
// self.isProcessingAction = YES;
|
2025-12-11 16:39:22 +08:00
|
|
|
|
[KBHUD show];
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
[self.shopVM purchaseThemeWithId:self.themeId completion:^(BOOL success, NSError * _Nullable error) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
2025-12-11 16:59:14 +08:00
|
|
|
|
// weakSelf.isProcessingAction = NO;
|
2025-12-11 16:39:22 +08:00
|
|
|
|
[KBHUD dismiss];
|
|
|
|
|
|
if (error || !success) {
|
|
|
|
|
|
NSString *msg = error.localizedDescription ?: KBLocalized(@"购买失败");
|
|
|
|
|
|
[KBHUD showInfo:msg];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
weakSelf.detailModel.isPurchased = YES;
|
|
|
|
|
|
[weakSelf updateBottomBarAppearance];
|
|
|
|
|
|
[weakSelf requestDownload];
|
|
|
|
|
|
});
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)requestDownload {
|
2025-12-11 16:59:14 +08:00
|
|
|
|
// if (self.isProcessingAction) { return; }
|
|
|
|
|
|
// self.isProcessingAction = YES;
|
2025-12-11 16:39:22 +08:00
|
|
|
|
[KBHUD show];
|
|
|
|
|
|
NSMutableDictionary *skin = [NSMutableDictionary dictionary];
|
|
|
|
|
|
if (!skin[@"id"] && self.detailModel.themeId) {
|
|
|
|
|
|
skin[@"id"] = self.detailModel.themeId;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!skin[@"name"] && self.detailModel.themeName) {
|
|
|
|
|
|
skin[@"name"] = self.detailModel.themeName;
|
|
|
|
|
|
}
|
2025-12-11 16:59:14 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
skin[@"zip_url"] = self.detailModel.themeDownloadUrl ? self.detailModel.themeDownloadUrl : @"";
|
2025-12-11 19:43:55 +08:00
|
|
|
|
if (self.detailModel.themePreviewImageUrl.length > 0) {
|
|
|
|
|
|
skin[@"preview"] = self.detailModel.themePreviewImageUrl;
|
|
|
|
|
|
}
|
2025-12-11 16:39:22 +08:00
|
|
|
|
|
|
|
|
|
|
[[KBSkinService shared] applySkinWithJSON:skin
|
|
|
|
|
|
fromViewController:self
|
|
|
|
|
|
mode:KBSkinSourceModeRemoteZip
|
|
|
|
|
|
completion:^(BOOL success) {
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
[KBHUD showSuccess:KBLocalized(@"已开始下载")];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
[KBHUD showInfo:KBLocalized(@"下载失败")];
|
|
|
|
|
|
}
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)updateBottomBarAppearance {
|
|
|
|
|
|
BOOL purchased = self.detailModel.isPurchased;
|
|
|
|
|
|
if (purchased) {
|
|
|
|
|
|
self.bottomBar.titleText = KBLocalized(@"Download again");
|
|
|
|
|
|
self.bottomBar.showsPrice = NO;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
NSString *price = self.detailModel ? [NSString stringWithFormat:@"%.2f", self.detailModel.themePrice] : @"0";
|
|
|
|
|
|
self.bottomBar.titleText = KBLocalized(@"Download");
|
|
|
|
|
|
self.bottomBar.priceText = price;
|
|
|
|
|
|
self.bottomBar.showsPrice = YES;
|
|
|
|
|
|
UIImage *coin = [UIImage imageNamed:@"shop_jb_icon"];
|
|
|
|
|
|
if (coin) {
|
|
|
|
|
|
self.bottomBar.iconImage = coin;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-10 15:29:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-11 15:00:58 +08:00
|
|
|
|
- (void)fetchThemeDetailIfNeeded {
|
|
|
|
|
|
if (self.themeId.length == 0) {
|
|
|
|
|
|
NSLog(@"[KBSkinDetailVC] themeId is empty, skip detail request");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
2025-12-11 15:19:23 +08:00
|
|
|
|
[self.shopVM fetchThemeDetailWithId:self.themeId completion:^(KBShopThemeDetailModel * _Nullable detail, NSError * _Nullable error) {
|
2025-12-11 15:00:58 +08:00
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
NSLog(@"[KBSkinDetailVC] fetch detail failed: %@", error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-11 15:19:23 +08:00
|
|
|
|
if (!detail) {
|
|
|
|
|
|
NSLog(@"[KBSkinDetailVC] theme detail is empty");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
weakSelf.detailModel = detail;
|
|
|
|
|
|
NSMutableArray<NSString *> *tagNames = [NSMutableArray array];
|
|
|
|
|
|
for (KBShopThemeTagModel *tag in detail.themeTag) {
|
|
|
|
|
|
if (tag.label.length) {
|
|
|
|
|
|
[tagNames addObject:tag.label];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
weakSelf.tags = tagNames.copy;
|
2025-12-11 16:39:22 +08:00
|
|
|
|
[weakSelf updateBottomBarAppearance];
|
2025-12-11 15:00:58 +08:00
|
|
|
|
[weakSelf.collectionView reloadData];
|
|
|
|
|
|
});
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (KBShopVM *)shopVM {
|
|
|
|
|
|
if (!_shopVM) {
|
|
|
|
|
|
_shopVM = [[KBShopVM alloc] init];
|
|
|
|
|
|
}
|
|
|
|
|
|
return _shopVM;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-11 17:51:00 +08:00
|
|
|
|
#pragma mark - Data
|
|
|
|
|
|
|
|
|
|
|
|
- (void)fetchRecommendedThemes {
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
[self.shopVM fetchRecommendedThemesWithCompletion:^(NSArray<KBShopThemeModel *> * _Nullable themes, NSError * _Nullable error) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
NSLog(@"[KBSkinDetailVC] fetch recommended failed: %@", error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
weakSelf.recommendedThemes = themes ?: @[];
|
|
|
|
|
|
NSIndexSet *sections = [NSIndexSet indexSetWithIndex:KBSkinDetailSectionGrid];
|
|
|
|
|
|
[weakSelf.collectionView reloadSections:sections];
|
|
|
|
|
|
});
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSString *)priceTextForTheme:(KBShopThemeModel *)model {
|
|
|
|
|
|
// if (model.isFree) {
|
|
|
|
|
|
// return KBLocalized(@"Free");
|
|
|
|
|
|
// }
|
|
|
|
|
|
if (model.themePrice > 0.0) {
|
|
|
|
|
|
return [NSString stringWithFormat:@"%.2f", model.themePrice];
|
|
|
|
|
|
}
|
|
|
|
|
|
return @"0";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-08 22:25:57 +08:00
|
|
|
|
@end
|