Compare commits

...

9 Commits

Author SHA1 Message Date
bggRGjQaUbCoE
e0243461bb opt scheme
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-15 18:03:36 +08:00
bggRGjQaUbCoE
2877372f67 fix msg avatar
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-15 12:47:06 +08:00
bggRGjQaUbCoE
d6c12195f8 opt member tab
try-catch handle live dm

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-15 12:37:51 +08:00
bggRGjQaUbCoE
e280f6ee4a login/exp log
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-14 15:57:55 +08:00
bggRGjQaUbCoE
4275719844 opt save dyn
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-14 12:21:17 +08:00
bggRGjQaUbCoE
f41af00b31 fix live dm
opt live/article report

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-13 12:42:22 +08:00
Fengning Zhu
10ed5f2ea4 fix: resolve fullscreen UI offset issue on some Android tablets (#873) 2025-07-13 11:47:38 +08:00
bggRGjQaUbCoE
44ba554e0e fix save reply
opt profile page

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-12 00:13:57 +08:00
bggRGjQaUbCoE
c346d586a5 opt reply item
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-11 21:27:46 +08:00
46 changed files with 667 additions and 219 deletions

View File

@@ -929,4 +929,10 @@ class Api {
static const String updateReserve = '/x/new-reserve/up/reserve/update';
static const String reserveInfo = '/x/new-reserve/up/reserve/info';
static const String loginLog = '/x/member/web/login/log';
static const String expLog = '/x/member/web/exp/log';
static const String moralLog = '/x/member/web/moral/log';
}

View File

@@ -7,6 +7,7 @@ import 'package:PiliPlus/models/user/stat.dart';
import 'package:PiliPlus/models_new/coin_log/data.dart';
import 'package:PiliPlus/models_new/history/data.dart';
import 'package:PiliPlus/models_new/later/data.dart';
import 'package:PiliPlus/models_new/login_log/data.dart';
import 'package:PiliPlus/models_new/media_list/data.dart';
import 'package:PiliPlus/models_new/space_setting/data.dart';
import 'package:PiliPlus/models_new/sub/sub/data.dart';
@@ -413,4 +414,34 @@ class UserHttp {
return Error(res.data['message']);
}
}
static Future<LoadingState<LoginLogData>> loginLog() async {
final res = await Request().get(
Api.loginLog,
queryParameters: {
'jsonp': 'jsonp',
'web_location': '333.33',
},
);
if (res.data['code'] == 0) {
return Success(LoginLogData.fromJson(res.data['data']));
} else {
return Error(res.data['message']);
}
}
static Future<LoadingState<CoinLogData>> expLog() async {
final res = await Request().get(
Api.expLog,
queryParameters: {
'jsonp': 'jsonp',
'web_location': '333.33',
},
);
if (res.data['code'] == 0) {
return Success(CoinLogData.fromJson(res.data['data']));
} else {
return Error(res.data['message']);
}
}
}

View File

@@ -61,7 +61,7 @@ class HotVideoItemModel extends BaseRecVideoItemModel with MultiSelectData {
class HotStat extends Stat {
int? reply;
int? favorite;
int? coin;
num? coin;
int? share;
int? nowRank;
int? hisRank;

View File

@@ -1,5 +1,5 @@
class PgcLCF {
int? coinNumber;
num? coinNumber;
int? favorite;
int? isOriginal;
int? like;

View File

@@ -2,7 +2,7 @@ class AccountMyInfoData {
int? mid;
String? name;
String? sign;
int? coins;
num? coins;
String? birthday;
String? face;
int? faceNftNew;
@@ -46,7 +46,7 @@ class AccountMyInfoData {
mid: json['mid'] as int?,
name: json['name'] as String?,
sign: json['sign'] as String?,
coins: json['coins'] as int?,
coins: json['coins'] as num?,
birthday: json['birthday'] as String?,
face: json['face'] as String?,
faceNftNew: json['face_nft_new'] as int?,

View File

@@ -5,7 +5,7 @@ class ArticleInfoData {
int? like;
bool? attention;
bool? favorite;
int? coin;
num? coin;
Stats? stats;
String? title;
String? bannerUrl;
@@ -57,7 +57,7 @@ class ArticleInfoData {
like: json['like'] as int?,
attention: json['attention'] as bool?,
favorite: json['favorite'] as bool?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
stats: json['stats'] == null
? null
: Stats.fromJson(json['stats'] as Map<String, dynamic>),

View File

@@ -5,7 +5,7 @@ class Stats {
int? dislike;
int? reply;
int? share;
int? coin;
num? coin;
int? dynam1c;
Stats({
@@ -26,7 +26,7 @@ class Stats {
dislike: json['dislike'] as int?,
reply: json['reply'] as int?,
share: json['share'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
dynam1c: json['dynamic'] as int?,
);
}

View File

@@ -5,7 +5,7 @@ class Stats {
int? dislike;
int? reply;
int? share;
int? coin;
num? coin;
int? dynam1c;
Stats({
@@ -26,7 +26,7 @@ class Stats {
dislike: json['dislike'] as int?,
reply: json['reply'] as int?,
share: json['share'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
dynam1c: json['dynamic'] as int?,
);
}

View File

@@ -5,7 +5,7 @@ class Stats {
int? dislike;
int? reply;
int? share;
int? coin;
num? coin;
int? dynam1c;
Stats({
@@ -26,7 +26,7 @@ class Stats {
dislike: json['dislike'] as int?,
reply: json['reply'] as int?,
share: json['share'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
dynam1c: json['dynamic'] as int?,
);
}

View File

@@ -6,7 +6,7 @@ class CntInfo {
int? share;
int? reply;
int? danmaku;
int? coin;
num? coin;
int? vt;
int? playSwitch;
String? viewText1;
@@ -33,7 +33,7 @@ class CntInfo {
share: json['share'] as int?,
reply: json['reply'] as int?,
danmaku: json['danmaku'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
vt: json['vt'] as int?,
playSwitch: json['play_switch'] as int?,
viewText1: json['view_text_1'] as String?,

View File

@@ -3,7 +3,7 @@ class Stat {
int? view;
int? danmaku;
int? reply;
int? coin;
num? coin;
int? seriesFollow;
int? seriesView;
int? likes;
@@ -26,7 +26,7 @@ class Stat {
view: json['view'] as int?,
danmaku: json['danmaku'] as int?,
reply: json['reply'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
seriesFollow: json['series_follow'] as int?,
seriesView: json['series_view'] as int?,
likes: json['likes'] as int?,

View File

@@ -4,7 +4,7 @@ class Stat {
int? danmaku;
int? reply;
int? favorite;
int? coin;
num? coin;
int? share;
int? nowRank;
int? hisRank;
@@ -35,7 +35,7 @@ class Stat {
danmaku: json['danmaku'] as int?,
reply: json['reply'] as int?,
favorite: json['favorite'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
share: json['share'] as int?,
nowRank: json['now_rank'] as int?,
hisRank: json['his_rank'] as int?,

View File

@@ -0,0 +1,15 @@
import 'package:PiliPlus/models_new/login_log/list.dart';
class LoginLogData {
int? count;
List<LoginLogItem>? list;
LoginLogData({this.count, this.list});
factory LoginLogData.fromJson(Map<String, dynamic> json) => LoginLogData(
count: json['count'] as int?,
list: (json['list'] as List<dynamic>?)
?.map((e) => LoginLogItem.fromJson(e as Map<String, dynamic>))
.toList(),
);
}

View File

@@ -0,0 +1,26 @@
class LoginLogItem {
String ip;
int? time;
String timeAt;
bool? status;
int? type;
String geo;
LoginLogItem({
required this.ip,
this.time,
required this.timeAt,
this.status,
this.type,
required this.geo,
});
factory LoginLogItem.fromJson(Map<String, dynamic> json) => LoginLogItem(
ip: json['ip'] ?? '',
time: json['time'] as int?,
timeAt: json['time_at'] ?? '',
status: json['status'] as bool?,
type: json['type'] as int?,
geo: json['geo'] ?? '',
);
}

View File

@@ -1,11 +1,11 @@
class Coin {
int? maxNum;
int? coinNumber;
num? maxNum;
num? coinNumber;
Coin({this.maxNum, this.coinNumber});
factory Coin.fromJson(Map<String, dynamic> json) => Coin(
maxNum: json['max_num'] as int?,
coinNumber: json['coin_number'] as int?,
maxNum: json['max_num'] as num?,
coinNumber: json['coin_number'] as num?,
);
}

View File

@@ -1,5 +1,5 @@
class Stat {
int? coins;
num? coins;
int? danmakus;
int? favorite;
int? favorites;

View File

@@ -2,7 +2,7 @@ import 'package:PiliPlus/models_new/pgc/pgc_info_model/danmaku.dart';
import 'package:PiliPlus/models_new/pgc/pgc_info_model/vt.dart';
class StatForUnity {
int? coin;
num? coin;
Danmaku? danmaku;
int? likes;
int? reply;
@@ -11,7 +11,7 @@ class StatForUnity {
StatForUnity({this.coin, this.danmaku, this.likes, this.reply, this.vt});
factory StatForUnity.fromJson(Map<String, dynamic> json) => StatForUnity(
coin: json['coin'] as int?,
coin: json['coin'] as num?,
danmaku: json['danmaku'] == null
? null
: Danmaku.fromJson(json['danmaku'] as Map<String, dynamic>),

View File

@@ -58,6 +58,7 @@ class SpaceData {
List<Entry>? entry;
List<SpaceButtonList>? spaceButtonList;
int? relSpecial;
bool? hasItem;
SpaceData({
this.relation,
@@ -94,90 +95,95 @@ class SpaceData {
this.relSpecial,
});
factory SpaceData.fromJson(Map<String, dynamic> json) => SpaceData(
relation: json['relation'] as int?,
guestRelation: json['guest_relation'] as int?,
medal: json['medal'] as int?,
defaultTab: json['default_tab'] as String?,
isParams: json['is_params'] as bool?,
setting: json['setting'] == null
? null
: SpaceSetting.fromJson(json['setting'] as Map<String, dynamic>),
tab: json['tab'] == null
? null
: SpaceTab.fromJson(json['tab'] as Map<String, dynamic>),
card: json['card'] == null
? null
: SpaceCard.fromJson(json['card'] as Map<String, dynamic>),
images: json['images'] == null
? null
: SpaceImages.fromJson(json['images'] as Map<String, dynamic>),
live: json['live'] == null
? null
: Live.fromJson(json['live'] as Map<String, dynamic>),
elec: json['elec'] == null
? null
: Elec.fromJson(json['elec'] as Map<String, dynamic>),
archive: json['archive'] == null
? null
: Archive.fromJson(json['archive'] as Map<String, dynamic>),
series: json['series'] == null
? null
: SpaceSeries.fromJson(json['series'] as Map<String, dynamic>),
playGame: json['play_game'] == null
? null
: PlayGame.fromJson(json['play_game'] as Map<String, dynamic>),
article: json['article'] == null
? null
: Article.fromJson(json['article'] as Map<String, dynamic>),
season: json['season'] == null
? null
: SpaceSeason.fromJson(json['season'] as Map<String, dynamic>),
coinArchive: json['coin_archive'] == null
? null
: CoinArchive.fromJson(
json['coin_archive'] as Map<String, dynamic>),
likeArchive: json['like_archive'] == null
? null
: LikeArchive.fromJson(
json['like_archive'] as Map<String, dynamic>),
audios: json['audios'] == null
? null
: Audios.fromJson(json['audios'] as Map<String, dynamic>),
favourite2: json['favourite2'] == null
? null
: Favourite2.fromJson(json['favourite2'] as Map<String, dynamic>),
comic: json['comic'] == null
? null
: Comic.fromJson(json['comic'] as Map<String, dynamic>),
ugcSeason: json['ugc_season'] == null
? null
: UgcSeason.fromJson(json['ugc_season'] as Map<String, dynamic>),
cheese: json['cheese'] == null
? null
: Cheese.fromJson(json['cheese'] as Map<String, dynamic>),
guard: json['guard'] == null
? null
: Guard.fromJson(json['guard'] as Map<String, dynamic>),
attentionTip: json['attention_tip'] == null
? null
: AttentionTip.fromJson(
json['attention_tip'] as Map<String, dynamic>),
nftShowModule: json['nft_show_module'] == null
? null
: NftShowModule.fromJson(
json['nft_show_module'] as Map<String, dynamic>),
tab2: (json['tab2'] as List<dynamic>?)
?.map((e) => SpaceTab2.fromJson(e as Map<String, dynamic>))
.toList(),
nftFaceButton: json['nft_face_button'] as dynamic,
digitalButton: json['digital_button'] as dynamic,
entry: (json['entry'] as List<dynamic>?)
?.map((e) => Entry.fromJson(e as Map<String, dynamic>))
.toList(),
spaceButtonList: (json['space_button_list'] as List<dynamic>?)
?.map((e) => SpaceButtonList.fromJson(e as Map<String, dynamic>))
.toList(),
relSpecial: (json['rel_special'] as num?)?.toInt(),
);
SpaceData.fromJson(Map<String, dynamic> json) {
relation = json['relation'] as int?;
guestRelation = json['guest_relation'] as int?;
medal = json['medal'] as int?;
defaultTab = json['default_tab'] as String?;
isParams = json['is_params'] as bool?;
setting = json['setting'] == null
? null
: SpaceSetting.fromJson(json['setting'] as Map<String, dynamic>);
tab = json['tab'] == null
? null
: SpaceTab.fromJson(json['tab'] as Map<String, dynamic>);
card = json['card'] == null
? null
: SpaceCard.fromJson(json['card'] as Map<String, dynamic>);
images = json['images'] == null
? null
: SpaceImages.fromJson(json['images'] as Map<String, dynamic>);
live = json['live'] == null
? null
: Live.fromJson(json['live'] as Map<String, dynamic>);
elec = json['elec'] == null
? null
: Elec.fromJson(json['elec'] as Map<String, dynamic>);
archive = json['archive'] == null
? null
: Archive.fromJson(json['archive'] as Map<String, dynamic>);
series = json['series'] == null
? null
: SpaceSeries.fromJson(json['series'] as Map<String, dynamic>);
playGame = json['play_game'] == null
? null
: PlayGame.fromJson(json['play_game'] as Map<String, dynamic>);
article = json['article'] == null
? null
: Article.fromJson(json['article'] as Map<String, dynamic>);
season = json['season'] == null
? null
: SpaceSeason.fromJson(json['season'] as Map<String, dynamic>);
coinArchive = json['coin_archive'] == null
? null
: CoinArchive.fromJson(json['coin_archive'] as Map<String, dynamic>);
likeArchive = json['like_archive'] == null
? null
: LikeArchive.fromJson(json['like_archive'] as Map<String, dynamic>);
audios = json['audios'] == null
? null
: Audios.fromJson(json['audios'] as Map<String, dynamic>);
favourite2 = json['favourite2'] == null
? null
: Favourite2.fromJson(json['favourite2'] as Map<String, dynamic>);
comic = json['comic'] == null
? null
: Comic.fromJson(json['comic'] as Map<String, dynamic>);
ugcSeason = json['ugc_season'] == null
? null
: UgcSeason.fromJson(json['ugc_season'] as Map<String, dynamic>);
cheese = json['cheese'] == null
? null
: Cheese.fromJson(json['cheese'] as Map<String, dynamic>);
guard = json['guard'] == null
? null
: Guard.fromJson(json['guard'] as Map<String, dynamic>);
attentionTip = json['attention_tip'] == null
? null
: AttentionTip.fromJson(json['attention_tip'] as Map<String, dynamic>);
nftShowModule = json['nft_show_module'] == null
? null
: NftShowModule.fromJson(
json['nft_show_module'] as Map<String, dynamic>);
tab2 = (json['tab2'] as List<dynamic>?)
?.map((e) => SpaceTab2.fromJson(e as Map<String, dynamic>))
.toList();
nftFaceButton = json['nft_face_button'] as dynamic;
digitalButton = json['digital_button'] as dynamic;
entry = (json['entry'] as List<dynamic>?)
?.map((e) => Entry.fromJson(e as Map<String, dynamic>))
.toList();
spaceButtonList = (json['space_button_list'] as List<dynamic>?)
?.map((e) => SpaceButtonList.fromJson(e as Map<String, dynamic>))
.toList();
relSpecial = (json['rel_special'] as num?)?.toInt();
hasItem = archive?.item?.isNotEmpty == true ||
favourite2?.item?.isNotEmpty == true ||
coinArchive?.item?.isNotEmpty == true ||
likeArchive?.item?.isNotEmpty == true ||
article?.item?.isNotEmpty == true ||
audios?.item?.isNotEmpty == true ||
comic?.item?.isNotEmpty == true ||
season?.item?.isNotEmpty == true;
}
}

View File

@@ -5,7 +5,7 @@ class Stats {
int? dislike;
int? reply;
int? share;
int? coin;
num? coin;
int? dynam1c;
Stats({
@@ -26,7 +26,7 @@ class Stats {
dislike: json['dislike'] as int?,
reply: json['reply'] as int?,
share: json['share'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
dynam1c: json['dynamic'] as int?,
);
}

View File

@@ -22,7 +22,7 @@ class SpaceTab {
bool? opus;
bool? cheeseVideo;
bool? brand;
bool? hasItem;
// bool? hasItem;
SpaceTab({
this.archive,
@@ -74,6 +74,6 @@ class SpaceTab {
opus = json['opus'] as bool?;
cheeseVideo = json['cheese_video'] as bool?;
brand = json['brand'] as bool?;
hasItem = json.values.any((e) => e == true);
// hasItem = json.values.any((e) => e == true);
}
}

View File

@@ -5,7 +5,7 @@ class Stats {
int? dislike;
int? reply;
int? share;
int? coin;
num? coin;
int? dynam1c;
Stats({
@@ -26,7 +26,7 @@ class Stats {
dislike: json['dislike'] as int?,
reply: json['reply'] as int?,
share: json['share'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
dynam1c: json['dynamic'] as int?,
);
}

View File

@@ -21,7 +21,7 @@ class SpaceAudioItem {
int? limit;
int? activityId;
String? limitdesc;
int? coinNum;
num? coinNum;
int? ctime;
Statistic? statistic;
dynamic vipInfo;
@@ -78,7 +78,7 @@ class SpaceAudioItem {
limit: json['limit'] as int?,
activityId: json['activityId'] as int?,
limitdesc: json['limitdesc'] as String?,
coinNum: json['coin_num'] as int?,
coinNum: json['coin_num'] as num?,
ctime: json['ctime'] as int?,
statistic: json['statistic'] == null
? null

View File

@@ -1,6 +1,6 @@
class PgcTriple {
int? coin;
int? coinNumber;
num? coin;
num? coinNumber;
int? favorite;
int? fmid;
int? follow;

View File

@@ -4,7 +4,7 @@ class Stat {
int? danmaku;
int? reply;
int? favorite;
int? coin;
num? coin;
int? share;
int? nowRank;
int? hisRank;
@@ -35,7 +35,7 @@ class Stat {
danmaku: json['danmaku'] as int?,
reply: json['reply'] as int?,
favorite: json['favorite'] as int?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
share: json['share'] as int?,
nowRank: json['now_rank'] as int?,
hisRank: json['his_rank'] as int?,

View File

@@ -4,7 +4,7 @@ class VideoRelation {
bool? seasonFav;
bool? like;
bool? dislike;
int? coin;
num? coin;
VideoRelation({
this.attention,
@@ -21,6 +21,6 @@ class VideoRelation {
seasonFav: json['season_fav'] as bool?,
like: json['like'] as bool?,
dislike: json['dislike'] as bool?,
coin: json['coin'] as int?,
coin: json['coin'] as num?,
);
}

View File

@@ -5,6 +5,7 @@ import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/common/account_type.dart';
import 'package:PiliPlus/models/dynamics/article_content_model.dart'
show ArticleContentModel;
import 'package:PiliPlus/models/dynamics/result.dart';
@@ -12,7 +13,7 @@ import 'package:PiliPlus/models/model_avatar.dart';
import 'package:PiliPlus/models_new/article/article_info/data.dart';
import 'package:PiliPlus/models_new/article/article_view/data.dart';
import 'package:PiliPlus/pages/common/reply_controller.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/url_utils.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -161,7 +162,7 @@ class ArticleController extends ReplyController<MainListReply> {
}
if (isLoaded.value) {
queryData();
if (accountService.isLogin.value && !MineController.anonymity.value) {
if (Accounts.get(AccountType.heartbeat).isLogin && !Pref.historyPause) {
VideoHttp.historyReport(aid: commentId, type: 5);
}
}

View File

@@ -59,6 +59,7 @@ class _BlackListPageState extends State<BlackListPage> {
}
Widget _buildBody(LoadingState<List<BlackListItem>?> loadingState) {
late final style = TextStyle(color: Theme.of(context).colorScheme.outline);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
@@ -89,10 +90,9 @@ class _BlackListPageState extends State<BlackListPage> {
style: const TextStyle(fontSize: 14),
),
subtitle: Text(
DateUtil.dateFormat(item.mtime),
'添加时间: ${DateUtil.format(item.mtime, format: DateUtil.longFormatDs)}',
maxLines: 1,
style:
TextStyle(color: Theme.of(context).colorScheme.outline),
style: style,
overflow: TextOverflow.ellipsis,
),
dense: true,

View File

@@ -4,8 +4,7 @@ import 'package:PiliPlus/models_new/coin_log/data.dart';
import 'package:PiliPlus/models_new/coin_log/list.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
class MemberCoinLogController
extends CommonListController<CoinLogData, CoinLogItem> {
class CoinLogController extends CommonListController<CoinLogData, CoinLogItem> {
@override
void onInit() {
super.onInit();

View File

@@ -2,19 +2,19 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/coin_log/list.dart';
import 'package:PiliPlus/pages/member_coin_log/controller.dart';
import 'package:PiliPlus/pages/coin_log/controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MemberCoinLogPage extends StatefulWidget {
const MemberCoinLogPage({super.key});
class CoinLogPage extends StatefulWidget {
const CoinLogPage({super.key});
@override
State<MemberCoinLogPage> createState() => _MemberCoinLogPageState();
State<CoinLogPage> createState() => _CoinLogPageState();
}
class _MemberCoinLogPageState extends State<MemberCoinLogPage> {
late final _controller = Get.put(MemberCoinLogController());
class _CoinLogPageState extends State<CoinLogPage> {
late final _controller = Get.put(CoinLogController());
@override
Widget build(BuildContext context) {

View File

@@ -14,7 +14,7 @@ abstract class CommonIntroController extends GetxController {
// 是否点赞
RxBool hasLike = false.obs;
// 投币数量
final RxInt coinNum = 0.obs;
final RxNum coinNum = RxNum(0);
// 是否投币
bool get hasCoin => coinNum.value != 0;
// 是否收藏

View File

@@ -130,7 +130,10 @@ Widget module(
),
const SizedBox(width: 6),
Text(
DateUtil.dateFormat(orig.modules.moduleAuthor!.pubTs),
isSave
? DateUtil.format(orig.modules.moduleAuthor!.pubTs,
format: DateUtil.longFormatDs)
: DateUtil.dateFormat(orig.modules.moduleAuthor!.pubTs),
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelSmall!.fontSize),

View File

@@ -0,0 +1,21 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models_new/coin_log/data.dart';
import 'package:PiliPlus/models_new/coin_log/list.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
class ExpLogController extends CommonListController<CoinLogData, CoinLogItem> {
@override
void onInit() {
super.onInit();
queryData();
}
@override
List<CoinLogItem>? getDataList(CoinLogData response) {
return response.list;
}
@override
Future<LoadingState<CoinLogData>> customGetData() => UserHttp.expLog();
}

137
lib/pages/exp_log/view.dart Normal file
View File

@@ -0,0 +1,137 @@
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/coin_log/list.dart';
import 'package:PiliPlus/pages/exp_log/controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ExpLogPage extends StatefulWidget {
const ExpLogPage({super.key});
@override
State<ExpLogPage> createState() => _ExpLogPageState();
}
class _ExpLogPageState extends State<ExpLogPage> {
late final _controller = Get.put(ExpLogController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('经验记录')),
body: SafeArea(
top: false,
bottom: false,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 680),
child: CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.only(
left: 10,
right: 10,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
sliver: Obx(() => _buildBody(_controller.loadingState.value)),
),
],
),
),
),
),
);
}
Widget _buildBody(LoadingState<List<CoinLogItem>?> loadingState) {
return switch (loadingState) {
Loading() => linearLoading,
Success(:var response) => response?.isNotEmpty == true
? Builder(
builder: (context) {
final them = Theme.of(context);
final outline = them.colorScheme.outline.withValues(alpha: 0.1);
final divider = Divider(
height: 1,
color: outline,
);
final sliverDivider = SliverToBoxAdapter(
child: divider,
);
final dividerV = VerticalDivider(
width: 1,
color: outline,
);
return SliverMainAxisGroup(
slivers: [
sliverDivider,
SliverToBoxAdapter(
child: ColoredBox(
color: them.colorScheme.onInverseSurface,
child: _item(
const CoinLogItem(
time: '时间',
delta: '变化',
reason: '原因',
),
dividerV,
isHeader: true,
),
),
),
sliverDivider,
SliverList.separated(
itemCount: response!.length,
itemBuilder: (context, index) {
return _item(response[index], dividerV);
},
separatorBuilder: (context, index) => divider,
),
sliverDivider,
],
);
},
)
: HttpError(onReload: _controller.onReload),
Error(:var errMsg) => HttpError(
errMsg: errMsg,
onReload: _controller.onReload,
),
};
}
Widget _item(CoinLogItem item, Widget divider, {bool isHeader = false}) {
Widget text(int flex, String text) => Expanded(
flex: flex,
child: Padding(
padding: isHeader
? const EdgeInsets.symmetric(vertical: 6)
: const EdgeInsets.symmetric(vertical: 8),
child: Center(
child: Text(
text,
textAlign: TextAlign.center,
style: isHeader
? const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)
: const TextStyle(fontSize: 13),
),
),
),
);
Widget content = Row(
children: [
divider,
text(2, item.time),
divider,
text(1, item.delta),
divider,
text(2, item.reason),
divider,
],
);
return IntrinsicHeight(
child: isHeader ? content : SelectionArea(child: content),
);
}
}

View File

@@ -5,12 +5,12 @@ import 'package:PiliPlus/common/widgets/text_field/controller.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/live.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/common/account_type.dart';
import 'package:PiliPlus/models/common/video/live_quality.dart';
import 'package:PiliPlus/models_new/live/live_dm_info/data.dart';
import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart';
import 'package:PiliPlus/models_new/live/live_room_play_info/codec.dart';
import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/plugin/pl_player/models/data_source.dart';
import 'package:PiliPlus/services/account_service.dart';
@@ -52,6 +52,7 @@ class LiveRoomController extends GetxController {
List<RichTextItem>? savedDanmaku;
AccountService accountService = Get.find<AccountService>();
late final isLogin = accountService.isLogin.value;
LiveDmInfoData? dmInfo;
@@ -60,7 +61,7 @@ class LiveRoomController extends GetxController {
super.onInit();
roomId = int.parse(Get.parameters['roomid']!);
queryLiveInfoH5();
if (accountService.isLogin.value && !MineController.anonymity.value) {
if (Accounts.get(AccountType.heartbeat).isLogin && !Pref.historyPause) {
VideoHttp.roomEntryAction(roomId: roomId);
}
}
@@ -186,20 +187,22 @@ class LiveRoomController extends GetxController {
LiveHttp.liveRoomDanmaPrefetch(roomId: roomId).then((v) {
if (v['status']) {
if ((v['data'] as List?)?.isNotEmpty == true) {
messages.addAll((v['data'] as List)
.map((obj) => {
'name': obj['user']['base']['name'],
'uid': obj['user']['uid'],
'text': obj['text'],
'emots': obj['emots'],
'uemote': obj['emoticon']['emoticon_unique'] != ""
? obj['emoticon']
: null,
})
.toList());
WidgetsBinding.instance.addPostFrameCallback(
(_) => scrollToBottom(),
);
try {
messages.addAll((v['data'] as List)
.map((obj) => {
'name': obj['user']['base']['name'],
'uid': obj['user']['uid'],
'text': obj['text'],
'emots': obj['emots'],
'uemote': obj['emoticon']['emoticon_unique'] != ""
? obj['emoticon']
: null,
})
.toList());
WidgetsBinding.instance.addPostFrameCallback(
(_) => scrollToBottom(),
);
} catch (_) {}
}
}
});
@@ -267,34 +270,36 @@ class LiveRoomController extends GetxController {
.toList(),
)
..addEventListener((obj) {
if (obj['cmd'] == 'DANMU_MSG') {
// logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}');
final info = obj['info'];
final first = info[0];
final content = first[15];
final extra = jsonDecode(content['extra']);
final user = content['user'];
final uid = user['uid'];
messages.add({
'name': user['base']['name'],
'uid': uid,
'text': info[1],
'emots': extra['emots'],
'uemote': first[13],
});
if (showDanmaku) {
controller?.addDanmaku(
DanmakuContentItem(
extra['content'],
color: DmUtils.decimalToColor(extra['color']),
type: DmUtils.getPosition(extra['mode']),
selfSend: uid == accountService.mid,
),
);
WidgetsBinding.instance
.addPostFrameCallback((_) => scrollToBottom());
try {
if (obj['cmd'] == 'DANMU_MSG') {
// logger.i(' 原始弹幕消息 ======> ${jsonEncode(obj)}');
final info = obj['info'];
final first = info[0];
final content = first[15];
final extra = jsonDecode(content['extra']);
final user = content['user'];
final uid = user['uid'];
messages.add({
'name': user['base']['name'],
'uid': uid,
'text': info[1],
'emots': extra['emots'],
'uemote': first[13],
});
if (showDanmaku) {
controller?.addDanmaku(
DanmakuContentItem(
extra['content'],
color: DmUtils.decimalToColor(extra['color']),
type: DmUtils.getPosition(extra['mode']),
selfSend: isLogin && uid == accountService.mid,
),
);
WidgetsBinding.instance
.addPostFrameCallback((_) => scrollToBottom());
}
}
}
} catch (_) {}
})
..init();
}

View File

@@ -0,0 +1,22 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models_new/login_log/data.dart';
import 'package:PiliPlus/models_new/login_log/list.dart';
import 'package:PiliPlus/pages/common/common_list_controller.dart';
class LoginLogController
extends CommonListController<LoginLogData, LoginLogItem> {
@override
void onInit() {
super.onInit();
queryData();
}
@override
List<LoginLogItem>? getDataList(LoginLogData response) {
return response.list;
}
@override
Future<LoadingState<LoginLogData>> customGetData() => UserHttp.loginLog();
}

View File

@@ -0,0 +1,137 @@
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/login_log/list.dart';
import 'package:PiliPlus/pages/login_log/controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class LoginLogPage extends StatefulWidget {
const LoginLogPage({super.key});
@override
State<LoginLogPage> createState() => _LoginLogPageState();
}
class _LoginLogPageState extends State<LoginLogPage> {
late final _controller = Get.put(LoginLogController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('登录记录')),
body: SafeArea(
top: false,
bottom: false,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 680),
child: CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.only(
left: 10,
right: 10,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
sliver: Obx(() => _buildBody(_controller.loadingState.value)),
),
],
),
),
),
),
);
}
Widget _buildBody(LoadingState<List<LoginLogItem>?> loadingState) {
return switch (loadingState) {
Loading() => linearLoading,
Success(:var response) => response?.isNotEmpty == true
? Builder(
builder: (context) {
final them = Theme.of(context);
final outline = them.colorScheme.outline.withValues(alpha: 0.1);
final divider = Divider(
height: 1,
color: outline,
);
final sliverDivider = SliverToBoxAdapter(
child: divider,
);
final dividerV = VerticalDivider(
width: 1,
color: outline,
);
return SliverMainAxisGroup(
slivers: [
sliverDivider,
SliverToBoxAdapter(
child: ColoredBox(
color: them.colorScheme.onInverseSurface,
child: _item(
LoginLogItem(
timeAt: '时间',
ip: '变化',
geo: '地理位置',
),
dividerV,
isHeader: true,
),
),
),
sliverDivider,
SliverList.separated(
itemCount: response!.length,
itemBuilder: (context, index) {
return _item(response[index], dividerV);
},
separatorBuilder: (context, index) => divider,
),
sliverDivider,
],
);
},
)
: HttpError(onReload: _controller.onReload),
Error(:var errMsg) => HttpError(
errMsg: errMsg,
onReload: _controller.onReload,
),
};
}
Widget _item(LoginLogItem item, Widget divider, {bool isHeader = false}) {
Widget text(int flex, String text) => Expanded(
flex: flex,
child: Padding(
padding: isHeader
? const EdgeInsets.symmetric(vertical: 6)
: const EdgeInsets.symmetric(vertical: 8),
child: Center(
child: Text(
text,
textAlign: TextAlign.center,
style: isHeader
? const TextStyle(fontSize: 13, fontWeight: FontWeight.bold)
: const TextStyle(fontSize: 13),
),
),
),
);
Widget content = Row(
children: [
divider,
text(3, item.timeAt),
divider,
text(2, item.ip),
divider,
text(3, item.geo),
divider,
],
);
return IntrinsicHeight(
child: isHeader ? content : SelectionArea(child: content),
);
}
}

View File

@@ -85,7 +85,7 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
}
tab2?.retainWhere((item) => implTabs.contains(item.param));
if (tab2?.isNotEmpty == true) {
if (data.tab!.hasItem != true && tab2!.first.param == 'home') {
if (data.hasItem != true && tab2!.first.param == 'home') {
// remove empty home tab
tab2!.removeAt(0);
}

View File

@@ -4,9 +4,11 @@ import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/space/space/data.dart';
import 'package:PiliPlus/pages/coin_log/view.dart';
import 'package:PiliPlus/pages/exp_log/view.dart';
import 'package:PiliPlus/pages/login_log/view.dart';
import 'package:PiliPlus/pages/member/controller.dart';
import 'package:PiliPlus/pages/member/widget/user_info_card.dart';
import 'package:PiliPlus/pages/member_coin_log/view.dart';
import 'package:PiliPlus/pages/member_contribute/view.dart';
import 'package:PiliPlus/pages/member_dynamics/view.dart';
import 'package:PiliPlus/pages/member_favorite/view.dart';
@@ -158,16 +160,38 @@ class _MemberPageState extends State<MemberPage> {
),
),
PopupMenuItem(
onTap: () => Get.to(const MemberCoinLogPage()),
onTap: () => Get.to(const LoginLogPage()),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(FontAwesomeIcons.b, size: 18),
Icon(Icons.login, size: 18),
SizedBox(width: 10),
Text('登录记录'),
],
),
),
PopupMenuItem(
onTap: () => Get.to(const CoinLogPage()),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(FontAwesomeIcons.b, size: 16),
SizedBox(width: 10),
Text('硬币记录'),
],
),
),
PopupMenuItem(
onTap: () => Get.to(const ExpLogPage()),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.linear_scale, size: 18),
SizedBox(width: 10),
Text('经验记录'),
],
),
),
PopupMenuItem(
onTap: () => Get.toNamed('/spaceSetting'),
child: const Row(

View File

@@ -131,6 +131,8 @@ class _EditProfilePageState extends State<EditProfilePage> {
padding: const EdgeInsets.symmetric(vertical: 5),
child: ClipOval(
child: CachedNetworkImage(
width: 55,
height: 55,
imageUrl: ImageUtil.thumbnailUrl(response.face),
),
),
@@ -175,7 +177,7 @@ class _EditProfilePageState extends State<EditProfilePage> {
onTap: () => showDatePicker(
context: context,
initialDate: DateTime.parse(response.birthday!),
firstDate: DateTime(1900, 1, 1),
firstDate: DateTime(0001, 1, 1),
lastDate: DateTime.now(),
).then((res) {
if (res != null) {
@@ -190,11 +192,11 @@ class _EditProfilePageState extends State<EditProfilePage> {
_item(
theme: theme,
title: '个性签名',
text: response.sign.isNullOrEmpty ? '' : response.sign!,
text: response.sign,
onTap: () => _editDialog(
type: ProfileType.sign,
title: '个性签名',
text: response.sign!,
text: response.sign ?? '',
),
),
divider1,
@@ -273,6 +275,7 @@ class _EditProfilePageState extends State<EditProfilePage> {
required String text,
}) {
_textController.text = text;
final lines = type == ProfileType.uname ? 1 : 4;
showDialog(
context: context,
builder: (BuildContext context) {
@@ -281,8 +284,8 @@ class _EditProfilePageState extends State<EditProfilePage> {
title: Text('修改$title'),
content: TextField(
controller: _textController,
minLines: type == ProfileType.uname ? 1 : 4,
maxLines: type == ProfileType.uname ? 1 : 4,
minLines: lines,
maxLines: lines,
autofocus: true,
style: const TextStyle(fontSize: 14),
textInputAction:
@@ -427,18 +430,20 @@ class _EditProfilePageState extends State<EditProfilePage> {
fontWeight: FontWeight.normal,
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
title: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (text != null)
Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: theme.colorScheme.outline,
Flexible(
child: Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: theme.colorScheme.outline,
),
),
)
else if (widget != null)
@@ -448,6 +453,8 @@ class _EditProfilePageState extends State<EditProfilePage> {
Icons.keyboard_arrow_right,
color: theme.colorScheme.outline,
)
else
const SizedBox(width: 24)
],
),
);

View File

@@ -368,10 +368,9 @@ class _SavePanelState extends State<SavePanel> {
if (pubdate != null) ...[
const Spacer(),
Text(
DateTime.fromMillisecondsSinceEpoch(
pubdate! * 1000)
.toString()
.substring(0, 19),
DateUtil.format(pubdate,
format:
DateUtil.longFormatDs),
style: TextStyle(
color:
theme.colorScheme.outline,

View File

@@ -238,7 +238,8 @@ class ReplyItemGrpc extends StatelessWidget {
children: <Widget>[
Text(
replyLevel == 0
? DateUtil.longFormatDs.format(DateTime.now())
? DateUtil.format(replyItem.ctime.toInt(),
format: DateUtil.longFormatDs)
: DateUtil.dateFormat(replyItem.ctime.toInt()),
style: TextStyle(
fontSize: theme.textTheme.labelSmall!.fontSize,
@@ -290,7 +291,6 @@ class ReplyItemGrpc extends StatelessWidget {
),
),
if (replyItem.content.pictures.isNotEmpty) ...[
const SizedBox(height: 4),
Padding(
padding: padding,
child: LayoutBuilder(
@@ -309,6 +309,7 @@ class ReplyItemGrpc extends StatelessWidget {
),
),
),
const SizedBox(height: 4),
],
if (replyLevel != 0) ...[
const SizedBox(height: 4),

View File

@@ -498,7 +498,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
child: Scaffold(
resizeToAvoidBottomInset: false,
key: videoDetailController.scaffoldKey,
appBar: removeSafeArea
appBar: (removeSafeArea || isFullScreen)
? null
: PreferredSize(
preferredSize: const Size.fromHeight(0),
@@ -1122,7 +1122,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
Scaffold(
resizeToAvoidBottomInset: false,
key: videoDetailController.scaffoldKey,
appBar: removeSafeArea
appBar: (removeSafeArea || isFullScreen)
? null
: AppBar(
backgroundColor: Colors.black,
@@ -1131,7 +1131,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
body: SafeArea(
left: !removeSafeArea && !isFullScreen,
right: !removeSafeArea && !isFullScreen,
top: !removeSafeArea,
top: !removeSafeArea && !isFullScreen,
bottom: false,
child: childWhenDisabledLandscapeInner,
),
@@ -1142,7 +1142,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
Widget get childWhenDisabledAlmostSquare => Scaffold(
resizeToAvoidBottomInset: false,
key: videoDetailController.scaffoldKey,
appBar: removeSafeArea
appBar: (removeSafeArea || isFullScreen)
? null
: AppBar(
backgroundColor: Colors.black,
@@ -1151,7 +1151,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
body: SafeArea(
left: !removeSafeArea && !isFullScreen,
right: !removeSafeArea && !isFullScreen,
top: !removeSafeArea,
top: !removeSafeArea && !isFullScreen,
bottom: false,
child: childWhenDisabledAlmostSquareInner,
),

View File

@@ -30,6 +30,13 @@ class WhisperSessionItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final resource =
item.sessionInfo.avatar.fallbackLayers.layers.first.resource;
final avatar = resource.hasResImage()
? resource.resImage.imageSrc.remote.url
: resource.hasResAnimation()
? resource.resAnimation.webpSrc.remote.url
: resource.resNativeDraw.drawSrc.remote.url;
Map? vipInfo = item.sessionInfo.hasVipInfo()
? jsonDecode(item.sessionInfo.vipInfo)
: null;
@@ -100,8 +107,7 @@ class WhisperSessionItem extends StatelessWidget {
arguments: {
'talkerId': item.id.privateId.talkerUid.toInt(),
'name': item.sessionInfo.sessionName,
'face': item.sessionInfo.avatar.fallbackLayers.layers.first
.resource.resImage.imageSrc.remote.url,
'face': avatar,
if (item.sessionInfo.avatar.hasMid())
'mid': item.sessionInfo.avatar.mid.toInt(),
},
@@ -159,8 +165,7 @@ class WhisperSessionItem extends StatelessWidget {
child: PendantAvatar(
size: 42,
badgeSize: 14,
avatar: item.sessionInfo.avatar.fallbackLayers.layers.first
.resource.resImage.imageSrc.remote.url,
avatar: avatar,
garbPendantImage:
pendant?.resImage.imageSrc.remote.hasUrl() == true
? pendant!.resImage.imageSrc.remote.url

View File

@@ -463,8 +463,7 @@ class PlPlayerController {
// 添加一个私有构造函数
PlPlayerController._() {
if (!Accounts.get(AccountType.heartbeat).isLogin ||
GStorage.localCache.get(LocalCacheKey.historyPause) == true) {
if (!Accounts.get(AccountType.heartbeat).isLogin || Pref.historyPause) {
enableHeart = false;
}

View File

@@ -438,6 +438,7 @@ class PiliScheme {
return false;
// bilibili://browser/?url=https%3A%2F%2Fwww.bilibili.com%2F
case 'browser':
if (selfHandle) return false;
final url = uri.queryParameters['url'];
if (url != null) {
_toWebview(url, off, parameters);

View File

@@ -722,4 +722,7 @@ class Pref {
static bool get directExitOnBack =>
_setting.get(SettingBoxKey.directExitOnBack, defaultValue: false);
static bool get historyPause =>
_localCache.get(LocalCacheKey.historyPause, defaultValue: false);
}