feat: show member article

This commit is contained in:
bggRGjQaUbCoE
2024-10-19 10:38:29 +08:00
parent 8c26ef4ff6
commit d04a72c462
34 changed files with 1098 additions and 8 deletions

View File

@@ -300,6 +300,9 @@ class Api {
static const String spaceBangumi =
'${HttpString.appBaseUrl}/x/v2/space/bangumi';
static const String spaceArticle =
'${HttpString.appBaseUrl}/x/v2/space/article';
static const String spaceFav = '/x/v3/fav/folder/space';
// 用户名片信息

View File

@@ -20,6 +20,7 @@ import '../models/member/info.dart';
import '../models/member/seasons.dart';
import '../models/member/tags.dart';
import '../models/space_archive/data.dart' as archive;
import '../models/space_article/data.dart' as article;
import '../utils/utils.dart';
import '../utils/wbi_sign.dart';
import 'index.dart';
@@ -60,6 +61,54 @@ class MemberHttp {
}
}
static Future<LoadingState> spaceArticle({
required int mid,
required int page,
}) async {
String? accessKey = GStorage.localCache
.get(LocalCacheKey.accessKey, defaultValue: {})['value'];
Map<String, String> data = {
if (accessKey != null) 'access_key': accessKey,
'appkey': Constants.appKey,
'build': '1462100',
'c_locale': 'zh_CN',
'channel': 'yingyongbao',
'mobi_app': 'android_hd',
'platform': 'android',
'pn': '$page',
'ps': '10',
's_locale': 'zh_CN',
'statistics': Constants.statistics,
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
'vmid': mid.toString(),
};
String sign = Utils.appSign(
data,
Constants.appKey,
Constants.appSec,
);
data['sign'] = sign;
int? _mid = GStorage.userInfo.get('userInfoCache')?.mid;
dynamic res = await Request().get(
Api.spaceArticle,
data: data,
options: Options(
headers: {
'env': 'prod',
'app-key': 'android_hd',
'x-bili-mid': _mid,
'bili-http-engine': 'cronet',
'user-agent': Constants.userAgent,
},
),
);
if (res.data['code'] == 0) {
return LoadingState.success(article.Data.fromJson(res.data['data']));
} else {
return LoadingState.error(res.data['message']);
}
}
static Future<LoadingState> spaceFav({
required int mid,
}) async {

View File

@@ -15,6 +15,7 @@ import 'series.dart';
import 'setting.dart';
import 'tab.dart';
import 'tab2.dart';
import 'package:PiliPalaX/models/space_article/data.dart' as space;
part 'data.g.dart';
@@ -35,7 +36,7 @@ class Data {
Images? images;
Archive? archive;
Series? series;
Article? article;
space.Data? article;
Season? season;
@JsonKey(name: 'coin_archive')
CoinArchive? coinArchive;

View File

@@ -32,7 +32,7 @@ Data _$DataFromJson(Map<String, dynamic> json) => Data(
: Series.fromJson(json['series'] as Map<String, dynamic>),
article: json['article'] == null
? null
: Article.fromJson(json['article'] as Map<String, dynamic>),
: space.Data.fromJson(json['article'] as Map<String, dynamic>),
season: json['season'] == null
? null
: Season.fromJson(json['season'] as Map<String, dynamic>),

View File

@@ -0,0 +1,36 @@
import 'package:json_annotation/json_annotation.dart';
import 'nameplate.dart';
import 'official_verify.dart';
import 'pendant.dart';
import 'vip.dart';
part 'author.g.dart';
@JsonSerializable()
class Author {
int? mid;
String? name;
String? face;
Pendant? pendant;
@JsonKey(name: 'official_verify')
OfficialVerify? officialVerify;
Nameplate? nameplate;
Vip? vip;
Author({
this.mid,
this.name,
this.face,
this.pendant,
this.officialVerify,
this.nameplate,
this.vip,
});
factory Author.fromJson(Map<String, dynamic> json) {
return _$AuthorFromJson(json);
}
Map<String, dynamic> toJson() => _$AuthorToJson(this);
}

View File

@@ -0,0 +1,36 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'author.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Author _$AuthorFromJson(Map<String, dynamic> json) => Author(
mid: (json['mid'] as num?)?.toInt(),
name: json['name'] as String?,
face: json['face'] as String?,
pendant: json['pendant'] == null
? null
: Pendant.fromJson(json['pendant'] as Map<String, dynamic>),
officialVerify: json['official_verify'] == null
? null
: OfficialVerify.fromJson(
json['official_verify'] as Map<String, dynamic>),
nameplate: json['nameplate'] == null
? null
: Nameplate.fromJson(json['nameplate'] as Map<String, dynamic>),
vip: json['vip'] == null
? null
: Vip.fromJson(json['vip'] as Map<String, dynamic>),
);
Map<String, dynamic> _$AuthorToJson(Author instance) => <String, dynamic>{
'mid': instance.mid,
'name': instance.name,
'face': instance.face,
'pendant': instance.pendant,
'official_verify': instance.officialVerify,
'nameplate': instance.nameplate,
'vip': instance.vip,
};

View File

@@ -0,0 +1,19 @@
import 'package:json_annotation/json_annotation.dart';
part 'category.g.dart';
@JsonSerializable()
class Category {
int? id;
@JsonKey(name: 'parent_id')
int? parentId;
String? name;
Category({this.id, this.parentId, this.name});
factory Category.fromJson(Map<String, dynamic> json) {
return _$CategoryFromJson(json);
}
Map<String, dynamic> toJson() => _$CategoryToJson(this);
}

View File

@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'category.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Category _$CategoryFromJson(Map<String, dynamic> json) => Category(
id: (json['id'] as num?)?.toInt(),
parentId: (json['parent_id'] as num?)?.toInt(),
name: json['name'] as String?,
);
Map<String, dynamic> _$CategoryToJson(Category instance) => <String, dynamic>{
'id': instance.id,
'parent_id': instance.parentId,
'name': instance.name,
};

View File

@@ -0,0 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
import 'item.dart';
import 'list.dart';
part 'data.g.dart';
@JsonSerializable()
class Data {
int? count;
List<Item>? item;
@JsonKey(name: 'lists_count')
int? listsCount;
List<ArticleList>? lists;
Data({this.count, this.item, this.listsCount, this.lists});
factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
Map<String, dynamic> toJson() => _$DataToJson(this);
}

View File

@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'data.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Data _$DataFromJson(Map<String, dynamic> json) => Data(
count: (json['count'] as num?)?.toInt(),
item: (json['item'] as List<dynamic>?)
?.map((item) => Item.fromJson(item))
.toList(),
listsCount: (json['lists_count'] as num?)?.toInt(),
lists: (json['lists'] as List<dynamic>?)
?.map((item) => ArticleList.fromJson(item))
.toList(),
);
Map<String, dynamic> _$DataToJson(Data instance) => <String, dynamic>{
'count': instance.count,
'item': instance.item,
'lists_count': instance.listsCount,
'lists': instance.lists,
};

View File

@@ -0,0 +1,107 @@
import 'package:json_annotation/json_annotation.dart';
import 'author.dart';
import 'category.dart';
import 'media.dart';
import 'stats.dart';
part 'item.g.dart';
@JsonSerializable()
class Item {
int? id;
Category? category;
List<Category>? categories;
String? title;
String? summary;
@JsonKey(name: 'banner_url')
String? bannerUrl;
@JsonKey(name: 'template_id')
int? templateId;
int? state;
Author? author;
int? reprint;
@JsonKey(name: 'image_urls')
List<String>? imageUrls;
@JsonKey(name: 'publish_time')
int? publishTime;
int? ctime;
int? mtime;
Stats? stats;
int? attributes;
int? words;
@JsonKey(name: 'origin_image_urls')
List<String>? originImageUrls;
dynamic list;
@JsonKey(name: 'is_like')
bool? isLike;
Media? media;
@JsonKey(name: 'apply_time')
String? applyTime;
@JsonKey(name: 'check_time')
String? checkTime;
int? original;
@JsonKey(name: 'act_id')
int? actId;
dynamic dispute;
dynamic authenMark;
@JsonKey(name: 'cover_avid')
int? coverAvid;
@JsonKey(name: 'top_video_info')
dynamic topVideoInfo;
int? type;
@JsonKey(name: 'check_state')
int? checkState;
@JsonKey(name: 'origin_template_id')
int? originTemplateId;
String? uri;
String? param;
String? goto;
@JsonKey(name: 'publish_time_text')
String? publishTimeText;
String? dyn;
Item({
this.id,
this.category,
this.categories,
this.title,
this.summary,
this.bannerUrl,
this.templateId,
this.state,
this.author,
this.reprint,
this.imageUrls,
this.publishTime,
this.ctime,
this.mtime,
this.stats,
this.attributes,
this.words,
this.originImageUrls,
this.list,
this.isLike,
this.media,
this.applyTime,
this.checkTime,
this.original,
this.actId,
this.dispute,
this.authenMark,
this.coverAvid,
this.topVideoInfo,
this.type,
this.checkState,
this.originTemplateId,
this.uri,
this.param,
this.goto,
this.publishTimeText,
this.dyn,
});
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
Map<String, dynamic> toJson() => _$ItemToJson(this);
}

View File

@@ -0,0 +1,101 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'item.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Item _$ItemFromJson(Map<String, dynamic> json) => Item(
id: (json['id'] as num?)?.toInt(),
category: json['category'] == null
? null
: Category.fromJson(json['category'] as Map<String, dynamic>),
categories: (json['categories'] as List<dynamic>?)
?.map((e) => Category.fromJson(e as Map<String, dynamic>))
.toList(),
title: json['title'] as String?,
summary: json['summary'] as String?,
bannerUrl: json['banner_url'] as String?,
templateId: (json['template_id'] as num?)?.toInt(),
state: (json['state'] as num?)?.toInt(),
author: json['author'] == null
? null
: Author.fromJson(json['author'] as Map<String, dynamic>),
reprint: (json['reprint'] as num?)?.toInt(),
imageUrls: (json['image_urls'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
publishTime: (json['publish_time'] as num?)?.toInt(),
ctime: (json['ctime'] as num?)?.toInt(),
mtime: (json['mtime'] as num?)?.toInt(),
stats: json['stats'] == null
? null
: Stats.fromJson(json['stats'] as Map<String, dynamic>),
attributes: (json['attributes'] as num?)?.toInt(),
words: (json['words'] as num?)?.toInt(),
originImageUrls: (json['origin_image_urls'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
list: json['list'],
isLike: json['is_like'] as bool?,
media: json['media'] == null
? null
: Media.fromJson(json['media'] as Map<String, dynamic>),
applyTime: json['apply_time'] as String?,
checkTime: json['check_time'] as String?,
original: (json['original'] as num?)?.toInt(),
actId: (json['act_id'] as num?)?.toInt(),
dispute: json['dispute'],
authenMark: json['authenMark'],
coverAvid: (json['cover_avid'] as num?)?.toInt(),
topVideoInfo: json['top_video_info'],
type: (json['type'] as num?)?.toInt(),
checkState: (json['check_state'] as num?)?.toInt(),
originTemplateId: (json['origin_template_id'] as num?)?.toInt(),
uri: json['uri'] as String?,
param: json['param'] as String?,
goto: json['goto'] as String?,
publishTimeText: json['publish_time_text'] as String?,
dyn: json['dynamic'] as String?,
);
Map<String, dynamic> _$ItemToJson(Item instance) => <String, dynamic>{
'id': instance.id,
'category': instance.category,
'categories': instance.categories,
'title': instance.title,
'summary': instance.summary,
'banner_url': instance.bannerUrl,
'template_id': instance.templateId,
'state': instance.state,
'author': instance.author,
'reprint': instance.reprint,
'image_urls': instance.imageUrls,
'publish_time': instance.publishTime,
'ctime': instance.ctime,
'mtime': instance.mtime,
'stats': instance.stats,
'attributes': instance.attributes,
'words': instance.words,
'origin_image_urls': instance.originImageUrls,
'list': instance.list,
'is_like': instance.isLike,
'media': instance.media,
'apply_time': instance.applyTime,
'check_time': instance.checkTime,
'original': instance.original,
'act_id': instance.actId,
'dispute': instance.dispute,
'authenMark': instance.authenMark,
'cover_avid': instance.coverAvid,
'top_video_info': instance.topVideoInfo,
'type': instance.type,
'check_state': instance.checkState,
'origin_template_id': instance.originTemplateId,
'uri': instance.uri,
'param': instance.param,
'goto': instance.goto,
'publish_time_text': instance.publishTimeText,
'dynamic': instance.dyn,
};

View File

@@ -0,0 +1,17 @@
import 'package:json_annotation/json_annotation.dart';
part 'label.g.dart';
@JsonSerializable()
class Label {
String? path;
String? text;
@JsonKey(name: 'label_theme')
String? labelTheme;
Label({this.path, this.text, this.labelTheme});
factory Label.fromJson(Map<String, dynamic> json) => _$LabelFromJson(json);
Map<String, dynamic> toJson() => _$LabelToJson(this);
}

View File

@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'label.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Label _$LabelFromJson(Map<String, dynamic> json) => Label(
path: json['path'] as String?,
text: json['text'] as String?,
labelTheme: json['label_theme'] as String?,
);
Map<String, dynamic> _$LabelToJson(Label instance) => <String, dynamic>{
'path': instance.path,
'text': instance.text,
'label_theme': instance.labelTheme,
};

View File

@@ -0,0 +1,44 @@
import 'package:json_annotation/json_annotation.dart';
part 'list.g.dart';
@JsonSerializable()
class ArticleList {
int? id;
int? mid;
String? name;
@JsonKey(name: 'image_url')
String? imageUrl;
@JsonKey(name: 'update_time')
int? updateTime;
int? ctime;
@JsonKey(name: 'publish_time')
int? publishTime;
String? summary;
int? words;
int? read;
@JsonKey(name: 'articles_count')
int? articlesCount;
@JsonKey(name: 'update_time_text')
String? updateTimeText;
ArticleList({
this.id,
this.mid,
this.name,
this.imageUrl,
this.updateTime,
this.ctime,
this.publishTime,
this.summary,
this.words,
this.read,
this.articlesCount,
this.updateTimeText,
});
factory ArticleList.fromJson(Map<String, dynamic> json) =>
_$ListFromJson(json);
Map<String, dynamic> toJson() => _$ListToJson(this);
}

View File

@@ -0,0 +1,37 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'list.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
ArticleList _$ListFromJson(Map<String, dynamic> json) => ArticleList(
id: (json['id'] as num?)?.toInt(),
mid: (json['mid'] as num?)?.toInt(),
name: json['name'] as String?,
imageUrl: json['image_url'] as String?,
updateTime: (json['update_time'] as num?)?.toInt(),
ctime: (json['ctime'] as num?)?.toInt(),
publishTime: (json['publish_time'] as num?)?.toInt(),
summary: json['summary'] as String?,
words: (json['words'] as num?)?.toInt(),
read: (json['read'] as num?)?.toInt(),
articlesCount: (json['articles_count'] as num?)?.toInt(),
updateTimeText: json['update_time_text'] as String?,
);
Map<String, dynamic> _$ListToJson(ArticleList instance) => <String, dynamic>{
'id': instance.id,
'mid': instance.mid,
'name': instance.name,
'image_url': instance.imageUrl,
'update_time': instance.updateTime,
'ctime': instance.ctime,
'publish_time': instance.publishTime,
'summary': instance.summary,
'words': instance.words,
'read': instance.read,
'articles_count': instance.articlesCount,
'update_time_text': instance.updateTimeText,
};

View File

@@ -0,0 +1,33 @@
import 'package:json_annotation/json_annotation.dart';
part 'media.g.dart';
@JsonSerializable()
class Media {
int? score;
@JsonKey(name: 'media_id')
int? mediaId;
String? title;
String? cover;
String? area;
@JsonKey(name: 'type_id')
int? typeId;
@JsonKey(name: 'type_name')
String? typeName;
int? spoiler;
Media({
this.score,
this.mediaId,
this.title,
this.cover,
this.area,
this.typeId,
this.typeName,
this.spoiler,
});
factory Media.fromJson(Map<String, dynamic> json) => _$MediaFromJson(json);
Map<String, dynamic> toJson() => _$MediaToJson(this);
}

View File

@@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'media.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Media _$MediaFromJson(Map<String, dynamic> json) => Media(
score: (json['score'] as num?)?.toInt(),
mediaId: (json['media_id'] as num?)?.toInt(),
title: json['title'] as String?,
cover: json['cover'] as String?,
area: json['area'] as String?,
typeId: (json['type_id'] as num?)?.toInt(),
typeName: json['type_name'] as String?,
spoiler: (json['spoiler'] as num?)?.toInt(),
);
Map<String, dynamic> _$MediaToJson(Media instance) => <String, dynamic>{
'score': instance.score,
'media_id': instance.mediaId,
'title': instance.title,
'cover': instance.cover,
'area': instance.area,
'type_id': instance.typeId,
'type_name': instance.typeName,
'spoiler': instance.spoiler,
};

View File

@@ -0,0 +1,29 @@
import 'package:json_annotation/json_annotation.dart';
part 'nameplate.g.dart';
@JsonSerializable()
class Nameplate {
int? nid;
String? name;
String? image;
@JsonKey(name: 'image_small')
String? imageSmall;
String? level;
String? condition;
Nameplate({
this.nid,
this.name,
this.image,
this.imageSmall,
this.level,
this.condition,
});
factory Nameplate.fromJson(Map<String, dynamic> json) {
return _$NameplateFromJson(json);
}
Map<String, dynamic> toJson() => _$NameplateToJson(this);
}

View File

@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'nameplate.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Nameplate _$NameplateFromJson(Map<String, dynamic> json) => Nameplate(
nid: (json['nid'] as num?)?.toInt(),
name: json['name'] as String?,
image: json['image'] as String?,
imageSmall: json['image_small'] as String?,
level: json['level'] as String?,
condition: json['condition'] as String?,
);
Map<String, dynamic> _$NameplateToJson(Nameplate instance) => <String, dynamic>{
'nid': instance.nid,
'name': instance.name,
'image': instance.image,
'image_small': instance.imageSmall,
'level': instance.level,
'condition': instance.condition,
};

View File

@@ -0,0 +1,17 @@
import 'package:json_annotation/json_annotation.dart';
part 'official_verify.g.dart';
@JsonSerializable()
class OfficialVerify {
int? type;
String? desc;
OfficialVerify({this.type, this.desc});
factory OfficialVerify.fromJson(Map<String, dynamic> json) {
return _$OfficialVerifyFromJson(json);
}
Map<String, dynamic> toJson() => _$OfficialVerifyToJson(this);
}

View File

@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'official_verify.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
OfficialVerify _$OfficialVerifyFromJson(Map<String, dynamic> json) =>
OfficialVerify(
type: (json['type'] as num?)?.toInt(),
desc: json['desc'] as String?,
);
Map<String, dynamic> _$OfficialVerifyToJson(OfficialVerify instance) =>
<String, dynamic>{
'type': instance.type,
'desc': instance.desc,
};

View File

@@ -0,0 +1,19 @@
import 'package:json_annotation/json_annotation.dart';
part 'pendant.g.dart';
@JsonSerializable()
class Pendant {
int? pid;
String? name;
String? image;
int? expire;
Pendant({this.pid, this.name, this.image, this.expire});
factory Pendant.fromJson(Map<String, dynamic> json) {
return _$PendantFromJson(json);
}
Map<String, dynamic> toJson() => _$PendantToJson(this);
}

View File

@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'pendant.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Pendant _$PendantFromJson(Map<String, dynamic> json) => Pendant(
pid: (json['pid'] as num?)?.toInt(),
name: json['name'] as String?,
image: json['image'] as String?,
expire: (json['expire'] as num?)?.toInt(),
);
Map<String, dynamic> _$PendantToJson(Pendant instance) => <String, dynamic>{
'pid': instance.pid,
'name': instance.name,
'image': instance.image,
'expire': instance.expire,
};

View File

@@ -0,0 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
import 'data.dart';
part 'space_article.g.dart';
@JsonSerializable()
class SpaceArticle {
int? code;
String? message;
int? ttl;
Data? data;
SpaceArticle({this.code, this.message, this.ttl, this.data});
factory SpaceArticle.fromJson(Map<String, dynamic> json) {
return _$SpaceArticleFromJson(json);
}
Map<String, dynamic> toJson() => _$SpaceArticleToJson(this);
}

View File

@@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'space_article.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
SpaceArticle _$SpaceArticleFromJson(Map<String, dynamic> json) => SpaceArticle(
code: (json['code'] as num?)?.toInt(),
message: json['message'] as String?,
ttl: (json['ttl'] as num?)?.toInt(),
data: json['data'] == null
? null
: Data.fromJson(json['data'] as Map<String, dynamic>),
);
Map<String, dynamic> _$SpaceArticleToJson(SpaceArticle instance) =>
<String, dynamic>{
'code': instance.code,
'message': instance.message,
'ttl': instance.ttl,
'data': instance.data,
};

View File

@@ -0,0 +1,30 @@
import 'package:json_annotation/json_annotation.dart';
part 'stats.g.dart';
@JsonSerializable()
class Stats {
int? view;
int? favorite;
int? like;
int? dislike;
int? reply;
int? share;
int? coin;
int? dyn;
Stats({
this.view,
this.favorite,
this.like,
this.dislike,
this.reply,
this.share,
this.coin,
this.dyn,
});
factory Stats.fromJson(Map<String, dynamic> json) => _$StatsFromJson(json);
Map<String, dynamic> toJson() => _$StatsToJson(this);
}

View File

@@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'stats.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Stats _$StatsFromJson(Map<String, dynamic> json) => Stats(
view: (json['view'] as num?)?.toInt(),
favorite: (json['favorite'] as num?)?.toInt(),
like: (json['like'] as num?)?.toInt(),
dislike: (json['dislike'] as num?)?.toInt(),
reply: (json['reply'] as num?)?.toInt(),
share: (json['share'] as num?)?.toInt(),
coin: (json['coin'] as num?)?.toInt(),
dyn: (json['dynamic'] as num?)?.toInt(),
);
Map<String, dynamic> _$StatsToJson(Stats instance) => <String, dynamic>{
'view': instance.view,
'favorite': instance.favorite,
'like': instance.like,
'dislike': instance.dislike,
'reply': instance.reply,
'share': instance.share,
'coin': instance.coin,
'dynamic': instance.dyn,
};

View File

@@ -0,0 +1,37 @@
import 'package:json_annotation/json_annotation.dart';
import 'label.dart';
part 'vip.g.dart';
@JsonSerializable()
class Vip {
int? type;
int? status;
@JsonKey(name: 'due_date')
int? dueDate;
@JsonKey(name: 'vip_pay_type')
int? vipPayType;
@JsonKey(name: 'theme_type')
int? themeType;
Label? label;
@JsonKey(name: 'avatar_subscript')
int? avatarSubscript;
@JsonKey(name: 'nickname_color')
String? nicknameColor;
Vip({
this.type,
this.status,
this.dueDate,
this.vipPayType,
this.themeType,
this.label,
this.avatarSubscript,
this.nicknameColor,
});
factory Vip.fromJson(Map<String, dynamic> json) => _$VipFromJson(json);
Map<String, dynamic> toJson() => _$VipToJson(this);
}

View File

@@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'vip.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Vip _$VipFromJson(Map<String, dynamic> json) => Vip(
type: (json['type'] as num?)?.toInt(),
status: (json['status'] as num?)?.toInt(),
dueDate: (json['due_date'] as num?)?.toInt(),
vipPayType: (json['vip_pay_type'] as num?)?.toInt(),
themeType: (json['theme_type'] as num?)?.toInt(),
label: json['label'] == null
? null
: Label.fromJson(json['label'] as Map<String, dynamic>),
avatarSubscript: (json['avatar_subscript'] as num?)?.toInt(),
nicknameColor: json['nickname_color'] as String?,
);
Map<String, dynamic> _$VipToJson(Vip instance) => <String, dynamic>{
'type': instance.type,
'status': instance.status,
'due_date': instance.dueDate,
'vip_pay_type': instance.vipPayType,
'theme_type': instance.themeType,
'label': instance.label,
'avatar_subscript': instance.avatarSubscript,
'nickname_color': instance.nicknameColor,
};

View File

@@ -1,12 +1,22 @@
import 'package:PiliPalaX/common/constants.dart';
import 'package:PiliPalaX/common/widgets/http_error.dart';
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
import 'package:PiliPalaX/http/loading_state.dart';
import 'package:PiliPalaX/models/space_article/item.dart';
import 'package:PiliPalaX/pages/member/new/content/member_contribute/content/article/member_article_ctr.dart';
import 'package:PiliPalaX/utils/app_scheme.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MemberArticle extends StatefulWidget {
const MemberArticle({
super.key,
required this.heroTag,
required this.mid,
});
final String? heroTag;
final int mid;
@override
State<MemberArticle> createState() => _MemberArticleState();
@@ -17,11 +27,91 @@ class _MemberArticleState extends State<MemberArticle>
@override
bool get wantKeepAlive => true;
late final _controller = Get.put(
MemberArticleCtr(mid: widget.mid),
tag: widget.heroTag,
);
@override
Widget build(BuildContext context) {
super.build(context);
return Center(
child: Text('Article'),
return Obx(() => _buildBody(_controller.loadingState.value));
}
_buildBody(LoadingState loadingState) {
return loadingState is Success
? MediaQuery.removePadding(
context: context,
removeTop: true,
child: RefreshIndicator(
onRefresh: () async {
await _controller.onRefresh();
},
child: ListView.separated(
itemCount: loadingState.response.length,
itemBuilder: (_, index) {
if (index == loadingState.response.length - 1) {
_controller.onLoadMore();
}
Item item = loadingState.response[index];
return ListTile(
dense: true,
onTap: () {
PiliScheme.routePush(Uri.parse(item.uri ?? ''));
},
leading: item.originImageUrls?.isNotEmpty == true
? Container(
margin: const EdgeInsets.symmetric(vertical: 6),
child: LayoutBuilder(
builder: (_, constraints) {
return NetworkImgLayer(
radius: 6,
src: item.originImageUrls!.first,
width: constraints.maxHeight *
StyleString.aspectRatio,
height: constraints.maxHeight,
);
},
),
)
: null,
title: Text(
item.title ?? '',
style: TextStyle(
fontSize: 15,
),
),
subtitle: item.summary?.isNotEmpty == true
? Text(
item.summary!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.outline,
),
)
: null,
);
},
separatorBuilder: (_, index) => Divider(height: 1),
),
),
)
: loadingState is Error
? Center(
child: CustomScrollView(
shrinkWrap: true,
slivers: [
HttpError(
errMsg: loadingState.errMsg,
fn: _controller.onReload,
),
],
),
)
: Center(
child: CircularProgressIndicator(),
);
}
}

View File

@@ -0,0 +1,50 @@
import 'package:PiliPalaX/http/loading_state.dart';
import 'package:PiliPalaX/http/member.dart';
import 'package:PiliPalaX/models/space_article/data.dart';
import 'package:PiliPalaX/pages/common/common_controller.dart';
class MemberArticleCtr extends CommonController {
MemberArticleCtr({
required this.mid,
});
final int mid;
bool isEnd = false;
int count = -1;
@override
void onInit() {
super.onInit();
queryData();
}
@override
Future onRefresh() async {
isEnd = false;
return super.onRefresh();
}
@override
Future queryData([bool isRefresh = true]) {
if (isEnd) return Future.value();
return super.queryData(isRefresh);
}
@override
bool customHandleResponse(Success response) {
Data data = response.response;
if (currentPage == 1) {
count = data.count ?? -1;
} else if (loadingState.value is Success) {
data.item?.insertAll(0, (loadingState.value as Success).response);
}
isEnd = (data.item?.length ?? -1) >= count;
loadingState.value = LoadingState.success(data.item);
return true;
}
@override
Future<LoadingState> customGetData() =>
MemberHttp.spaceArticle(mid: mid, page: currentPage);
}

View File

@@ -89,7 +89,10 @@ class _MemberContributeState extends State<MemberContribute>
heroTag: widget.heroTag,
mid: widget.mid,
),
'article' => MemberArticle(heroTag: widget.heroTag),
'article' => MemberArticle(
heroTag: widget.heroTag,
mid: widget.mid,
),
'audio' => MemberAudio(heroTag: widget.heroTag),
'season_video' => MemberVideo(
type: ContributeType.season,

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'package:PiliPalaX/common/constants.dart';
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
import 'package:PiliPalaX/common/widgets/video_card_v_member_home.dart';
import 'package:PiliPalaX/http/loading_state.dart';
import 'package:PiliPalaX/models/space/data.dart';
@@ -13,6 +14,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import '../../../../../utils/app_scheme.dart';
class MemberHome extends StatefulWidget {
const MemberHome({super.key, this.heroTag});
@@ -93,7 +96,7 @@ class _MemberHomeState extends State<MemberHome>
true) ...[
_videoHeader(
title: '最近点赞的视频',
param: 'coinArchive',
param: 'likeArchive',
count: loadingState.response.likeArchive.count,
),
// TODO
@@ -105,7 +108,53 @@ class _MemberHomeState extends State<MemberHome>
param1: 'article',
count: loadingState.response.article.count,
),
// TODO
SliverToBoxAdapter(
child: ListTile(
dense: true,
onTap: () {
PiliScheme.routePush(Uri.parse(
loadingState.response.article.item.first.uri ?? ''));
},
leading: loadingState.response.article.item.first
.originImageUrls?.isNotEmpty ==
true
? Container(
margin: const EdgeInsets.symmetric(vertical: 6),
child: LayoutBuilder(
builder: (_, constraints) {
return NetworkImgLayer(
radius: 6,
src: loadingState.response.article.item.first
.originImageUrls!.first,
width: constraints.maxHeight *
StyleString.aspectRatio,
height: constraints.maxHeight,
);
},
),
)
: null,
title: Text(
loadingState.response.article.item.first.title ?? '',
style: TextStyle(
fontSize: 15,
),
),
subtitle: loadingState.response.article.item.first.summary
?.isNotEmpty ==
true
? Text(
loadingState.response.article.item.first.summary!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
color: Theme.of(context).colorScheme.outline,
),
)
: null,
),
),
],
if (loadingState.response?.audios?.item?.isNotEmpty == true) ...[
_videoHeader(