Compare commits

...

13 Commits

Author SHA1 Message Date
dom
8ad130567e Release 2.0.2
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-31 18:25:39 +08:00
dom
7eb21bc5a2 build
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-31 18:24:02 +08:00
dom
ea4316a847 opt ui
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-31 16:57:12 +08:00
dom
2bbc97a950 fix macOS build
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-31 11:51:22 +08:00
dom
0178d105ba add Local Network permissions for iOS & macOS
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-31 11:17:32 +08:00
dom
771fa75f48 upgrade deps
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-31 11:17:09 +08:00
dom
82483b33fc opt live emote
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-30 00:02:58 +08:00
My-Responsitories
886c53c7d8 opt: m3e loading (#1877)
* opt: loading

* feat: refresh m3e

* restore refreshIndicator

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-03-29 23:34:04 +08:00
dom
f0050dd6e6 fix pendent offset
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 11:47:58 +08:00
dom
e6a2f65b4e fix reply up badge
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 11:47:58 +08:00
dom
2fc3f9864f opt ui
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 10:51:52 +08:00
dom
64c05a1b06 upgrade deps
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 10:02:00 +08:00
ninatan777
7c4e20f96c safely parse up list (#1876)
* fix: 兼容 mid/roomId/count 字段为字符串或数字类型

* update

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-03-29 10:01:10 +08:00
25 changed files with 430 additions and 236 deletions

View File

@@ -13,7 +13,7 @@ on:
jobs:
build-mac-app:
name: Release Mac
runs-on: macos-latest
runs-on: macos-26
steps:
- name: Checkout code
uses: actions/checkout@v6

View File

@@ -131,5 +131,13 @@
</array>
<key>UIStatusBarHidden</key>
<false/>
<key>NSLocalNetworkUsageDescription</key>
<string>需要访问本地网络以发现和连接 DLNA 投屏设备</string>
<key>NSBonjourServices</key>
<array>
<string>_ssdp._udp</string>
<string>_upnp._tcp</string>
<string>_http._tcp</string>
</array>
</dict>
</plist>

View File

@@ -10,23 +10,21 @@ class CustomToast extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final colorScheme = ColorScheme.of(context);
return Container(
margin: EdgeInsets.only(
bottom: MediaQuery.viewPaddingOf(context).bottom + 30,
),
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer.withValues(
alpha: toastOpacity,
),
color: colorScheme.primaryContainer.withValues(alpha: toastOpacity),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Text(
msg,
style: TextStyle(
fontSize: 13,
color: theme.colorScheme.onPrimaryContainer,
color: colorScheme.onPrimaryContainer,
),
),
);
@@ -41,7 +39,7 @@ class LoadingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final theme = Theme.of(context);
final onSurfaceVariant = theme.colorScheme.onSurfaceVariant;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
@@ -58,7 +56,6 @@ class LoadingWidget extends StatelessWidget {
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(onSurfaceVariant),
),
//msg
Text(msg, style: TextStyle(color: onSurfaceVariant)),
],

View File

@@ -0,0 +1,31 @@
import 'package:flutter/rendering.dart' show RenderProxyBox, BoxHitTestResult;
import 'package:flutter/widgets.dart';
class ExtraHitTestWidget extends SingleChildRenderObjectWidget {
const ExtraHitTestWidget({
super.key,
required this.width,
required Widget super.child,
});
final double width;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderExtraHitTestWidget(width: width);
}
}
class RenderExtraHitTestWidget extends RenderProxyBox {
RenderExtraHitTestWidget({
required double width,
}) : _width = width;
final double _width;
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return super.hitTestChildren(result, position: position) ||
position.dx <= _width;
}
}

View File

@@ -217,8 +217,8 @@ class RefreshIndicatorState extends State<RefreshIndicator>
RefreshIndicatorStatus? _status;
late Future<void> _pendingRefreshFuture;
double? _dragOffset;
late Color _effectiveValueColor =
widget.color ?? Theme.of(context).colorScheme.primary;
late Color _effectiveValueColor;
// late Color _backgroundColor;
static final Animatable<double> _threeQuarterTween = Tween<double>(
begin: 0.0,
@@ -274,9 +274,10 @@ class RefreshIndicatorState extends State<RefreshIndicator>
}
void _setupColorTween() {
final colorScheme = ColorScheme.of(context);
// _backgroundColor = colorScheme.surfaceContainerHighest;
// Reset the current value color.
_effectiveValueColor =
widget.color ?? Theme.of(context).colorScheme.primary;
_effectiveValueColor = widget.color ?? colorScheme.primary;
final Color color = _effectiveValueColor;
if (color.a == 0) {
// Set an always stopped animation instead of a driven tween.
@@ -558,14 +559,52 @@ class RefreshIndicatorState extends State<RefreshIndicator>
}
bool _onDrag(double offset, double viewportDimension) {
if (_positionController.value > 0.0 &&
_status == RefreshIndicatorStatus.drag) {
if (_positionController.value > 0.0 && _status == .drag) {
_dragOffset = _dragOffset! + offset;
_checkDragOffset(viewportDimension);
return true;
}
return false;
}
// late final _refreshKey = GlobalKey();
// Widget _m3eRefreshProgressIndicator(bool showIndeterminateIndicator) {
// const indicatorMargin = EdgeInsets.all(4);
// const indicatorPadding = EdgeInsets.all(6);
// const indicatorSize = 41.0;
// final progress = _value.value;
// return Padding(
// padding: indicatorMargin,
// child: SizedBox(
// width: indicatorSize,
// height: indicatorSize,
// child: Material(
// type: MaterialType.circle,
// color: _backgroundColor,
// elevation: widget.elevation,
// child: Padding(
// padding: indicatorPadding,
// child: showIndeterminateIndicator
// ? M3ELoadingIndicator(
// childKey: _refreshKey,
// color: _effectiveValueColor,
// morphs: Morphs.refreshMorphs,
// size: null,
// )
// : RawM3ELoadingIndicator(
// key: _refreshKey,
// morph: Morphs.manualMorph,
// progress: progress,
// angle: -progress * math.pi,
// color: _valueColor.value!,
// size: null,
// ),
// ),
// ),
// ),
// );
// }
}
// ignore: camel_case_types

View File

@@ -18,6 +18,7 @@
import 'dart:math' show pi;
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart' show SemanticsConfiguration;
///
/// created by dom on 2026/02/14
@@ -73,6 +74,7 @@ class RenderLoadingIndicator extends RenderBox {
if (_progress == value) return;
_progress = value;
markNeedsPaint();
markNeedsSemanticsUpdate();
}
@override
@@ -119,6 +121,16 @@ class RenderLoadingIndicator extends RenderBox {
);
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config
..role = .progressBar
..minValue = '0'
..maxValue = '100'
..value = (_progress * 100).round().toString();
}
@override
bool get isRepaintBoundary => true;
}

View File

@@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
const Widget m3eLoading = Center(child: M3ELoadingIndicator());
const Widget circularLoading = Center(child: CircularProgressIndicator());
const Widget linearLoading = SliverToBoxAdapter(
child: LinearProgressIndicator(),
);

View File

@@ -17,14 +17,27 @@
import 'dart:math' as math;
import 'package:PiliPlus/common/widgets/loading_widget/morphs.dart';
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart' show SpringSimulation;
import 'package:flutter/semantics.dart';
import 'package:material_new_shapes/material_new_shapes.dart';
/// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
class M3ELoadingIndicator extends StatefulWidget {
const M3ELoadingIndicator({super.key});
const M3ELoadingIndicator({
super.key,
// this.childKey,
this.morphs,
this.color,
this.size = const Size.square(40),
});
final List<Morph>? morphs;
final Color? color;
final Size size;
// final Key? childKey;
@override
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
@@ -32,42 +45,25 @@ class M3ELoadingIndicator extends StatefulWidget {
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
with SingleTickerProviderStateMixin {
static final List<Morph> _morphs = () {
final List<RoundedPolygon> shapes = [
MaterialShapes.softBurst,
MaterialShapes.cookie9Sided,
MaterialShapes.pentagon,
MaterialShapes.pill,
MaterialShapes.sunny,
MaterialShapes.cookie4Sided,
MaterialShapes.oval,
];
return [
for (var i = 0; i < shapes.length; i++)
Morph(
shapes[i],
shapes[(i + 1) % shapes.length],
),
];
}();
static const int _morphIntervalMs = 650;
static const double _fullRotation = 360.0;
static const double _fullRotation = 2 * math.pi;
static const int _globalRotationDurationMs = 4666;
static const double _quarterRotation = _fullRotation / 4;
late final List<Morph> _morphs;
late final AnimationController _controller;
int _morphIndex = 1;
double _morphRotationTargetAngle = _quarterRotation;
double _morphRotationTarget = _quarterRotation;
final _morphAnimationSpec = SpringSimulation(
static final _morphAnimationSpec = SpringSimulation(
SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0),
0.0,
1.0,
5.0,
snapToEnd: true,
// tolerance: const Tolerance(velocity: 0.1, distance: 0.1),
);
void _statusListener(AnimationStatus status) {
@@ -78,23 +74,21 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
void _startAnimation() {
_morphIndex++;
_morphRotationTargetAngle =
(_morphRotationTargetAngle + _quarterRotation) % _fullRotation;
_controller
..value = 0.0
..animateWith(_morphAnimationSpec);
_morphRotationTarget =
(_morphRotationTarget + _quarterRotation) % _fullRotation;
_controller.animateWith(_morphAnimationSpec);
}
@override
void initState() {
super.initState();
_morphs = widget.morphs ?? Morphs.loadingMorphs;
_controller =
AnimationController(
vsync: this,
duration: const Duration(milliseconds: _morphIntervalMs),
)
..addStatusListener(_statusListener)
..value = 0.0
..animateWith(_morphAnimationSpec);
}
@@ -110,82 +104,86 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
final elapsedInMs =
_morphIntervalMs * (_morphIndex - 1) +
(_controller.lastElapsedDuration?.inMilliseconds ?? 0);
final globalRotationControllerValue =
(elapsedInMs % _globalRotationDurationMs) / _globalRotationDurationMs;
final globalRotationDegrees = globalRotationControllerValue * _fullRotation;
final totalRotationDegrees =
progress * _quarterRotation +
_morphRotationTargetAngle +
globalRotationDegrees;
return totalRotationDegrees * (math.pi / 180.0);
final globalRotation =
(elapsedInMs % _globalRotationDurationMs) /
_globalRotationDurationMs *
_fullRotation;
return progress * _quarterRotation + _morphRotationTarget + globalRotation;
}
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme.secondaryFixedDim;
final color = widget.color ?? ColorScheme.of(context).secondaryFixedDim;
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
final progress = _controller.value;
return _M3ELoadingIndicator(
return RawM3ELoadingIndicator(
// key: widget.childKey,
morph: _morphs[_morphIndex % _morphs.length],
progress: progress,
angle: _calcAngle(progress),
color: color,
size: widget.size,
);
},
);
}
}
class _M3ELoadingIndicator extends LeafRenderObjectWidget {
const _M3ELoadingIndicator({
class RawM3ELoadingIndicator extends LeafRenderObjectWidget {
const RawM3ELoadingIndicator({
super.key,
required this.morph,
required this.progress,
required this.angle,
required this.color,
required this.size,
});
final Morph morph;
final double progress;
final double angle;
final Color color;
final Size size;
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderM3ELoadingIndicator(
return RenderM3ELoadingIndicator(
morph: morph,
progress: progress,
angle: angle,
color: color,
size: size,
);
}
@override
void updateRenderObject(
BuildContext context,
_RenderM3ELoadingIndicator renderObject,
RenderM3ELoadingIndicator renderObject,
) {
renderObject
..morph = morph
..progress = progress
..angle = angle
..color = color;
..color = color
..preferredSize = size;
}
}
class _RenderM3ELoadingIndicator extends RenderBox {
_RenderM3ELoadingIndicator({
class RenderM3ELoadingIndicator extends RenderBox {
RenderM3ELoadingIndicator({
required Morph morph,
required double progress,
required double angle,
required Color color,
required Size size,
}) : _morph = morph,
_progress = progress,
_angle = angle,
_preferredSize = size,
_color = color,
_paint = Paint()
..style = PaintingStyle.fill
@@ -223,20 +221,38 @@ class _RenderM3ELoadingIndicator extends RenderBox {
markNeedsPaint();
}
Size _preferredSize;
set preferredSize(Size value) {
if (_preferredSize == value) return;
_preferredSize = size;
markNeedsLayout();
}
@override
Size computeDryLayout(covariant BoxConstraints constraints) {
return constraints.constrain(_preferredSize);
}
@override
void performLayout() {
size = constraints.constrainDimensions(40, 40);
size = computeDryLayout(constraints);
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config.role = .loadingSpinner;
}
@override
void paint(PaintingContext context, Offset offset) {
final width = size.width;
final value = size.width / 2;
final matrix = Matrix4.identity()
..translateByDouble(offset.dx + value, offset.dy + value, 0.0, 1.0)
..rotateZ(angle)
..translateByDouble(-value, -value, 0.0, 1.0)
..scaleByDouble(width, width, width, 1.0);
final matrix =
Matrix4.translationValues(offset.dx + value, offset.dy + value, 0.0)
..rotateZ(angle)
..translateByDouble(-value, -value, 0.0, 1.0)
..scaleByDouble(width, width, width, 1.0);
final path = morph.toPath(progress: progress).transform(matrix.storage);
context.canvas.drawPath(path, _paint);

View File

@@ -0,0 +1,41 @@
import 'package:material_new_shapes/material_new_shapes.dart';
abstract final class Morphs {
static List<Morph> buildMorph(
List<RoundedPolygon> shapes, {
bool loop = true,
}) {
assert(shapes.length >= 2);
return [
for (var i = 0; i < shapes.length - 1; i++)
Morph(shapes[i], shapes[i + 1]),
if (loop) Morph(shapes[shapes.length - 1], shapes[0]),
];
}
static final loadingMorphs = buildMorph([
MaterialShapes.softBurst,
MaterialShapes.cookie9Sided,
MaterialShapes.pentagon,
MaterialShapes.pill,
MaterialShapes.sunny,
MaterialShapes.cookie4Sided,
MaterialShapes.oval,
]);
// static final refreshMorphs = buildMorph([
// MaterialShapes.softBurst,
// MaterialShapes.cookie9Sided,
// MaterialShapes.gem,
// MaterialShapes.flower,
// MaterialShapes.sunny,
// MaterialShapes.cookie4Sided,
// MaterialShapes.oval,
// MaterialShapes.cookie12Sided,
// ]);
// static final manualMorph = Morph(
// MaterialShapes.circle,
// MaterialShapes.softBurst,
// );
}

View File

@@ -57,7 +57,7 @@ class PendantAvatar extends StatelessWidget {
final pendantSize = size * 1.75;
pendant = Positioned(
// -(size * 1.75 - size) / 2
top: -0.375 * size + pendentOffset,
top: -0.375 * size + pendentOffset / 2,
child: IgnorePointer(
child: NetworkImgLayer(
type: .emote,

View File

@@ -1,3 +1,5 @@
import 'package:PiliPlus/utils/utils.dart';
class FollowUpModel {
FollowUpModel({
this.liveUsers,
@@ -49,7 +51,7 @@ class LiveUsers {
List<LiveUserItem>? items;
LiveUsers.fromJson(Map<String, dynamic> json) {
count = json['count'] ?? 0;
count = Utils.safeToInt(json['count']) ?? 0;
group = json['group'];
items = (json['items'] as List?)
?.map<LiveUserItem>((e) => LiveUserItem.fromJson(e))
@@ -63,14 +65,11 @@ class LiveUserItem extends UpItem {
int? roomId;
String? title;
LiveUserItem.fromJson(Map<String, dynamic> json)
: super(mid: json['mid'] ?? 0) {
face = json['face'];
LiveUserItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
isReserveRecall = json['is_reserve_recall'];
jumpUrl = json['jump_url'];
roomId = json['room_id'];
roomId = Utils.safeToInt(json['room_id']);
title = json['title'];
uname = json['uname'];
}
}
@@ -90,7 +89,7 @@ class UpItem {
UpItem.fromJson(Map<String, dynamic> json) {
face = json['face'];
hasUpdate = json['has_update'];
mid = json['mid'] ?? 0;
mid = Utils.safeToInt(json['mid']) ?? 0;
uname = json['uname'];
}

View File

@@ -3,7 +3,7 @@ class BaseEmote {
late String emoticonUnique;
late double width;
late double height;
late final isUpower = emoticonUnique.startsWith('upower_');
late final isOfficial = emoticonUnique.startsWith('official_');
BaseEmote.fromJson(Map<String, dynamic> json) {
url = json['url'];

View File

@@ -53,7 +53,7 @@ class SuperChatItem {
'ts': 0,
'uinfo': {
'medal': {
"name": "MedalName",
"name": "Medal",
"level": Utils.random.nextInt(40),
"id": 123,
"ruid": 456,

View File

@@ -188,6 +188,7 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
isVideoDetail: !showBackBtn,
replyType: controller.replyType,
firstFloor: replyItem,
upMid: controller.upMid,
),
);
if (showBackBtn) {

View File

@@ -3,6 +3,7 @@ import 'dart:math';
import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/dialog/report.dart';
import 'package:PiliPlus/common/widgets/extra_hit_test_widget.dart';
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/loading_state.dart';
@@ -98,37 +99,40 @@ class AuthorPanel extends StatelessWidget {
Get.toNamed('/member?mid=${moduleAuthor.mid}');
}
: null,
child: Row(
spacing: 10,
children: [
PendantAvatar(
size: 40,
moduleAuthor.face,
pendantImage: moduleAuthor.pendant?.image,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
moduleAuthor.name!,
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(
color:
moduleAuthor.vip != null &&
moduleAuthor.vip!.status > 0 &&
moduleAuthor.vip!.type == 2
? theme.colorScheme.vipColor
: theme.colorScheme.onSurface,
fontSize: theme.textTheme.titleSmall!.fontSize,
),
),
?pubTs,
],
child: ExtraHitTestWidget(
width: 50,
child: Row(
spacing: 10,
children: [
PendantAvatar(
size: 40,
moduleAuthor.face,
pendantImage: moduleAuthor.pendant?.image,
),
),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
moduleAuthor.name!,
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(
color:
moduleAuthor.vip != null &&
moduleAuthor.vip!.status > 0 &&
moduleAuthor.vip!.type == 2
? theme.colorScheme.vipColor
: theme.colorScheme.onSurface,
fontSize: theme.textTheme.titleSmall!.fontSize,
),
),
?pubTs,
],
),
),
],
),
),
);
Widget? moreBtn = isSave
@@ -177,21 +181,23 @@ class AuthorPanel extends StatelessWidget {
Positioned(
top: 0,
right: 0,
height: height,
child: CachedNetworkImage(
height: height,
memCacheHeight: height.cacheSize(context),
imageUrl: ImageUtils.safeThumbnailUrl(
moduleAuthor.decorate!.cardUrl,
bottom: 0,
child: Center(
child: CachedNetworkImage(
height: height,
memCacheHeight: height.cacheSize(context),
imageUrl: ImageUtils.safeThumbnailUrl(
moduleAuthor.decorate!.cardUrl,
),
placeholder: (_, _) => const SizedBox.shrink(),
),
placeholder: (_, _) => const SizedBox.shrink(),
),
),
if (moduleAuthor.decorate!.fan?.numStr?.isNotEmpty == true)
Positioned(
top: 0,
bottom: 0,
right: height,
height: height,
child: Center(
child: Text(
moduleAuthor.decorate!.fan!.numStr!.toString(),

View File

@@ -153,11 +153,11 @@ class _SuperChatCardState extends State<SuperChatCard> {
name = Row(
spacing: 5,
children: [
Flexible(child: name),
MedalWidget.fromMedalInfo(
medal: medal,
padding: MedalWidget.mediumPadding,
),
Flexible(child: name),
],
);
} catch (e, s) {

View File

@@ -2,7 +2,6 @@ import 'package:PiliPlus/common/widgets/flutter/popup_menu.dart';
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/http/live.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models_new/live/live_danmaku/danmaku_msg.dart';
import 'package:PiliPlus/models_new/live/live_superchat/item.dart';
import 'package:PiliPlus/pages/live_room/controller.dart';
@@ -241,14 +240,20 @@ class LiveRoomChatPanel extends StatelessWidget {
InlineSpan _buildMsg(double devicePixelRatio, DanmakuMsg obj) {
final uemote = obj.uemote;
if (uemote != null) {
// "room_{{room_id}}_{{int}}" or "upower_[{{emote}}]"
final isUpower = uemote.isUpower;
// "room_{{room_id}}_{{int}}" , "upower_[{{emote}}]" , "official_{{int}}"
final double width, height;
if (uemote.isOfficial) {
width = uemote.width / devicePixelRatio;
height = uemote.height / devicePixelRatio;
} else {
width = height = 162.0 / devicePixelRatio;
}
return WidgetSpan(
child: NetworkImgLayer(
src: uemote.url,
type: ImageType.emote,
width: isUpower ? uemote.width : uemote.width / devicePixelRatio,
height: isUpower ? uemote.height : uemote.height / devicePixelRatio,
type: .emote,
width: width,
height: height,
),
);
}
@@ -265,7 +270,7 @@ class LiveRoomChatPanel extends StatelessWidget {
WidgetSpan(
child: NetworkImgLayer(
src: emote.url,
type: ImageType.emote,
type: .emote,
width: emote.width,
height: emote.height,
),

View File

@@ -239,6 +239,7 @@ class _MainReplyPageState extends State<MainReplyPage>
isVideoDetail: false,
replyType: _controller.replyType,
firstFloor: replyItem,
upMid: _controller.upMid,
),
).constraintWidth(),
),

View File

@@ -250,6 +250,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
replyType: _videoReplyController.videoType.replyType,
isVideoDetail: true,
isNested: widget.isNested,
upMid: _videoReplyController.upMid,
),
);
});

View File

@@ -5,6 +5,7 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/dialog/report.dart';
import 'package:PiliPlus/common/widgets/extra_hit_test_widget.dart';
import 'package:PiliPlus/common/widgets/flutter/text/text.dart' as custom_text;
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
@@ -139,100 +140,108 @@ class ReplyItemGrpc extends StatelessWidget {
feedBack();
Get.toNamed('/member?mid=${replyItem.mid}');
},
child: Row(
crossAxisAlignment: .center,
spacing: 12,
children: [
PendantAvatar(
member.face,
size: 34,
badgeSize: 14,
vipStatus: member.vipStatus.toInt(),
officialType: member.officialVerifyType.toInt(),
pendantImage: member.hasGarbPendantImage()
? member.garbPendantImage
: null,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
spacing: 6,
children: [
Flexible(
child: Text(
member.name,
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(
color: (member.vipStatus > 0 && member.vipType == 2)
? theme.colorScheme.vipColor
: theme.colorScheme.outline,
fontSize: 13,
child: ExtraHitTestWidget(
width: 46,
child: Row(
crossAxisAlignment: .center,
spacing: 12,
children: [
PendantAvatar(
member.face,
size: 34,
badgeSize: 14,
vipStatus: member.vipStatus.toInt(),
officialType: member.officialVerifyType.toInt(),
pendantImage: member.hasGarbPendantImage()
? member.garbPendantImage
: null,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
spacing: 6,
children: [
Flexible(
child: Text(
member.name,
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(
color: (member.vipStatus > 0 && member.vipType == 2)
? theme.colorScheme.vipColor
: theme.colorScheme.outline,
fontSize: 13,
),
),
),
),
Image.asset(
Utils.levelName(
member.level,
isSeniorMember: member.isSeniorMember == 1,
),
height: 11,
cacheHeight: 11.cacheSize(context),
),
if (replyItem.mid == upMid)
const PBadge(
text: 'UP',
size: PBadgeSize.small,
isStack: false,
fontSize: 9,
)
else if (GlobalData().showMedal &&
member.hasFansMedalLevel())
MedalWidget(
medalName: member.fansMedalName,
level: member.fansMedalLevel.toInt(),
backgroundColor: DmUtils.decimalToColor(
member.fansMedalColor.toInt(),
Image.asset(
Utils.levelName(
member.level,
isSeniorMember: member.isSeniorMember == 1,
),
nameColor: DmUtils.decimalToColor(
member.fansMedalColorName.toInt(),
height: 11,
cacheHeight: 11.cacheSize(context),
),
if (replyItem.mid == upMid)
const PBadge(
text: 'UP',
size: PBadgeSize.small,
isStack: false,
fontSize: 9,
)
else if (GlobalData().showMedal &&
member.hasFansMedalLevel())
MedalWidget(
medalName: member.fansMedalName,
level: member.fansMedalLevel.toInt(),
backgroundColor: DmUtils.decimalToColor(
member.fansMedalColor.toInt(),
),
nameColor: DmUtils.decimalToColor(
member.fansMedalColorName.toInt(),
),
padding: const .symmetric(
horizontal: 6,
vertical: 1.5,
),
),
padding: const .symmetric(horizontal: 6, vertical: 1.5),
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
replyLevel == 0
? DateFormatUtils.format(
replyItem.ctime.toInt(),
format: DateFormatUtils.longFormatDs,
)
: DateFormatUtils.dateFormat(replyItem.ctime.toInt()),
style: TextStyle(
fontSize: theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
),
),
if (replyItem.replyControl.hasLocation())
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${replyItem.replyControl.location}',
replyLevel == 0
? DateFormatUtils.format(
replyItem.ctime.toInt(),
format: DateFormatUtils.longFormatDs,
)
: DateFormatUtils.dateFormat(
replyItem.ctime.toInt(),
),
style: TextStyle(
fontSize: theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
),
),
],
),
],
if (replyItem.replyControl.hasLocation())
Text(
'${replyItem.replyControl.location}',
style: TextStyle(
fontSize: theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
),
),
],
),
],
),
),
),
],
],
),
),
);
if (PendantAvatar.showDecorate) {

View File

@@ -16,6 +16,7 @@ import 'package:PiliPlus/utils/extension/widget_ext.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:fixnum/fixnum.dart' show Int64;
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@@ -33,6 +34,7 @@ class VideoReplyReplyPanel extends CommonSlidePage {
required this.isVideoDetail,
required this.replyType,
this.isNested = false,
this.upMid,
});
final int? id;
final int oid;
@@ -42,6 +44,7 @@ class VideoReplyReplyPanel extends CommonSlidePage {
final bool isVideoDetail;
final int replyType;
final bool isNested;
final Int64? upMid;
@override
State<VideoReplyReplyPanel> createState() => _VideoReplyReplyPanelState();
@@ -224,7 +227,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
replyLevel: 2,
needDivider: false,
onReply: (replyItem) => _controller.onReply(replyItem, index: -1),
upMid: _controller.upMid,
upMid: widget.upMid ?? _controller.upMid,
onCheckReply: (item) =>
_controller.onCheckReply(item, isManual: true),
),

View File

@@ -73,3 +73,10 @@ foreach ($patch in $patches) {
Write-Host "$patch applied"
}
}
# TODO: remove
if ($platform.ToLower() -eq "android") {
"69e31205362b4e59b7eb89b24797e687b4b67afe" | Set-Content -Path .\bin\internal\engine.version
Remove-Item -Path ".\bin\cache" -Recurse -Force
flutter --version
}

View File

@@ -28,5 +28,13 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSLocalNetworkUsageDescription</key>
<string>需要访问本地网络以发现和连接 DLNA 投屏设备</string>
<key>NSBonjourServices</key>
<array>
<string>_ssdp._udp</string>
<string>_upnp._tcp</string>
<string>_http._tcp</string>
</array>
</dict>
</plist>

View File

@@ -320,18 +320,18 @@ packages:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
sha256: b8fe52979ff12432ecf8f0abf6ff70410b1bb734be1c9e4f2f86807ad7166c79
url: "https://pub.dev"
source: hosted
version: "7.0.0"
version: "7.1.0"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.1.0"
convert:
dependency: transitive
description:
@@ -400,10 +400,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
sha256: b4fed1b2835da9d670d7bed7db79ae2a94b0f5ad6312268158a9b5479abbacdd
url: "https://pub.dev"
source: hosted
version: "12.3.0"
version: "12.4.0"
device_info_plus_platform_interface:
dependency: transitive
description:
@@ -906,10 +906,10 @@ packages:
dependency: "direct main"
description:
name: image_cropper
sha256: "2cd06f097b5bd18ff77d3f80fadef83e270ea23c82906bbf17febc3db8d68ec6"
sha256: d2555be1ec4b7b12fc502ede481c846ad44578fbb0748debd4c648b25ca07cad
url: "https://pub.dev"
source: hosted
version: "12.1.0"
version: "12.1.1"
image_cropper_for_web:
dependency: transitive
description:
@@ -1154,12 +1154,13 @@ packages:
source: git
version: "1.3.7"
media_kit_libs_ios_video:
dependency: transitive
dependency: "direct overridden"
description:
name: media_kit_libs_ios_video
sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991
url: "https://pub.dev"
source: hosted
path: "libs/ios/media_kit_libs_ios_video"
ref: dev
resolved-ref: "547999bfb8b5cae9f9aca6125f46fd7cb500e994"
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
source: git
version: "1.1.4"
media_kit_libs_linux:
dependency: transitive
@@ -1170,12 +1171,13 @@ packages:
source: hosted
version: "1.2.1"
media_kit_libs_macos_video:
dependency: transitive
dependency: "direct overridden"
description:
name: media_kit_libs_macos_video
sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d
url: "https://pub.dev"
source: hosted
path: "libs/macos/media_kit_libs_macos_video"
ref: dev
resolved-ref: "547999bfb8b5cae9f9aca6125f46fd7cb500e994"
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
source: git
version: "1.1.4"
media_kit_libs_video:
dependency: "direct main"
@@ -1281,10 +1283,10 @@ packages:
dependency: "direct main"
description:
name: package_info_plus
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
url: "https://pub.dev"
source: hosted
version: "9.0.0"
version: "9.0.1"
package_info_plus_platform_interface:
dependency: transitive
description:
@@ -1577,10 +1579,10 @@ packages:
dependency: "direct main"
description:
name: share_plus
sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840"
sha256: "223873d106614442ea6f20db5a038685cc5b32a2fba81cdecaefbbae0523f7fa"
url: "https://pub.dev"
source: hosted
version: "12.0.1"
version: "12.0.2"
share_plus_platform_interface:
dependency: transitive
description:
@@ -1903,10 +1905,10 @@ packages:
dependency: transitive
description:
name: vector_graphics
sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1"
sha256: "81da85e9ca8885ade47f9685b953cb098970d11be4821ac765580a6607ea4373"
url: "https://pub.dev"
source: hosted
version: "1.1.20"
version: "1.1.21"
vector_graphics_codec:
dependency: transitive
description:

View File

@@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
# update when release
version: 2.0.1+1
version: 2.0.2+1
environment:
sdk: ">=3.10.0"
@@ -281,6 +281,16 @@ dependency_overrides:
url: https://github.com/My-Responsitories/media-kit.git
path: libs/windows/media_kit_libs_windows_video
ref: version_1.2.5
media_kit_libs_ios_video:
git:
url: https://github.com/bggRGjQaUbCoE/media-kit.git
path: libs/ios/media_kit_libs_ios_video
ref: dev
media_kit_libs_macos_video:
git:
url: https://github.com/bggRGjQaUbCoE/media-kit.git
path: libs/macos/media_kit_libs_macos_video
ref: dev
font_awesome_flutter: 10.9.0
dev_dependencies: