mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-20 23:22:07 +00:00
Merge pull request #423 from Schnitzel5/feature/follow-system-theme
added follow system theme + screenshot in player view
This commit is contained in:
commit
202c073859
12 changed files with 601 additions and 215 deletions
|
|
@ -89,6 +89,7 @@
|
|||
"appearance_subtitle": "Thema, Datum- & Zeitformat",
|
||||
"theme": "Thema",
|
||||
"dark_mode": "Dunkler Modus",
|
||||
"follow_system_theme": "System Hell/Dunkel-Modus folgen",
|
||||
"on": "An",
|
||||
"off": "Aus",
|
||||
"pure_black_dark_mode": "Rein schwarzer Dunkler Modus",
|
||||
|
|
@ -375,6 +376,7 @@
|
|||
"save": "Speichern",
|
||||
"picture_saved": "Bild gespeichert",
|
||||
"cover_updated": "Cover aktualisiert",
|
||||
"include_subtitles": "Mit Untertiteln speichern",
|
||||
"blend_mode_default": "Standard",
|
||||
"blend_mode_multiply": "Multiplizieren",
|
||||
"blend_mode_screen": "Bildschirm",
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
"appearance_subtitle": "Theme, date & time format",
|
||||
"theme": "Theme",
|
||||
"dark_mode": "Dark mode",
|
||||
"follow_system_theme": "Follow system theme",
|
||||
"on": "On",
|
||||
"off": "Off",
|
||||
"pure_black_dark_mode": "Pure black dark mode",
|
||||
|
|
@ -379,6 +380,7 @@
|
|||
"save": "Save",
|
||||
"picture_saved": "Picture saved",
|
||||
"cover_updated": "Cover updated",
|
||||
"include_subtitles": "Include subtitles",
|
||||
"blend_mode_default": "Default",
|
||||
"blend_mode_multiply": "Multiply",
|
||||
"blend_mode_screen": "Screen",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:desktop_webview_window/desktop_webview_window.dart';
|
|||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
|
@ -100,6 +101,15 @@ class _MyAppState extends ConsumerState<MyApp> {
|
|||
}
|
||||
});
|
||||
super.initState();
|
||||
var dispatcher = SchedulerBinding.instance.platformDispatcher;
|
||||
dispatcher.onPlatformBrightnessChanged = () {
|
||||
var brightness = dispatcher.platformBrightness;
|
||||
if (brightness == Brightness.light) {
|
||||
ref.read(themeModeStateProvider.notifier).setLightTheme();
|
||||
} else {
|
||||
ref.read(themeModeStateProvider.notifier).setDarkTheme();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ class Settings {
|
|||
|
||||
bool? themeIsDark;
|
||||
|
||||
bool? followSystemTheme;
|
||||
|
||||
bool? incognitoMode;
|
||||
|
||||
List<ChapterPageurls>? chapterPageUrlsList;
|
||||
|
|
@ -254,6 +256,7 @@ class Settings {
|
|||
this.relativeTimesTamps = 2,
|
||||
this.flexSchemeColorIndex = 2,
|
||||
this.themeIsDark = false,
|
||||
this.followSystemTheme = false,
|
||||
this.incognitoMode = false,
|
||||
this.chapterPageUrlsList,
|
||||
this.showPagesNumber = true,
|
||||
|
|
@ -472,6 +475,7 @@ class Settings {
|
|||
.toList();
|
||||
}
|
||||
themeIsDark = json['themeIsDark'];
|
||||
followSystemTheme = json['followSystemTheme'];
|
||||
userAgent = json['userAgent'];
|
||||
backupFrequency = json['backupFrequency'];
|
||||
backupListOptions = json['backupListOptions']?.cast<int>();
|
||||
|
|
@ -637,6 +641,7 @@ class Settings {
|
|||
'sortLibraryAnime': sortLibraryAnime?.toJson(),
|
||||
'sortLibraryManga': sortLibraryManga?.toJson(),
|
||||
'themeIsDark': themeIsDark,
|
||||
'followSystemTheme': followSystemTheme,
|
||||
'userAgent': userAgent,
|
||||
'backupFrequency': backupFrequency,
|
||||
'backupListOptions': backupListOptions,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart' as riv;
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/changed.dart';
|
||||
import 'package:mangayomi/models/chapter.dart';
|
||||
import 'package:mangayomi/models/manga.dart';
|
||||
import 'package:mangayomi/models/video.dart' as vid;
|
||||
import 'package:mangayomi/modules/anime/providers/anime_player_controller_provider.dart';
|
||||
import 'package:mangayomi/modules/anime/widgets/aniskip_countdown_btn.dart';
|
||||
|
|
@ -19,9 +21,11 @@ import 'package:mangayomi/modules/anime/widgets/subtitle_view.dart';
|
|||
import 'package:mangayomi/modules/anime/widgets/subtitle_setting_widget.dart';
|
||||
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
|
||||
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'package:mangayomi/modules/widgets/custom_draggable_tabbar.dart';
|
||||
import 'package:mangayomi/modules/widgets/progress_center.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/services/aniskip.dart';
|
||||
import 'package:mangayomi/services/get_video_list.dart';
|
||||
import 'package:mangayomi/services/torrent_server.dart';
|
||||
|
|
@ -30,8 +34,11 @@ import 'package:mangayomi/utils/language.dart';
|
|||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:super_sliver_list/super_sliver_list.dart';
|
||||
|
||||
bool _isDesktop = Platform.isMacOS || Platform.isLinux || Platform.isWindows;
|
||||
|
||||
|
|
@ -209,6 +216,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
bool _hasOpeningSkip = false;
|
||||
bool _hasEndingSkip = false;
|
||||
bool _initSubtitleAndAudio = true;
|
||||
bool _includeSubtitles = false;
|
||||
|
||||
late StreamSubscription<Duration> _currentPositionSub = _player
|
||||
.stream
|
||||
|
|
@ -1174,6 +1182,16 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
}
|
||||
},
|
||||
),
|
||||
btnToShowShareScreenshot(
|
||||
widget.episode,
|
||||
onChanged: (v) {
|
||||
if (v) {
|
||||
_player.play();
|
||||
} else {
|
||||
_player.pause();
|
||||
}
|
||||
},
|
||||
),
|
||||
// IconButton(
|
||||
// onPressed: () {
|
||||
// showDialog(
|
||||
|
|
@ -1373,6 +1391,205 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
|
|||
);
|
||||
}
|
||||
|
||||
Widget btnToShowShareScreenshot(
|
||||
Chapter episode, {
|
||||
void Function(bool)? onChanged,
|
||||
}) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
onChanged?.call(false);
|
||||
Widget button(String label, IconData icon, Function() onPressed) =>
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
shadowColor: Colors.transparent,
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Icon(icon),
|
||||
),
|
||||
Text(label),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final name =
|
||||
"${episode.manga.value!.name} ${episode.name} - ${_currentPosition.value.toString()}"
|
||||
.replaceAll(RegExp(r'[^a-zA-Z0-9 .()\-\s]'), '_');
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
constraints: BoxConstraints(maxWidth: context.width(1)),
|
||||
builder: (context) {
|
||||
return SuperListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
color: context.themeData.scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
height: 7,
|
||||
width: 35,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: context.secondaryColor.withValues(
|
||||
alpha: 0.4,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
button(
|
||||
context.l10n.set_as_cover,
|
||||
Icons.image_outlined,
|
||||
() async {
|
||||
final imageBytes = await _player.screenshot(
|
||||
format: "image/png",
|
||||
includeLibassSubtitles: _includeSubtitles,
|
||||
);
|
||||
if (context.mounted) {
|
||||
final res = await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
content: Text(
|
||||
context.l10n.use_this_as_cover_art,
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(context.l10n.cancel),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final manga =
|
||||
episode.manga.value!;
|
||||
isar.writeTxnSync(() {
|
||||
isar.mangas.putSync(
|
||||
manga
|
||||
..customCoverImage =
|
||||
imageBytes,
|
||||
);
|
||||
ref
|
||||
.read(
|
||||
synchingProvider(
|
||||
syncId: 1,
|
||||
).notifier,
|
||||
)
|
||||
.addChangedPart(
|
||||
ActionType.updateItem,
|
||||
manga.id,
|
||||
manga.toJson(),
|
||||
false,
|
||||
);
|
||||
});
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context, "ok");
|
||||
}
|
||||
},
|
||||
child: Text(context.l10n.ok),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
if (res != null &&
|
||||
res == "ok" &&
|
||||
context.mounted) {
|
||||
Navigator.pop(context);
|
||||
botToast(
|
||||
context.l10n.cover_updated,
|
||||
second: 3,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
button(
|
||||
context.l10n.share,
|
||||
Icons.share_outlined,
|
||||
() async {
|
||||
final imageBytes = await _player.screenshot(
|
||||
format: "image/png",
|
||||
includeLibassSubtitles: _includeSubtitles,
|
||||
);
|
||||
await Share.shareXFiles([
|
||||
XFile.fromData(
|
||||
imageBytes!,
|
||||
name: name,
|
||||
mimeType: 'image/png',
|
||||
),
|
||||
]);
|
||||
},
|
||||
),
|
||||
button(
|
||||
context.l10n.save,
|
||||
Icons.save_outlined,
|
||||
() async {
|
||||
final imageBytes = await _player.screenshot(
|
||||
format: "image/png",
|
||||
includeLibassSubtitles: _includeSubtitles,
|
||||
);
|
||||
final dir =
|
||||
await StorageProvider().getGalleryDirectory();
|
||||
final file = File(p.join(dir!.path, "$name.png"));
|
||||
file.writeAsBytesSync(imageBytes!);
|
||||
if (context.mounted) {
|
||||
botToast(context.l10n.picture_saved, second: 3);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SwitchListTile(
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_includeSubtitles = value;
|
||||
});
|
||||
},
|
||||
title: Text(context.l10n.include_subtitles),
|
||||
value: _includeSubtitles,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
onChanged?.call(true);
|
||||
},
|
||||
icon: Icon(Icons.adaptive.share),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(body: _videoPlayer(context));
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'download_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$downloadChapterHash() => r'08a7196ae7da5d980629ef80d04ab9b251006eaf';
|
||||
String _$downloadChapterHash() => r'bf43fddf83fce382ff794c688288153477f9a3aa';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ class _RestoreKotatsuBackupProviderElement
|
|||
}
|
||||
|
||||
String _$restoreTachiBkBackupHash() =>
|
||||
r'8f45e43afa63618ee9b7c4bdd5cae50a2dc30b1a';
|
||||
r'76021dbcf0d576b50379f19f17e6a3ee8434942c';
|
||||
|
||||
/// See also [restoreTachiBkBackup].
|
||||
@ProviderFor(restoreTachiBkBackup)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/app_font_family.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/widgets/follow_system_theme_button.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/utils/date.dart';
|
||||
|
|
@ -71,6 +72,7 @@ class AppearanceScreen extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
const DarkModeButton(),
|
||||
const FollowSystemThemeButton(),
|
||||
const ThemeSelector(),
|
||||
if (isDarkTheme)
|
||||
Padding(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/flex_scheme_color_state_provider.dart';
|
||||
|
|
@ -39,3 +42,27 @@ class ThemeModeState extends _$ThemeModeState {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class FollowSystemThemeState extends _$FollowSystemThemeState {
|
||||
@override
|
||||
bool build() {
|
||||
return isar.settings.getSync(227)!.followSystemTheme ?? false;
|
||||
}
|
||||
|
||||
void set(bool value) {
|
||||
final settings = isar.settings.getSync(227);
|
||||
state = value;
|
||||
if (value) {
|
||||
if (WidgetsBinding.instance.platformDispatcher.platformBrightness ==
|
||||
Brightness.light) {
|
||||
ref.read(themeModeStateProvider.notifier).setLightTheme();
|
||||
} else {
|
||||
ref.read(themeModeStateProvider.notifier).setDarkTheme();
|
||||
}
|
||||
}
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(settings!..followSystemTheme = value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,5 +22,22 @@ final themeModeStateProvider =
|
|||
);
|
||||
|
||||
typedef _$ThemeModeState = AutoDisposeNotifier<bool>;
|
||||
String _$followSystemThemeStateHash() =>
|
||||
r'0c65e1471807dddb2f536d336d1f44b29e9e3a5a';
|
||||
|
||||
/// See also [FollowSystemThemeState].
|
||||
@ProviderFor(FollowSystemThemeState)
|
||||
final followSystemThemeStateProvider =
|
||||
AutoDisposeNotifierProvider<FollowSystemThemeState, bool>.internal(
|
||||
FollowSystemThemeState.new,
|
||||
name: r'followSystemThemeStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$followSystemThemeStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$FollowSystemThemeState = AutoDisposeNotifier<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_state_provider.dart';
|
||||
|
||||
class FollowSystemThemeButton extends ConsumerStatefulWidget {
|
||||
const FollowSystemThemeButton({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<FollowSystemThemeButton> createState() => _FollowSystemThemeButtonState();
|
||||
}
|
||||
|
||||
class _FollowSystemThemeButtonState extends ConsumerState<FollowSystemThemeButton> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isFollow = ref.watch(followSystemThemeStateProvider);
|
||||
final l10n = l10nLocalizations(context);
|
||||
return SwitchListTile(
|
||||
onChanged: (value) {
|
||||
ref.read(followSystemThemeStateProvider.notifier).set(value);
|
||||
},
|
||||
title: Text(l10n!.follow_system_theme),
|
||||
subtitle: Text(
|
||||
!isFollow ? l10n.off : l10n.on,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
value: isFollow,
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue