Files
PiliPlus/lib/utils/extension.dart
bggRGjQaUbCoE 67182d2641 feat: create vote
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
2025-07-04 22:08:35 +08:00

199 lines
6.2 KiB
Dart

import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/hero_dialog_route.dart';
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart';
import 'package:PiliPlus/grpc/bilibili/app/im/v1.pbenum.dart'
show IMSettingType, ThreeDotItemType;
import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/pages/common/common_whisper_controller.dart';
import 'package:PiliPlus/pages/contact/view.dart';
import 'package:PiliPlus/pages/whisper_settings/view.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
extension ImageExtension on num? {
int? cacheSize(BuildContext context) {
if (this == null || this == 0) {
return null;
}
return (this! * MediaQuery.devicePixelRatioOf(context)).round();
}
}
extension ScrollControllerExt on ScrollController {
void animToTop() {
if (!hasClients) return;
if (offset >= Get.mediaQuery.size.height * 7) {
jumpTo(0);
} else {
animateTo(0,
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
}
}
void jumpToTop() {
if (!hasClients) return;
jumpTo(0);
}
}
extension IterableExt<T> on Iterable<T>? {
bool get isNullOrEmpty => this == null || this!.isEmpty;
}
extension ListExt<T> on List<T>? {
T? getOrNull(int index) {
if (isNullOrEmpty) {
return null;
}
if (index < 0 || index >= this!.length) {
return null;
}
return this![index];
}
T getOrElse(int index, {required T Function() orElse}) {
return getOrNull(index) ?? orElse();
}
bool removeFirstWhere(bool Function(T) test) {
if (this == null) return false;
final index = this!.indexWhere(test);
if (index != -1) {
this!.removeAt(index);
return true;
}
return false;
}
}
final _regExp = RegExp("^(http:)?//", caseSensitive: false);
extension StringExt on String? {
String get http2https => this?.replaceFirst(_regExp, "https://") ?? '';
bool get isNullOrEmpty => this == null || this!.isEmpty;
}
extension BuildContextExt on BuildContext {
Color get vipColor {
return Theme.of(this).brightness == Brightness.light
? const Color(0xFFFF6699)
: const Color(0xFFD44E7D);
}
Future<void> imageView({
int initialPage = 0,
required List<SourceModel> imgList,
ValueChanged<int>? onDismissed,
int? quality,
}) {
bool isMemberPage = Get.currentRoute.startsWith('/member?');
return Navigator.of(this).push(
HeroDialogRoute(
builder: (context) => InteractiveviewerGallery(
sources: imgList,
initIndex: initialPage,
onDismissed: onDismissed,
setStatusBar: !isMemberPage,
quality: quality ?? GlobalData().imgQuality,
),
),
);
}
}
extension Unique<E, Id> on List<E> {
List<E> unique([Id Function(E element)? id, bool inplace = true]) {
final ids = <Id>{};
return (inplace ? this : List<E>.from(this))
..retainWhere((x) => ids.add(id != null ? id(x) : x as Id));
}
}
extension ColorExtension on Color {
Color darken([double amount = .5]) {
assert(amount >= 0 && amount <= 1, 'Amount must be between 0 and 1');
return Color.lerp(this, Colors.black, amount)!;
}
}
extension BrightnessExt on Brightness {
Brightness get reverse =>
this == Brightness.light ? Brightness.dark : Brightness.light;
}
extension RationalExt on Rational {
/// Checks whether given [Rational] instance fits into Android requirements
/// or not.
///
/// Android docs specified boundaries as inclusive.
bool get fitsInAndroidRequirements {
final aspectRatio = numerator / denominator;
const min = 1 / 2.39;
const max = 2.39;
return (min <= aspectRatio) && (aspectRatio <= max);
}
}
extension ThreeDotItemTypeExt on ThreeDotItemType {
Icon get icon => switch (this) {
ThreeDotItemType.THREE_DOT_ITEM_TYPE_MSG_SETTING =>
const Icon(Icons.settings, size: 20),
ThreeDotItemType.THREE_DOT_ITEM_TYPE_READ_ALL =>
const Icon(Icons.cleaning_services, size: 20),
ThreeDotItemType.THREE_DOT_ITEM_TYPE_CLEAR_LIST =>
const Icon(Icons.delete_forever_outlined, size: 20),
ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER =>
const Icon(Icons.live_tv, size: 20),
ThreeDotItemType.THREE_DOT_ITEM_TYPE_CONTACTS =>
const Icon(Icons.account_box_outlined, size: 20),
ThreeDotItemType.THREE_DOT_ITEM_TYPE_FANS_GROUP_HELPER =>
const Icon(Icons.notifications_none, size: 20),
_ => const Icon(MdiIcons.circleMedium, size: 20),
};
void action({
required BuildContext context,
required CommonWhisperController controller,
}) {
switch (this) {
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_READ_ALL:
showConfirmDialog(
context: context,
title: '一键已读',
content: '是否清除全部新消息提醒?',
onConfirm: controller.onClearUnread,
);
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_CLEAR_LIST:
showConfirmDialog(
context: context,
title: '清空列表',
content: '清空后所有消息将被删除,无法恢复',
onConfirm: controller.onDeleteList,
);
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_MSG_SETTING:
Get.to(const WhisperSettingsPage(
imSettingType: IMSettingType.SETTING_TYPE_NEED_ALL,
));
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_UP_HELPER:
Get.toNamed(
'/whisperDetail',
arguments: {
'talkerId': 844424930131966,
'name': 'UP主小助手',
'face':
'https://message.biliimg.com/bfs/im/489a63efadfb202366c2f88853d2217b5ddc7a13.png',
},
);
case ThreeDotItemType.THREE_DOT_ITEM_TYPE_CONTACTS:
Get.to(const ContactPage(isFromSelect: false));
default:
SmartDialog.showToast('TODO: $name');
}
}
}