Compare commits

..

8 Commits

Author SHA1 Message Date
bggRGjQaUbCoE
7439160f03 fix imageview
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-06 12:21:18 +08:00
bggRGjQaUbCoE
b496ea4da4 opt insert rich text
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-05 13:12:39 +08:00
bggRGjQaUbCoE
0f1665bf08 fix update vote
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-04 23:17:56 +08:00
dom
83459df3b7 feat: create vote (#871)
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-04 22:10:11 +08:00
bggRGjQaUbCoE
9ce84fb997 fix vote
fix filter dyn

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-04 17:36:54 +08:00
bggRGjQaUbCoE
708bf27710 remove silence endtime
deprecated

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-04 12:00:04 +08:00
bggRGjQaUbCoE
dae64e74d5 upgrade deps
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-03 18:07:28 +08:00
bggRGjQaUbCoE
8414c0f71f tweak
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-03 15:56:32 +08:00
29 changed files with 894 additions and 153 deletions

View File

@@ -47,6 +47,7 @@
## feat
- [x] 发起投票
- [x] 发布动态/评论支持`富文本编辑`/`表情显示`/`@用户`
- [x] 修改消息设置
- [x] 修改聊天设置

View File

@@ -12,28 +12,31 @@ import 'package:flutter/material.dart';
class ImageModel {
ImageModel({
required this.width,
required this.height,
required num? width,
required num? height,
required this.url,
this.liveUrl,
});
}) {
this.width = width == null || width == 0 ? 1 : width;
this.height = height == null || height == 0 ? 1 : height;
}
dynamic width;
dynamic height;
late num width;
late num height;
String url;
String? liveUrl;
bool? _isLongPic;
bool? _isLivePhoto;
dynamic get safeWidth => width ?? 1;
dynamic get safeHeight => height ?? 1;
bool get isLongPic => _isLongPic ??= (safeHeight / safeWidth) > (22 / 9);
bool get isLongPic => _isLongPic ??= (height / width) > _maxRatio;
bool get isLivePhoto =>
_isLivePhoto ??= enableLivePhoto && liveUrl?.isNotEmpty == true;
static bool enableLivePhoto = Pref.enableLivePhoto;
}
const double _maxRatio = 22 / 9;
Widget imageView(
double maxWidth,
List<ImageModel> picArr, {
@@ -44,17 +47,16 @@ Widget imageView(
double imageWidth = (maxWidth - 2 * 5) / 3;
double imageHeight = imageWidth;
if (picArr.length == 1) {
dynamic width = picArr[0].safeWidth;
dynamic height = picArr[0].safeHeight;
dynamic width = picArr[0].width;
dynamic height = picArr[0].height;
double ratioWH = width / height;
double ratioHW = height / width;
double maxRatio = 22 / 9;
imageWidth = ratioWH > 1.5
? maxWidth
: (ratioWH >= 1 || (height > width && ratioHW < 1.5))
? 2 * imageWidth
: 1.5 * imageWidth;
imageHeight = imageWidth * min(ratioHW, maxRatio);
imageHeight = imageWidth * min(ratioHW, _maxRatio);
} else if (picArr.length == 2) {
imageWidth = imageHeight = 2 * imageWidth;
}
@@ -144,7 +146,7 @@ Widget imageView(
width: imageWidth,
height: imageHeight,
isLongPic: () => item.isLongPic,
callback: () => item.safeWidth <= item.safeHeight,
callback: () => item.width <= item.height,
getPlaceHolder: () {
return Container(
width: imageWidth,

View File

@@ -26,7 +26,7 @@ import 'package:flutter/services.dart';
/// created by bggRGjQaUbCoE on 2025/6/27
///
enum RichTextType { text, composing, at, emoji }
enum RichTextType { text, composing, at, emoji, vote }
class Emote {
late String url;
@@ -43,20 +43,20 @@ class Emote {
mixin RichTextTypeMixin {
RichTextType get type;
Emote? get emote;
String? get uid;
String? get id;
String? get rawText;
}
extension TextEditingDeltaExt on TextEditingDelta {
({RichTextType type, String? rawText, Emote? emote, String? uid}) get config {
({RichTextType type, String? rawText, Emote? emote, String? id}) get config {
if (this case RichTextTypeMixin e) {
return (type: e.type, rawText: e.rawText, emote: e.emote, uid: e.uid);
return (type: e.type, rawText: e.rawText, emote: e.emote, id: e.id);
}
return (
type: composing.isValid ? RichTextType.composing : RichTextType.text,
rawText: null,
emote: null,
uid: null
id: null
);
}
@@ -82,7 +82,7 @@ class RichTextEditingDeltaInsertion extends TextEditingDeltaInsertion
required super.composing,
RichTextType? type,
this.emote,
this.uid,
this.id,
this.rawText,
}) {
this.type = type ??
@@ -96,7 +96,7 @@ class RichTextEditingDeltaInsertion extends TextEditingDeltaInsertion
final Emote? emote;
@override
final String? uid;
final String? id;
@override
final String? rawText;
@@ -112,7 +112,7 @@ class RichTextEditingDeltaReplacement extends TextEditingDeltaReplacement
required super.composing,
RichTextType? type,
this.emote,
this.uid,
this.id,
this.rawText,
}) {
this.type = type ??
@@ -126,7 +126,7 @@ class RichTextEditingDeltaReplacement extends TextEditingDeltaReplacement
final Emote? emote;
@override
final String? uid;
final String? id;
@override
final String? rawText;
@@ -138,7 +138,7 @@ class RichTextItem {
String? _rawText;
late TextRange range;
Emote? emote;
String? uid;
String? id;
String get rawText => _rawText ?? text;
@@ -146,7 +146,7 @@ class RichTextItem {
bool get isComposing => type == RichTextType.composing;
bool get isRich => type == RichTextType.at || type == RichTextType.emoji;
bool get isRich => !isText && !isComposing;
RichTextItem({
this.type = RichTextType.text,
@@ -154,7 +154,7 @@ class RichTextItem {
String? rawText,
required this.range,
this.emote,
this.uid,
this.id,
}) {
_rawText = rawText;
}
@@ -164,7 +164,7 @@ class RichTextItem {
String? rawText,
this.type = RichTextType.text,
this.emote,
this.uid,
this.id,
}) {
range = TextRange(start: 0, end: text.length);
_rawText = rawText;
@@ -198,7 +198,7 @@ class RichTextItem {
rawText: config.rawText,
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
);
return [insertedItem];
}
@@ -224,7 +224,7 @@ class RichTextItem {
final insertedItem = RichTextItem(
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
text: delta.textInserted,
rawText: config.rawText,
range: TextRange(start: insertionOffset, end: end),
@@ -251,7 +251,7 @@ class RichTextItem {
final insertedItem = RichTextItem(
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
text: delta.textInserted,
rawText: config.rawText,
range: TextRange(start: insertionOffset, end: insertEnd),
@@ -397,7 +397,7 @@ class RichTextItem {
final insertedItem = RichTextItem(
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
text: delta.replacementText,
rawText: config.rawText,
range: TextRange(
@@ -427,7 +427,7 @@ class RichTextItem {
text = delta.replacementText;
type = config.type;
emote = config.emote;
uid = config.uid;
id = config.id;
final end = range.start + text.length;
range = TextRange(start: range.start, end: end);
controller.newSelection = TextSelection.collapsed(offset: end);
@@ -441,7 +441,7 @@ class RichTextItem {
_rawText = config.rawText;
type = config.type;
emote = config.emote;
uid = config.uid;
id = config.id;
final end = range.start + text.length;
range = TextRange(start: range.start, end: end);
controller.newSelection = TextSelection.collapsed(offset: end);
@@ -476,7 +476,7 @@ class RichTextItem {
rawText: config.rawText,
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
range: TextRange(start: replacedRange.start, end: end),
);
controller.newSelection = TextSelection.collapsed(offset: end);
@@ -487,7 +487,7 @@ class RichTextItem {
final config = delta.config;
type = config.type;
emote = config.emote;
uid = config.uid;
id = config.id;
final end = range.start + text.length;
range = TextRange(start: range.start, end: end);
controller.newSelection = TextSelection.collapsed(offset: end);
@@ -523,7 +523,7 @@ class RichTextItem {
rawText: config.rawText,
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
range: TextRange(start: range.start, end: end),
);
controller.newSelection = TextSelection.collapsed(offset: end);
@@ -536,7 +536,7 @@ class RichTextItem {
final config = delta.config;
type = config.type;
emote = config.emote;
uid = config.uid;
id = config.id;
final end = range.start + text.length;
range = TextRange(start: range.start, end: end);
controller.newSelection = TextSelection.collapsed(offset: end);
@@ -622,7 +622,7 @@ class RichTextEditingController extends TextEditingController {
rawText: config.rawText,
type: config.type,
emote: config.emote,
uid: config.uid,
id: config.id,
),
);
newSelection =
@@ -732,7 +732,7 @@ class RichTextEditingController extends TextEditingController {
// break;
// }
// }
// debugPrint('isValid: $isValid');
// debugPrint('isValid: $isValid,,${text.length},,${plainText.length}');
// debugPrint('$items\n$selection');
return TextSpan(
@@ -763,6 +763,7 @@ class RichTextEditingController extends TextEditingController {
final emote = e.emote;
if (emote != null) {
return WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: NetworkImgLayer(
@@ -776,6 +777,25 @@ class RichTextEditingController extends TextEditingController {
);
}
return TextSpan(text: e.text);
case RichTextType.vote:
richStyle ??= (style ?? const TextStyle())
.copyWith(color: Theme.of(context).colorScheme.primary);
return TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.bar_chart_rounded,
size: 22,
color: richStyle!.color,
),
),
TextSpan(
text: '${e.rawText} ',
style: richStyle,
),
],
);
}
}).toList(),
);
@@ -916,7 +936,7 @@ class RichTextEditingController extends TextEditingController {
}
if (offset > range.start && offset < range.end) {
if (e.isRich) {
if (offset < value.selection.baseOffset) {
if (offset < selection.baseOffset) {
return newSelection.copyWith(
baseOffset: range.start, extentOffset: range.start);
} else {

View File

@@ -3251,7 +3251,7 @@ class EditableTextState extends State<EditableText>
// to make sure the user can see the changes they just made. Programmatic
// changes to `textEditingValue` do not trigger the behavior even if the
// text field is focused.
_scheduleShowCaretOnScreen(withAnimation: true);
scheduleShowCaretOnScreen(withAnimation: true);
}
bool _checkNeedsAdjustAffinity(TextEditingValue value) {
@@ -4119,7 +4119,7 @@ class EditableTextState extends State<EditableText>
bool _showCaretOnScreenScheduled = false;
void _scheduleShowCaretOnScreen({required bool withAnimation}) {
void scheduleShowCaretOnScreen({required bool withAnimation}) {
if (_showCaretOnScreenScheduled) {
return;
}
@@ -4217,7 +4217,7 @@ class EditableTextState extends State<EditableText>
if (_lastBottomViewInset < view.viewInsets.bottom) {
// Because the metrics change signal from engine will come here every frame
// (on both iOS and Android). So we don't need to show caret with animation.
_scheduleShowCaretOnScreen(withAnimation: false);
scheduleShowCaretOnScreen(withAnimation: false);
}
}
_lastBottomViewInset = view.viewInsets.bottom;
@@ -4518,7 +4518,7 @@ class EditableTextState extends State<EditableText>
WidgetsBinding.instance.addObserver(this);
_lastBottomViewInset = View.of(context).viewInsets.bottom;
if (!widget.readOnly) {
_scheduleShowCaretOnScreen(withAnimation: true);
scheduleShowCaretOnScreen(withAnimation: true);
}
final TextSelection? updatedSelection = _adjustedSelectionWhenFocused();
if (updatedSelection != null) {
@@ -4747,7 +4747,7 @@ class EditableTextState extends State<EditableText>
final bool shouldShowCaret =
widget.readOnly ? _value.selection != value.selection : _value != value;
if (shouldShowCaret) {
_scheduleShowCaretOnScreen(withAnimation: true);
scheduleShowCaretOnScreen(withAnimation: true);
}
// Even if the value doesn't change, it may be necessary to focus and build

View File

@@ -82,12 +82,11 @@ typedef InputCounterWidgetBuilder = Widget? Function(
class _TextFieldSelectionGestureDetectorBuilder
extends TextSelectionGestureDetectorBuilder {
_TextFieldSelectionGestureDetectorBuilder(
{required _RichTextFieldState state})
_TextFieldSelectionGestureDetectorBuilder({required RichTextFieldState state})
: _state = state,
super(delegate: state);
final _RichTextFieldState _state;
final RichTextFieldState _state;
@override
bool get onUserTapAlwaysCalled => _state.widget.onTapAlwaysCalled;
@@ -961,7 +960,7 @@ class RichTextField extends StatefulWidget {
}
@override
State<RichTextField> createState() => _RichTextFieldState();
State<RichTextField> createState() => RichTextFieldState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
@@ -1150,7 +1149,7 @@ class RichTextField extends StatefulWidget {
}
}
class _RichTextFieldState extends State<RichTextField>
class RichTextFieldState extends State<RichTextField>
with RestorationMixin
implements TextSelectionGestureDetectorBuilderDelegate, AutofillClient {
// RestorableRichTextEditingController? _controller;
@@ -1912,6 +1911,10 @@ class _RichTextFieldState extends State<RichTextField>
),
);
}
void scheduleShowCaretOnScreen({required bool withAnimation}) {
_editableText?.scheduleShowCaretOnScreen(withAnimation: withAnimation);
}
}
TextStyle? _m2StateInputStyle(BuildContext context) =>

View File

@@ -917,4 +917,8 @@ class Api {
static const String spaceAudio = '/audio/music-service/web/song/upper';
static const String dynMention = '/x/polymer/web-dynamic/v1/mention/search';
static const String createVote = '/x/vote/create';
static const String updateVote = '/x/vote/update';
}

View File

@@ -510,4 +510,30 @@ class DynamicsHttp {
return Error(res.data['message']);
}
}
static Future<LoadingState<int?>> createVote(VoteInfo voteInfo) async {
final res = await Request().post(
Api.createVote,
queryParameters: {'csrf': Accounts.main.csrf},
data: {'vote_info': voteInfo.toJson()},
);
if (res.data['code'] == 0) {
return Success(res.data['data']?['vote_id']);
} else {
return Error(res.data['message']);
}
}
static Future<LoadingState<int?>> updateVote(VoteInfo voteInfo) async {
final res = await Request().post(
Api.updateVote,
queryParameters: {'csrf': Accounts.main.csrf},
data: {'vote_info': voteInfo.toJson()},
);
if (res.data['code'] == 0) {
return Success(res.data['data']?['vote_id']);
} else {
return Error(res.data['message']);
}
}
}

View File

@@ -168,7 +168,7 @@ class MsgHttp {
}
static Future uploadBfs({
dynamic path,
required String path,
String? category,
String? biz,
CancelToken? cancelToken,

View File

@@ -41,12 +41,12 @@ class DynamicsDataModel {
continue;
}
if (enableFilter &&
banWordForDyn.hasMatch(
item.orig?.modules.moduleDynamic?.major?.opus?.summary?.text ??
item.modules.moduleDynamic?.major?.opus?.summary?.text ??
item.orig?.modules.moduleDynamic?.desc?.text ??
item.modules.moduleDynamic?.desc?.text ??
'')) {
banWordForDyn.hasMatch((item.orig?.modules.moduleDynamic?.major
?.opus?.summary?.text ??
'') +
(item.modules.moduleDynamic?.major?.opus?.summary?.text ?? '') +
(item.orig?.modules.moduleDynamic?.desc?.text ?? '') +
(item.modules.moduleDynamic?.desc?.text ?? ''))) {
continue;
}
if (filterBan &&

View File

@@ -8,6 +8,17 @@ class SimpleVoteInfo {
int? voteId;
late int joinNum;
SimpleVoteInfo({
this.choiceCnt,
this.defaultShare,
this.desc,
this.endTime,
this.status,
this.uid,
this.voteId,
this.joinNum = 0,
});
SimpleVoteInfo.fromJson(Map<String, dynamic> json) {
choiceCnt = json['choice_cnt'];
defaultShare = json['default_share'];
@@ -29,6 +40,34 @@ class VoteInfo extends SimpleVoteInfo {
int? voterLevel;
String? face;
String? name;
// 0 文字, 1 图片
int? type;
int? votePublisher;
int? duration;
int? onlyFansLevel;
VoteInfo({
super.choiceCnt,
super.defaultShare,
super.desc,
super.endTime,
super.status,
super.uid,
super.voteId,
super.joinNum = 0,
this.title,
this.ctime,
this.myVotes,
List<Option>? options,
this.optionsCnt,
this.voterLevel,
this.face,
this.name,
this.type,
this.votePublisher,
this.duration,
this.onlyFansLevel,
}) : options = options ?? <Option>[];
VoteInfo.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
title = json['title'];
@@ -42,22 +81,48 @@ class VoteInfo extends SimpleVoteInfo {
voterLevel = json['voter_level'];
face = json['face'];
name = json['name'];
type = json['type'];
votePublisher = json['vote_publisher'];
}
factory VoteInfo.fromSeparatedJson(Map<String, dynamic> json) {
return VoteInfo.fromJson(json['vote_info'])
..myVotes = (json['my_votes'] as List?)?.cast(); // voteInfo
}
Map<String, dynamic> toJson() => {
'title': title,
'desc': desc,
'type': type,
'choice_cnt': choiceCnt,
'duration': duration,
'options': options.map((e) => e.toJson()).toList(),
'only_fans_level': onlyFansLevel,
'vote_publisher': votePublisher,
if (voteId != null) 'vote_id': voteId,
};
}
class Option {
int? optidx;
String? optdesc;
int? optIdx;
String? optDesc;
late int cnt;
String? imgUrl;
Option({
this.optDesc,
this.imgUrl,
});
Option.fromJson(Map<String, dynamic> json) {
optidx = json['opt_idx'];
optdesc = json['opt_desc'];
optIdx = json['opt_idx'];
optDesc = json['opt_desc'];
cnt = json['cnt'] ?? 0;
imgUrl = json['img_url'];
}
Map<String, dynamic> toJson() => {
'opt_desc': optDesc,
'img_url': imgUrl,
};
}

View File

@@ -48,7 +48,9 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
if (Platform.isAndroid) {
WidgetsBinding.instance.addObserver(this);
}
initPubState();
@@ -68,7 +70,9 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
}
focusNode.dispose();
editController.dispose();
WidgetsBinding.instance.removeObserver(this);
if (Platform.isAndroid) {
WidgetsBinding.instance.removeObserver(this);
}
super.dispose();
}
@@ -83,6 +87,7 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
if (mounted &&
widget.autofocus &&
panelType.value == PanelType.keyboard) {
controller.restoreChatPanel();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (focusNode.hasFocus) {
focusNode.unfocus();

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/text_field/controller.dart';
import 'package:PiliPlus/common/widgets/text_field/text_field.dart';
import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/models_new/dynamic/dyn_mention/item.dart';
import 'package:PiliPlus/models_new/emote/emote.dart' as e;
@@ -31,6 +32,8 @@ abstract class CommonRichTextPubPage
abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
extends CommonPublishPageState<T> {
final key = GlobalKey<RichTextFieldState>();
@override
late final RichTextEditingController editController =
RichTextEditingController(
@@ -66,9 +69,9 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
clipBehavior: Clip.none,
children: [
GestureDetector(
onTap: () {
onTap: () async {
controller.keepChatPanel();
context.imageView(
await context.imageView(
imgList: pathList
.map((path) => SourceModel(
url: path,
@@ -77,6 +80,7 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
.toList(),
initialPage: index,
);
controller.restoreChatPanel();
},
onLongPress: onClear,
child: ClipRRect(
@@ -193,31 +197,47 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
List<Map<String, dynamic>>? getRichContent() {
if (editController.items.isEmpty) return null;
return editController.items.map((e) {
return switch (e.type) {
RichTextType.text || RichTextType.composing => <String, dynamic>{
final list = <Map<String, dynamic>>[];
for (var e in editController.items) {
switch (e.type) {
case RichTextType.text || RichTextType.composing:
list.add({
"raw_text": e.text,
"type": 1,
"biz_id": "",
},
RichTextType.at => <String, dynamic>{
});
case RichTextType.at:
list.add({
"raw_text": '@${e.rawText}',
"type": 2,
"biz_id": e.uid,
},
RichTextType.emoji => <String, dynamic>{
"biz_id": e.id,
});
case RichTextType.emoji:
list.add({
"raw_text": e.rawText,
"type": 9,
"biz_id": "",
},
};
}).toList();
});
case RichTextType.vote:
list.add({
"raw_text": e.rawText,
"type": 4,
"biz_id": e.id,
});
list.add({
"raw_text": ' ',
"type": 1,
"biz_id": "",
});
}
}
return list;
}
double _mentionOffset = 0;
void onMention([bool fromClick = false]) {
Future<void> onMention([bool fromClick = false]) async {
controller.keepChatPanel();
DynMentionPanel.onDynMention(
await DynMentionPanel.onDynMention(
context,
offset: _mentionOffset,
callback: (offset) => _mentionOffset = offset,
@@ -227,11 +247,12 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
'@${res.name} ',
RichTextType.at,
rawText: res.name,
uid: res.uid,
id: res.uid,
fromClick: fromClick,
);
}
});
controller.restoreChatPanel();
}
void onInsertText(
@@ -239,7 +260,7 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
RichTextType type, {
String? rawText,
Emote? emote,
String? uid,
String? id,
bool? fromClick,
}) {
if (text.isEmpty) {
@@ -268,7 +289,7 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
rawText: rawText,
type: type,
emote: emote,
uid: uid,
id: id,
);
} else {
delta = RichTextEditingDeltaInsertion(
@@ -282,7 +303,7 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
rawText: rawText,
type: type,
emote: emote,
uid: uid,
id: id,
);
}
} else {
@@ -297,7 +318,7 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
rawText: rawText,
type: type,
emote: emote,
uid: uid,
id: id,
);
}
@@ -308,13 +329,9 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
}
editController
..value = newValue
..syncRichText(delta);
..syncRichText(delta)
..value = newValue;
} else {
editController.value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
);
editController.items
..clear()
..add(
@@ -327,10 +344,16 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
end: text.length,
),
emote: emote,
uid: uid,
id: id,
),
);
editController.value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
);
}
key.currentState?.scheduleShowCaretOnScreen(withAnimation: true);
}
@override

View File

@@ -91,28 +91,30 @@ class _VotePanelState extends State<VotePanel> {
if (_enabled)
Padding(
padding: const EdgeInsets.only(top: 16),
child: OutlinedButton(
onPressed: groupValue.isNotEmpty
? () async {
final res = await widget.callback(
groupValue,
anonymity,
);
if (res.isSuccess) {
if (mounted) {
setState(() {
_enabled = false;
_showPercentage = true;
_voteInfo = res.data;
_percentage = _cnt2Percentage(_voteInfo.options);
});
child: Obx(
() => OutlinedButton(
onPressed: groupValue.isNotEmpty
? () async {
final res = await widget.callback(
groupValue,
anonymity,
);
if (res.isSuccess) {
if (mounted) {
setState(() {
_enabled = false;
_showPercentage = true;
_voteInfo = res.data;
_percentage = _cnt2Percentage(_voteInfo.options);
});
}
} else {
res.toast();
}
} else {
res.toast();
}
}
: null,
child: const Center(child: Text('投票')),
: null,
child: const Center(child: Text('投票')),
),
),
),
],
@@ -144,14 +146,14 @@ class _VotePanelState extends State<VotePanel> {
child: Builder(
builder: (context) {
final opt = _voteInfo.options[index];
final selected = groupValue.contains(opt.optidx);
final selected = groupValue.contains(opt.optIdx);
return PercentageChip(
label: opt.optdesc!,
label: opt.optDesc!,
percentage: _showPercentage ? _percentage[index] : null,
selected: selected,
onSelected: !_enabled || (groupValue.length >= _maxCnt && !selected)
? null
: (value) => _onSelected(context, value, opt.optidx!),
: (value) => _onSelected(context, value, opt.optIdx!),
);
},
),

View File

@@ -5,12 +5,15 @@ import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/common/widgets/draggable_sheet/draggable_scrollable_sheet_dyn.dart'
as dyn_sheet;
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/common/widgets/text_field/controller.dart';
import 'package:PiliPlus/common/widgets/text_field/text_field.dart';
import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/models/common/publish_panel_type.dart';
import 'package:PiliPlus/models/common/reply/reply_option_type.dart';
import 'package:PiliPlus/models/dynamics/vote_model.dart';
import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/topic_item.dart';
import 'package:PiliPlus/pages/common/publish/common_rich_text_pub_page.dart';
import 'package:PiliPlus/pages/dynamics_create_vote/view.dart';
import 'package:PiliPlus/pages/dynamics_mention/controller.dart';
import 'package:PiliPlus/pages/dynamics_select_topic/controller.dart';
import 'package:PiliPlus/pages/dynamics_select_topic/view.dart';
@@ -341,7 +344,6 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
return PopupMenuButton<bool>(
requestFocus: false,
initialValue: _isPrivate.value,
onOpened: controller.keepChatPanel,
onSelected: (value) => _isPrivate.value = value,
itemBuilder: (context) => List.generate(
2,
@@ -398,7 +400,6 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
return PopupMenuButton<ReplyOptionType>(
requestFocus: false,
initialValue: _replyOption.value,
onOpened: controller.keepChatPanel,
onSelected: (item) => _replyOption.value = item,
itemBuilder: (context) => ReplyOptionType.values
.map(
@@ -549,6 +550,57 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
tooltip: '@',
selected: false,
),
ToolbarIconButton(
onPressed: () async {
controller.keepChatPanel();
RichTextItem? voteItem = editController.items
.firstWhereOrNull((e) => e.type == RichTextType.vote);
VoteInfo? voteInfo = await Navigator.of(context).push(
GetPageRoute(
page: () => CreateVotePage(
voteId: voteItem?.id == null
? null
: int.parse(voteItem!.id!))),
);
if (voteInfo != null) {
if (voteItem != null) {
final range = voteItem.range;
final text = ' ${voteInfo.title} ';
final selection = TextSelection.collapsed(
offset: range.start + text.length);
final delta = RichTextEditingDeltaReplacement(
oldText: editController.text,
replacementText: text,
replacedRange: range,
selection: selection,
composing: TextRange.empty,
type: RichTextType.vote,
id: voteInfo.voteId.toString(),
rawText: voteInfo.title,
);
final newValue = delta.apply(editController.value);
editController
..syncRichText(delta)
..value = newValue;
} else {
onInsertText(
'我发起了一个投票',
RichTextType.text,
);
onInsertText(
' ${voteInfo.title} ',
RichTextType.vote,
rawText: voteInfo.title,
id: voteInfo.voteId.toString(),
);
}
}
controller.restoreChatPanel();
},
icon: const Icon(Icons.bar_chart_rounded, size: 22),
tooltip: '投票',
selected: false,
),
// if (kDebugMode)
// ToolbarIconButton(
// onPressed: editController.clear,
@@ -569,6 +621,7 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
},
child: Obx(
() => RichTextField(
key: key,
controller: editController,
minLines: 4,
maxLines: null,

View File

@@ -0,0 +1,126 @@
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/models/dynamics/vote_model.dart';
import 'package:PiliPlus/models_new/upload_bfs/data.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
class CreateVoteController extends GetxController {
CreateVoteController(this.voteId);
final int? voteId;
String key = Utils.generateRandomString(6);
final RxString title = ''.obs;
final RxString desc = ''.obs;
final RxInt type = 0.obs;
final RxList<Option> options = <Option>[
Option(optDesc: '', imgUrl: ''),
Option(optDesc: '', imgUrl: ''),
].obs;
final RxInt choiceCnt = 1.obs;
final now = DateTime.now();
late final end = now.copyWith(day: now.day + 90);
late Rx<DateTime> endtime;
final RxBool canCreate = false.obs;
void updateCanCreate() {
if (type.value == 0) {
canCreate.value = title.value.isNotEmpty &&
options.every((e) => e.optDesc?.isNotEmpty == true);
} else {
canCreate.value = title.value.isNotEmpty &&
options.every(
(e) =>
e.optDesc?.isNotEmpty == true && e.imgUrl?.isNotEmpty == true,
);
}
}
@override
void onInit() {
super.onInit();
endtime = DateTime(
now.year,
now.month,
now.day + 1,
now.hour,
now.minute,
).obs;
if (voteId != null) {
queryData();
}
}
Future<void> queryData() async {
var res = await DynamicsHttp.voteInfo(voteId);
if (res.isSuccess) {
key = Utils.generateRandomString(6);
final VoteInfo data = res.data;
title.value = data.title!;
desc.value = data.desc ?? '';
type.value = data.options.first.imgUrl?.isNotEmpty == true ? 1 : 0;
options.value = data.options;
choiceCnt.value = data.choiceCnt!;
endtime.value = DateTime.fromMillisecondsSinceEpoch(data.endTime! * 1000);
canCreate.value = true;
} else {
showConfirmDialog(
context: Get.context!,
title: res.toString(),
onConfirm: Get.back,
);
}
}
void onDel(int i) {
options.removeAt(i);
updateCanCreate();
if (choiceCnt.value > options.length) {
choiceCnt.value = options.length;
}
}
Future<void> onCreate() async {
final voteInfo = VoteInfo(
title: title.value,
desc: desc.value,
type: type.value,
duration: endtime.value.difference(now).inSeconds,
options: options,
onlyFansLevel: 0,
choiceCnt: choiceCnt.value,
votePublisher: Accounts.main.mid,
voteId: voteId,
);
var res = voteId == null
? await DynamicsHttp.createVote(voteInfo)
: await DynamicsHttp.updateVote(voteInfo);
if (res.isSuccess) {
voteInfo.voteId = res.data;
Get.back(result: voteInfo);
} else {
res.toast();
}
}
Future<void> onUpload(int index, XFile pickedFile) async {
var res = await MsgHttp.uploadBfs(
path: pickedFile.path,
category: 'daily',
biz: 'vote',
);
if (res['status']) {
UploadBfsResData data = res['data'];
options
..[index].imgUrl = data.imageUrl
..refresh();
updateCanCreate();
} else {
SmartDialog.showToast(res['msg']);
}
}
}

View File

@@ -0,0 +1,412 @@
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/dynamics/vote_model.dart';
import 'package:PiliPlus/pages/dynamics_create_vote/controller.dart';
import 'package:PiliPlus/utils/date_util.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
class CreateVotePage extends StatefulWidget {
const CreateVotePage({super.key, this.voteId});
final int? voteId;
@override
State<CreateVotePage> createState() => _CreateVotePageState();
}
class _CreateVotePageState extends State<CreateVotePage> {
late final _controller = Get.put(CreateVoteController(widget.voteId),
tag: Utils.generateRandomString(8));
late final imagePicker = ImagePicker();
late Divider _divider;
late TextStyle _leadingStyle;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
_divider = Divider(
height: 1,
color: theme.colorScheme.outline.withValues(alpha: 0.1),
);
_leadingStyle = TextStyle(
fontSize: 15,
color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.9),
);
final padding = MediaQuery.paddingOf(context);
final divider = [
const SizedBox(height: 10),
_divider,
const SizedBox(height: 10),
];
return Scaffold(
appBar: AppBar(
title: Text('${_controller.voteId != null ? '' : '发起'}投票'),
),
body: ListView(
padding: EdgeInsets.only(
left: padding.left + 16,
right: padding.right + 16,
bottom: padding.bottom + 80,
),
children: [
const Text(
'投票类型',
style: TextStyle(fontSize: 14),
),
const SizedBox(height: 12),
_buildType(theme),
const SizedBox(height: 40),
Obx(
() => _buildInput(
theme,
key: ValueKey('${_controller.key}title'),
initialValue: _controller.title.value,
onChanged: (value) => _controller
..title.value = value
..updateCanCreate(),
desc: '投票标题',
hintText: '请填写标题',
inputFormatters: [LengthLimitingTextInputFormatter(32)],
),
),
...divider,
Obx(
() => _buildInput(
theme,
key: ValueKey('${_controller.key}desc'),
initialValue: _controller.desc.value,
onChanged: (value) => _controller.desc.value = value,
desc: '投票说明',
inputFormatters: [LengthLimitingTextInputFormatter(100)],
),
),
...divider,
const SizedBox(height: 40),
Obx(
() {
final showImg = _controller.type.value == 1;
final showDel = _controller.options.length > 2;
List<Widget> children = [];
for (int i = 0; i < _controller.options.length; i++) {
final e = _controller.options[i];
children
..add(_buildInput(
theme,
key: ValueKey(e.hashCode),
showDel: showDel,
onDel: () {
FocusManager.instance.primaryFocus?.unfocus();
_controller.onDel(i);
},
showImg: showImg,
imgUrl: e.imgUrl,
onPickImg: () => EasyThrottle.throttle(
'picImg',
const Duration(milliseconds: 500),
() => _onPickImg(i),
),
initialValue: e.optDesc,
onChanged: (value) => _controller
..options[i].optDesc = value
..updateCanCreate(),
desc: '选项${i + 1}',
hintText: '选项内容最多20字',
inputFormatters: [LengthLimitingTextInputFormatter(20)],
))
..addAll(divider);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...children,
if (_controller.options.length < 20)
FilledButton(
onPressed: () => _controller
..options.add(Option(optDesc: '', imgUrl: ''))
..updateCanCreate(),
style: FilledButton.styleFrom(
minimumSize: Size.zero,
padding: const EdgeInsets.only(
left: 10, right: 14, top: 4, bottom: 4),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
foregroundColor: theme.colorScheme.onSurfaceVariant,
backgroundColor: theme.colorScheme.onInverseSurface,
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, size: 16),
Text(
' 添加选项',
style: TextStyle(fontSize: 13),
),
],
),
),
],
);
},
),
const SizedBox(height: 40),
Row(
spacing: 12,
children: [
SizedBox(
width: 100,
child: Text('单选/多选', style: _leadingStyle),
),
Obx(() {
final choiceCnt = _controller.choiceCnt.value;
final choices =
List.generate(_controller.options.length, (i) => i + 1);
return Listener(
onPointerDown: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
child: PopupMenuButton<int>(
initialValue: choiceCnt,
requestFocus: false,
child:
Text(choiceCnt == 1 ? '单选 ' : '最多选$choiceCnt项'),
onSelected: (value) => _controller.choiceCnt.value = value,
itemBuilder: (context) {
return choices
.map((e) => PopupMenuItem(
value: e,
child: Text(e == 1 ? '单选' : '最多选$e项'),
))
.toList();
},
),
);
}),
],
),
const SizedBox(height: 4),
...divider,
Row(
spacing: 12,
children: [
SizedBox(
width: 100,
child: Text('投票截止时间', style: _leadingStyle),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
FocusManager.instance.primaryFocus?.unfocus();
DateTime? newDate = await showDatePicker(
context: context,
initialDate: _controller.endtime.value,
firstDate: _controller.now,
lastDate: _controller.end,
);
if (newDate != null && context.mounted) {
TimeOfDay? newTime = await showTimePicker(
context: context,
initialTime:
TimeOfDay.fromDateTime(_controller.endtime.value),
);
if (newTime != null) {
final newEndtime = DateTime(
newDate.year,
newDate.month,
newDate.day,
newTime.hour,
newTime.minute,
);
if (newEndtime.difference(DateTime.now()) >=
const Duration(minutes: 5)) {
_controller.endtime.value = newEndtime;
} else {
SmartDialog.showToast('至少选择5分钟之后');
}
}
}
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Obx(() => Text(
DateUtil.longFormatD.format(_controller.endtime.value),
)),
),
),
],
),
...divider,
const SizedBox(height: 40),
Obx(() {
final canCreate = _controller.canCreate.value;
return FilledButton.tonal(
onPressed: canCreate ? _controller.onCreate : null,
child: const Text('发起投票'),
);
}),
],
),
);
}
Widget _buildInput(
ThemeData theme, {
Key? key,
String? initialValue,
required ValueChanged<String> onChanged,
required String desc,
String? hintText,
List<TextInputFormatter>? inputFormatters,
bool showDel = false,
bool showImg = false,
String? imgUrl,
VoidCallback? onDel,
VoidCallback? onPickImg,
}) {
return Row(
spacing: 12,
children: [
SizedBox(
width: 65,
child: Text(
desc,
style: _leadingStyle,
),
),
Expanded(
child: TextFormField(
key: key,
initialValue: initialValue,
onChanged: onChanged,
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
hintText: hintText ?? desc,
hintStyle: TextStyle(
fontSize: 15,
color: theme.colorScheme.outline.withValues(alpha: 0.7),
),
),
inputFormatters: inputFormatters,
),
),
if (showImg)
GestureDetector(
onTap: onPickImg,
child: NetworkImgLayer(
src: imgUrl,
width: 40,
height: 40,
radius: 6,
),
),
if (showDel)
iconButton(
size: 26,
iconSize: 18,
tooltip: '移除',
context: context,
icon: Icons.clear,
onPressed: onDel,
bgColor: Colors.transparent,
iconColor: theme.colorScheme.onSurfaceVariant,
),
],
);
}
Widget _buildType(ThemeData theme) => Obx(
() {
return Row(
spacing: 16,
children: List.generate(
2,
(index) {
final isEnable = index == _controller.type.value;
final style = TextButton.styleFrom(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
padding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 17),
shape: RoundedRectangleBorder(
side: BorderSide(
color: isEnable
? theme.colorScheme.secondary
: theme.colorScheme.outline,
),
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
backgroundColor: isEnable
? theme.colorScheme.secondaryContainer
: Colors.transparent,
foregroundColor: isEnable
? theme.colorScheme.onSecondaryContainer
: theme.colorScheme.onSurfaceVariant,
);
Widget child = TextButton(
style: style,
onPressed: () => _controller
..type.value = index
..updateCanCreate(),
child: Text(
'${const ['文字', '图片'][index]}投票',
strutStyle: const StrutStyle(forceStrutHeight: true),
),
);
if (isEnable) {
child = Stack(
clipBehavior: Clip.none,
children: [
child,
Positioned(
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(4),
bottomRight: Radius.circular(6),
),
color: theme.colorScheme.primary,
),
child: Icon(
size: 10,
Icons.check,
color: theme.colorScheme.onPrimary,
),
),
),
],
);
}
return child;
},
),
);
},
);
void _onPickImg(int index) {
EasyThrottle.throttle('imagePicker', const Duration(milliseconds: 500),
() async {
try {
XFile? pickedFile = await imagePicker.pickImage(
imageQuality: 100,
source: ImageSource.gallery,
);
if (pickedFile != null) {
_controller.onUpload(index, pickedFile);
}
} catch (e) {
SmartDialog.showToast(e.toString());
}
});
}
}

View File

@@ -225,6 +225,7 @@ class _RepostPanelState extends CommonRichTextPubPageState<RepostPanel> {
},
child: Obx(
() => RichTextField(
key: key,
controller: editController,
minLines: 4,
maxLines: null,

View File

@@ -102,6 +102,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<LiveSendDmPanel> {
},
child: Obx(
() => RichTextField(
key: key,
controller: editController,
minLines: 1,
maxLines: 2,
@@ -200,5 +201,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<LiveSendDmPanel> {
}
@override
void onMention([bool fromClick = false]) {}
Future<void> onMention([bool fromClick = false]) {
return Future.value();
}
}

View File

@@ -11,7 +11,6 @@ import 'package:PiliPlus/models_new/space/space/setting.dart';
import 'package:PiliPlus/models_new/space/space/tab2.dart';
import 'package:PiliPlus/pages/common/common_data_controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/date_util.dart';
import 'package:PiliPlus/utils/request_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
@@ -32,7 +31,6 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
Live? live;
int? silence;
String? endTime;
int? isFollowed; // 被关注
RxInt relation = 0.obs;
@@ -85,15 +83,6 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
data.series?.item?.isNotEmpty == true) {
hasSeasonOrSeries = true;
}
if (data.card?.endTime != null) {
if (data.card!.endTime == 0) {
endTime = ': 永久封禁';
} else if (data.card!.endTime! >
DateTime.now().millisecondsSinceEpoch ~/ 1000) {
endTime =
':至 ${DateUtil.longFormatDs.format(DateTime.fromMillisecondsSinceEpoch(data.card!.endTime! * 1000))}';
}
}
tab2?.retainWhere((item) => implTabs.contains(item.param));
if (tab2?.isNotEmpty == true) {
if (data.tab!.hasItem != true && tab2!.first.param == 'home') {

View File

@@ -325,7 +325,6 @@ class _MemberPageState extends State<MemberPage> {
onFollow: () => _userController.onFollow(context),
live: _userController.live,
silence: _userController.silence,
endTime: _userController.endTime,
),
)
: GestureDetector(

View File

@@ -25,7 +25,6 @@ class UserInfoCard extends StatelessWidget {
required this.onFollow,
this.live,
this.silence,
this.endTime,
});
final bool isV;
@@ -36,7 +35,6 @@ class UserInfoCard extends StatelessWidget {
final VoidCallback onFollow;
final Live? live;
final int? silence;
final String? endTime;
@override
Widget build(BuildContext context) {
@@ -283,7 +281,7 @@ class UserInfoCard extends StatelessWidget {
),
),
TextSpan(
text: ' 该账号封禁中${endTime ?? ''}',
text: ' 该账号封禁中',
style: TextStyle(
color: isLight
? theme.colorScheme.onErrorContainer

View File

@@ -139,6 +139,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
},
child: Obx(
() => RichTextField(
key: key,
controller: editController,
minLines: 4,
maxLines: 8,
@@ -267,7 +268,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
Future<void> onCustomPublish({List? pictures}) async {
Map<String, int> atNameToMid = {
for (var e in editController.items)
if (e.type == RichTextType.at) e.rawText: int.parse(e.uid!),
if (e.type == RichTextType.at) e.rawText: int.parse(e.id!),
};
String message = editController.rawText;
var result = await VideoHttp.replyAdd(

View File

@@ -426,9 +426,9 @@ class _SendDanmakuPanelState extends CommonTextPubPageState<SendDanmakuPanel> {
);
}
void _showColorPicker() {
Future<void> _showColorPicker() async {
controller.keepChatPanel();
showDialog(
await showDialog(
context: context,
builder: (context) => AlertDialog(
clipBehavior: Clip.hardEdge,
@@ -445,6 +445,7 @@ class _SendDanmakuPanelState extends CommonTextPubPageState<SendDanmakuPanel> {
),
),
);
controller.restoreChatPanel();
}
@override

View File

@@ -240,6 +240,7 @@ class _WhisperDetailPageState
},
child: Obx(
() => RichTextField(
key: key,
readOnly: readOnly.value,
focusNode: focusNode,
controller: editController,
@@ -335,7 +336,9 @@ class _WhisperDetailPageState
}
@override
void onMention([bool fromClick = false]) {}
Future<void> onMention([bool fromClick = false]) {
return Future.value();
}
@override
void onSave() {}

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/pages/article_list/view.dart';
import 'package:PiliPlus/pages/blacklist/view.dart';
import 'package:PiliPlus/pages/danmaku_block/view.dart';
import 'package:PiliPlus/pages/dynamics/view.dart';
import 'package:PiliPlus/pages/dynamics_create_vote/view.dart';
import 'package:PiliPlus/pages/dynamics_detail/view.dart';
import 'package:PiliPlus/pages/dynamics_topic/view.dart';
import 'package:PiliPlus/pages/dynamics_topic_rcmd/view.dart';
@@ -186,6 +187,7 @@ class Routes {
CustomGetPage(name: '/msgLikeDetail', page: () => const LikeDetailPage()),
CustomGetPage(
name: '/liveDmBlockPage', page: () => const LiveDmBlockPage()),
CustomGetPage(name: '/createVote', page: () => const CreateVotePage()),
];
}

View File

@@ -73,10 +73,12 @@ class LoginAccount implements Account {
LoginAccount(this.cookieJar, this.accessKey, this.refresh,
[Set<AccountType>? type])
: type = type ?? {};
: type = type ?? {} {
cookieJar.setBuvid3();
}
LoginAccount.fromJson(Map json) {
cookieJar = BiliCookieJar.fromJson(json['cookies']);
cookieJar = BiliCookieJar.fromJson(json['cookies'])..setBuvid3();
accessKey = json['accessKey'];
refresh = json['refresh'];
type = (json['type'] as Iterable?)

View File

@@ -85,14 +85,14 @@ extension BuildContextExt on BuildContext {
: const Color(0xFFD44E7D);
}
void imageView({
Future<void> imageView({
int initialPage = 0,
required List<SourceModel> imgList,
ValueChanged<int>? onDismissed,
int? quality,
}) {
bool isMemberPage = Get.currentRoute.startsWith('/member?');
Navigator.of(this).push(
return Navigator.of(this).push(
HeroDialogRoute(
builder: (context) => InteractiveviewerGallery(
sources: imgList,

View File

@@ -139,8 +139,8 @@ packages:
description:
path: "."
ref: master
resolved-ref: "4d28c7d6dad6c099a44058527ddc65405a94b4d0"
url: "https://github.com/orz12/auto_orientation.git"
resolved-ref: ca2bb137bd0e4b221df3bc5ba1492d2902c78624
url: "https://github.com/bggRGjQaUbCoE/auto_orientation.git"
source: git
version: "2.3.1"
boolean_selector:
@@ -285,10 +285,10 @@ packages:
description:
path: "packages/chat_bottom_container"
ref: main
resolved-ref: "475331343d9ae041807d0d862a88fb1d2b7a64a1"
url: "https://github.com/pye52/flutter_chat_packages.git"
resolved-ref: acccababf698ef1712031c383ea4b7ff54ae630c
url: "https://github.com/bggRGjQaUbCoE/flutter_chat_packages.git"
source: git
version: "0.3.0"
version: "0.3.2"
checked_yaml:
dependency: transitive
description:
@@ -1366,10 +1366,10 @@ packages:
dependency: "direct main"
description:
name: permission_handler
sha256: "2d070d8684b68efb580a5997eb62f675e8a885ef0be6e754fb9ef489c177470f"
sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
url: "https://pub.dev"
source: hosted
version: "12.0.0+1"
version: "12.0.1"
permission_handler_android:
dependency: transitive
description:
@@ -1454,10 +1454,10 @@ packages:
dependency: transitive
description:
name: posix
sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.2"
version: "6.0.3"
pretty_qr_code:
dependency: "direct main"
description:
@@ -1598,10 +1598,10 @@ packages:
dependency: transitive
description:
name: sentry
sha256: "4aee18ffcdb6d92db3a770764fd0034b93f27ae736cf9f39855575a36c04fe91"
sha256: "40583b61f5f4dc0ea7095a34c03cd71e17b43bd8866fba34972398f70806b464"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
version: "9.2.0"
share_plus:
dependency: "direct main"
description:
@@ -1804,10 +1804,10 @@ packages:
dependency: "direct main"
description:
name: synchronized
sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6"
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.dev"
source: hosted
version: "3.3.1"
version: "3.4.0"
system_proxy:
dependency: "direct main"
description:

View File

@@ -124,7 +124,7 @@ dependencies:
# universal_platform: ^1.1.0
auto_orientation:
git:
url: https://github.com/orz12/auto_orientation.git
url: https://github.com/bggRGjQaUbCoE/auto_orientation.git
ref: master
protobuf: ^4.0.0
animations: ^2.0.11
@@ -177,7 +177,7 @@ dependencies:
# chat_bottom_container: ^0.2.0
chat_bottom_container:
git:
url: https://github.com/pye52/flutter_chat_packages.git
url: https://github.com/bggRGjQaUbCoE/flutter_chat_packages.git
ref: main
path: packages/chat_bottom_container
image_picker: ^1.1.2