added discord rpc

This commit is contained in:
Schnitzel5 2025-07-18 01:02:59 +02:00
parent 230f9c59a3
commit d139042241
37 changed files with 1453 additions and 333 deletions

View file

@ -463,5 +463,10 @@
"track_library_not_logged": "Login to the corresponding tracker to use this feature!",
"track_library_switch": "Switch to another tracker",
"go_back": "Go back",
"merge_library_nav_mobile": "Merge library navigation on mobile"
}
"merge_library_nav_mobile": "Merge library navigation on mobile",
"enable_discord_rpc": "Enable Discord RPC",
"hide_discord_rpc_incognito": "Hide Discord RPC while in Incognito",
"rpc_show_reading_watching_progress": "Show current chapter in Discord (requires a restart)",
"rpc_show_title": "Show current title in Discord",
"rpc_show_cover_image": "Show current cover image in Discord"
}

View file

@ -2854,6 +2854,36 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Merge library navigation on mobile'**
String get merge_library_nav_mobile;
/// No description provided for @enable_discord_rpc.
///
/// In en, this message translates to:
/// **'Enable Discord RPC'**
String get enable_discord_rpc;
/// No description provided for @hide_discord_rpc_incognito.
///
/// In en, this message translates to:
/// **'Hide Discord RPC while in Incognito'**
String get hide_discord_rpc_incognito;
/// No description provided for @rpc_show_reading_watching_progress.
///
/// In en, this message translates to:
/// **'Show current chapter in Discord (requires a restart)'**
String get rpc_show_reading_watching_progress;
/// No description provided for @rpc_show_title.
///
/// In en, this message translates to:
/// **'Show current title in Discord'**
String get rpc_show_title;
/// No description provided for @rpc_show_cover_image.
///
/// In en, this message translates to:
/// **'Show current cover image in Discord'**
String get rpc_show_cover_image;
}
class _AppLocalizationsDelegate

View file

@ -1468,4 +1468,21 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1481,4 +1481,21 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1469,4 +1469,21 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1486,6 +1486,23 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}
/// The translations for Spanish Castilian, as used in Latin America and the Caribbean (`es_419`).

View file

@ -1487,4 +1487,21 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1475,4 +1475,21 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1484,4 +1484,21 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1483,6 +1483,23 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}
/// The translations for Portuguese, as used in Brazil (`pt_BR`).

View file

@ -1485,4 +1485,21 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1469,4 +1469,21 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1475,4 +1475,21 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -1440,4 +1440,21 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get merge_library_nav_mobile => 'Merge library navigation on mobile';
@override
String get enable_discord_rpc => 'Enable Discord RPC';
@override
String get hide_discord_rpc_incognito =>
'Hide Discord RPC while in Incognito';
@override
String get rpc_show_reading_watching_progress =>
'Show current chapter in Discord (requires a restart)';
@override
String get rpc_show_title => 'Show current title in Discord';
@override
String get rpc_show_cover_image => 'Show current cover image in Discord';
}

View file

@ -26,6 +26,7 @@ import 'package:mangayomi/modules/more/settings/appearance/providers/theme_mode_
import 'package:mangayomi/l10n/generated/app_localizations.dart';
import 'package:mangayomi/services/http/m_client.dart';
import 'package:mangayomi/src/rust/frb_generated.dart';
import 'package:mangayomi/utils/discord_rpc.dart';
import 'package:mangayomi/utils/url_protocol/api.dart';
import 'package:mangayomi/modules/more/settings/appearance/providers/theme_provider.dart';
import 'package:mangayomi/modules/library/providers/file_scanner.dart';
@ -35,6 +36,7 @@ import 'package:window_manager/window_manager.dart';
import 'package:path/path.dart' as p;
late Isar isar;
late DiscordRPC discordRpc;
WebViewEnvironment? webViewEnvironment;
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
@ -61,6 +63,8 @@ void main(List<String> args) async {
isar = await StorageProvider().initDB(null, inspector: kDebugMode);
await Hive.initFlutter();
Hive.registerAdapter(TrackSearchAdapter());
discordRpc = DiscordRPC(applicationId: "1395040506677039157");
await discordRpc.initialize();
runApp(const ProviderScope(child: MyApp()));
unawaited(_postLaunchInit()); // Defer non-essential async operations
@ -129,6 +133,7 @@ class _MyAppState extends ConsumerState<MyApp> {
@override
void dispose() {
_linkSubscription?.cancel();
discordRpc.destroy();
super.dispose();
}

View file

@ -246,6 +246,16 @@ class Settings {
bool? mergeLibraryNavMobile;
bool? enableDiscordRpc;
bool? hideDiscordRpcInIncognito;
bool? rpcShowReadingWatchingProgress;
bool? rpcShowTitle;
bool? rpcShowCoverImage;
Settings({
this.id = 227,
this.displayType = DisplayType.compactGrid,
@ -355,6 +365,11 @@ class Settings {
this.novelExtensionsRepo,
this.lastTrackerLibraryLocation,
this.mergeLibraryNavMobile = false,
this.enableDiscordRpc = true,
this.hideDiscordRpcInIncognito = true,
this.rpcShowReadingWatchingProgress = true,
this.rpcShowTitle = true,
this.rpcShowCoverImage = true,
});
Settings.fromJson(Map<String, dynamic> json) {
@ -570,6 +585,11 @@ class Settings {
}
lastTrackerLibraryLocation = json['lastTrackerLibraryLocation'];
mergeLibraryNavMobile = json['mergeLibraryNavMobile'];
enableDiscordRpc = json['enableDiscordRpc'];
hideDiscordRpcInIncognito = json['hideDiscordRpcInIncognito'];
rpcShowReadingWatchingProgress = json['rpcShowReadingWatchingProgress'];
rpcShowTitle = json['rpcShowTitle'];
rpcShowCoverImage = json['rpcShowCoverImage'];
}
Map<String, dynamic> toJson() => {
@ -702,6 +722,11 @@ class Settings {
'novelExtensionsRepo': novelExtensionsRepo?.map((e) => e.toJson()).toList(),
'lastTrackerLibraryLocation': lastTrackerLibraryLocation,
'mergeLibraryNavMobile': mergeLibraryNavMobile,
'enableDiscordRpc': enableDiscordRpc,
'hideDiscordRpcInIncognito': hideDiscordRpcInIncognito,
'rpcShowReadingWatchingProgress': rpcShowReadingWatchingProgress,
'rpcShowTitle': rpcShowTitle,
'rpcShowCoverImage': rpcShowCoverImage,
};
}

File diff suppressed because it is too large Load diff

View file

@ -213,6 +213,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
bool _hasEndingSkip = false;
bool _initSubtitleAndAudio = true;
bool _includeSubtitles = false;
int lastRpcTimestampUpdate = DateTime.now().millisecondsSinceEpoch;
late final StreamSubscription<Duration> _currentPositionSub;
@ -221,6 +222,10 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
.duration
.listen((duration) {
_currentTotalDuration.value = duration;
discordRpc.startChapterTimestamp(
_currentPosition.value.inMilliseconds,
duration.inMilliseconds,
);
});
bool get hasNextEpisode => _streamController.getEpisodeIndex().$1 != 0;
@ -305,6 +310,14 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
if (_skipPhase.value != newPhase) _skipPhase.value = newPhase;
}
void _updateRpcTimestamp() {
final now = DateTime.now().millisecondsSinceEpoch;
if (lastRpcTimestampUpdate + 10000 < now) {
discordRpc.updateChapterTimestamp(_currentPosition.value.inMilliseconds);
lastRpcTimestampUpdate = now;
}
}
@override
void initState() {
super.initState();
@ -350,6 +363,8 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
_setPlaybackSpeed(ref.read(defaultPlayBackSpeedStateProvider));
if (ref.read(enableAniSkipStateProvider)) _initAniSkip();
});
discordRpc.showChapterDetails(ref, widget.episode);
_currentPosition.addListener(_updateRpcTimestamp);
}
Future<void> _loadAndroidFont() async {
@ -397,6 +412,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
@override
void dispose() {
_currentPosition.removeListener(_updateRpcTimestamp);
_setCurrentPosition(true);
_player.dispose();
_currentPositionSub.cancel();
@ -406,6 +422,8 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage>
_setLandscapeMode(false);
}
_skipPhase.dispose();
discordRpc.showIdleText();
discordRpc.showOriginalTimestamp();
super.dispose();
}

View file

@ -96,6 +96,8 @@ class _MainScreenState extends ConsumerState<MainScreen> {
_initializeProviders();
}
});
discordRpc.connect(ref);
}
void _initializeTimers() {
@ -158,6 +160,7 @@ class _MainScreenState extends ConsumerState<MainScreen> {
void dispose() {
_backupTimer?.cancel();
_syncTimer?.cancel();
discordRpc.disconnect();
super.dispose();
}

View file

@ -158,7 +158,7 @@ class _AddDownloadToQueueProviderElement
Chapter get chapter => (origin as AddDownloadToQueueProvider).chapter;
}
String _$downloadChapterHash() => r'e712508a81c414f66b12adeae23c3dc70380a920';
String _$downloadChapterHash() => r'914d4c0fda7d031878ef9c7ac0b3a44241558d5d';
/// See also [downloadChapter].
@ProviderFor(downloadChapter)

View file

@ -154,6 +154,7 @@ class _MangaChapterPageGalleryState
overlays: SystemUiOverlay.values,
);
}
discordRpc.showIdleText();
super.dispose();
}
@ -201,6 +202,7 @@ class _MangaChapterPageGalleryState
_animation.addListener(() => _photoViewController.scale = _animation.value);
_itemPositionsListener.itemPositions.addListener(_readProgressListener);
_initCurrentIndex();
discordRpc.showChapterDetails(ref, chapter);
}
final double _horizontalScaleValue = 1.0;

View file

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/modules/more/settings/general/providers/general_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/language.dart';
import 'package:mangayomi/l10n/generated/app_localizations.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class GeneralScreen extends ConsumerWidget {
const GeneralScreen({super.key});
@ -12,70 +10,63 @@ class GeneralScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = l10nLocalizations(context);
final l10nLocale = ref.watch(l10nLocaleStateProvider);
final enableDiscordRpc = ref.watch(enableDiscordRpcStateProvider);
final hideDiscordRpcInIncognito = ref.watch(
hideDiscordRpcInIncognitoStateProvider,
);
final rpcShowReadingWatchingProgress = ref.watch(
rpcShowReadingWatchingProgressStateProvider,
);
final rpcShowTitleState = ref.watch(rpcShowTitleStateProvider);
final rpcShowCoverImage = ref.watch(rpcShowCoverImageStateProvider);
return Scaffold(
appBar: AppBar(title: Text(l10n!.general)),
body: SingleChildScrollView(
child: Column(
children: [
ListTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(l10n.app_language),
content: SizedBox(
width: context.width(0.8),
child: SuperListView.builder(
shrinkWrap: true,
itemCount: AppLocalizations.supportedLocales.length,
itemBuilder: (context, index) {
final locale =
AppLocalizations.supportedLocales[index];
return RadioListTile(
dense: true,
contentPadding: const EdgeInsets.all(0),
value: locale,
groupValue: l10nLocale,
onChanged: (value) {
ref
.read(l10nLocaleStateProvider.notifier)
.setLocale(locale);
Navigator.pop(context);
},
title: Text(
completeLanguageName(locale.toLanguageTag()),
),
);
},
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () async {
Navigator.pop(context);
},
child: Text(
l10n.cancel,
style: TextStyle(color: context.primaryColor),
),
),
],
),
],
);
},
);
SwitchListTile(
value: enableDiscordRpc,
title: Text(l10n.enable_discord_rpc),
onChanged: (value) {
ref.read(enableDiscordRpcStateProvider.notifier).set(value);
if (value) {
discordRpc.connect(ref);
} else {
discordRpc.disconnect();
}
},
),
SwitchListTile(
value: hideDiscordRpcInIncognito,
title: Text(l10n.hide_discord_rpc_incognito),
onChanged: (value) {
ref
.read(hideDiscordRpcInIncognitoStateProvider.notifier)
.set(value);
},
),
SwitchListTile(
value: rpcShowReadingWatchingProgress,
title: Text(l10n.rpc_show_reading_watching_progress),
onChanged: (value) {
ref
.read(rpcShowReadingWatchingProgressStateProvider.notifier)
.set(value);
},
),
SwitchListTile(
value: rpcShowTitleState,
title: Text(l10n.rpc_show_title),
onChanged: (value) {
ref.read(rpcShowTitleStateProvider.notifier).set(value);
},
),
SwitchListTile(
value: rpcShowCoverImage,
title: Text(l10n.rpc_show_cover_image),
onChanged: (value) {
ref.read(rpcShowCoverImageStateProvider.notifier).set(value);
},
title: Text(l10n.app_language),
subtitle: Text(
completeLanguageName(l10nLocale.toLanguageTag()),
style: TextStyle(fontSize: 11, color: context.secondaryColor),
),
),
],
),

View file

@ -0,0 +1,87 @@
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'general_state_provider.g.dart';
@riverpod
class EnableDiscordRpcState extends _$EnableDiscordRpcState {
@override
bool build() {
return isar.settings.getSync(227)!.enableDiscordRpc ?? true;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..enableDiscordRpc = value),
);
}
}
@riverpod
class HideDiscordRpcInIncognitoState extends _$HideDiscordRpcInIncognitoState {
@override
bool build() {
return isar.settings.getSync(227)!.hideDiscordRpcInIncognito ?? true;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..hideDiscordRpcInIncognito = value),
);
}
}
@riverpod
class RpcShowReadingWatchingProgressState
extends _$RpcShowReadingWatchingProgressState {
@override
bool build() {
return isar.settings.getSync(227)!.rpcShowReadingWatchingProgress ?? true;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(
settings!..rpcShowReadingWatchingProgress = value,
),
);
}
}
@riverpod
class RpcShowTitleState extends _$RpcShowTitleState {
@override
bool build() {
return isar.settings.getSync(227)!.rpcShowTitle ?? true;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..rpcShowTitle = value),
);
}
}
@riverpod
class RpcShowCoverImageState extends _$RpcShowCoverImageState {
@override
bool build() {
return isar.settings.getSync(227)!.rpcShowCoverImage ?? true;
}
void set(bool value) {
final settings = isar.settings.getSync(227);
state = value;
isar.writeTxnSync(
() => isar.settings.putSync(settings!..rpcShowCoverImage = value),
);
}
}

View file

@ -0,0 +1,94 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'general_state_provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$enableDiscordRpcStateHash() =>
r'bc134787a33f362087befd5de85692812659faa0';
/// See also [EnableDiscordRpcState].
@ProviderFor(EnableDiscordRpcState)
final enableDiscordRpcStateProvider =
AutoDisposeNotifierProvider<EnableDiscordRpcState, bool>.internal(
EnableDiscordRpcState.new,
name: r'enableDiscordRpcStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$enableDiscordRpcStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$EnableDiscordRpcState = AutoDisposeNotifier<bool>;
String _$hideDiscordRpcInIncognitoStateHash() =>
r'8e45104c34b1255b7a3d4a6f182bf43be9e2b93d';
/// See also [HideDiscordRpcInIncognitoState].
@ProviderFor(HideDiscordRpcInIncognitoState)
final hideDiscordRpcInIncognitoStateProvider =
AutoDisposeNotifierProvider<HideDiscordRpcInIncognitoState, bool>.internal(
HideDiscordRpcInIncognitoState.new,
name: r'hideDiscordRpcInIncognitoStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$hideDiscordRpcInIncognitoStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$HideDiscordRpcInIncognitoState = AutoDisposeNotifier<bool>;
String _$rpcShowReadingWatchingProgressStateHash() =>
r'b4000c10234ce9070b44b535e9d10d16e6b7c0ec';
/// See also [RpcShowReadingWatchingProgressState].
@ProviderFor(RpcShowReadingWatchingProgressState)
final rpcShowReadingWatchingProgressStateProvider = AutoDisposeNotifierProvider<
RpcShowReadingWatchingProgressState, bool>.internal(
RpcShowReadingWatchingProgressState.new,
name: r'rpcShowReadingWatchingProgressStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$rpcShowReadingWatchingProgressStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$RpcShowReadingWatchingProgressState = AutoDisposeNotifier<bool>;
String _$rpcShowTitleStateHash() => r'9d7005af8968f61872fcc37979f5c54b745ffe65';
/// See also [RpcShowTitleState].
@ProviderFor(RpcShowTitleState)
final rpcShowTitleStateProvider =
AutoDisposeNotifierProvider<RpcShowTitleState, bool>.internal(
RpcShowTitleState.new,
name: r'rpcShowTitleStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$rpcShowTitleStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$RpcShowTitleState = AutoDisposeNotifier<bool>;
String _$rpcShowCoverImageStateHash() =>
r'6ce1c61be44c0a6315fbb51a45ac6e47c3e49b33';
/// See also [RpcShowCoverImageState].
@ProviderFor(RpcShowCoverImageState)
final rpcShowCoverImageStateProvider =
AutoDisposeNotifierProvider<RpcShowCoverImageState, bool>.internal(
RpcShowCoverImageState.new,
name: r'rpcShowCoverImageStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$rpcShowCoverImageStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$RpcShowCoverImageState = 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

View file

@ -14,6 +14,11 @@ class SettingsScreen extends StatelessWidget {
body: SingleChildScrollView(
child: Column(
children: [
ListTileWidget(
title: l10n.general,
icon: Icons.settings,
onTap: () => context.push('/general'),
),
ListTileWidget(
title: l10n.appearance,
subtitle: l10n.appearance_subtitle,

View file

@ -1,7 +1,9 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
import 'package:mangayomi/modules/more/settings/general/providers/general_state_provider.dart';
import 'package:mangayomi/modules/more/widgets/list_tile_widget.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
@ -12,12 +14,17 @@ class IncognitoModeWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = l10nLocalizations(context);
final incognitoMode = ref.watch(incognitoModeStateProvider);
final hideDiscordRpcInIncognito = ref.watch(
hideDiscordRpcInIncognitoStateProvider,
);
return ListTileWidget(
onTap: () {
if (incognitoMode == true) {
ref.read(incognitoModeStateProvider.notifier).setIncognitoMode(false);
if (hideDiscordRpcInIncognito) discordRpc.connect(ref);
} else {
ref.read(incognitoModeStateProvider.notifier).setIncognitoMode(true);
if (hideDiscordRpcInIncognito) discordRpc.disconnect();
}
},
icon: CupertinoIcons.eyeglasses,
@ -27,6 +34,13 @@ class IncognitoModeWidget extends ConsumerWidget {
value: incognitoMode,
onChanged: (value) {
ref.read(incognitoModeStateProvider.notifier).setIncognitoMode(value);
if (hideDiscordRpcInIncognito) {
if (value) {
discordRpc.disconnect();
} else {
discordRpc.connect(ref);
}
}
},
),
);

View file

@ -94,6 +94,7 @@ class _NovelWebViewState extends ConsumerState<NovelWebView>
overlays: SystemUiOverlay.values,
);
}
discordRpc.showIdleText();
super.dispose();
}
@ -111,6 +112,7 @@ class _NovelWebViewState extends ConsumerState<NovelWebView>
fontSize = initFontSize;
});
});
discordRpc.showChapterDetails(ref, chapter);
}
late bool _isBookmarked = _readerController.getChapterBookmarked();

View file

@ -6,7 +6,7 @@ part of 'aniskip.dart';
// RiverpodGenerator
// **************************************************************************
String _$aniSkipHash() => r'887869b54e2e151633efd46da83bde845e14f421';
String _$aniSkipHash() => r'2e5d19b025a2207ff64da7bf7908450ea9e5ff8c';
/// See also [AniSkip].
@ProviderFor(AniSkip)

View file

@ -6,7 +6,7 @@ part of 'get_video_list.dart';
// RiverpodGenerator
// **************************************************************************
String _$getVideoListHash() => r'1d34225b32d5c0475f06c7066792837d4b800800';
String _$getVideoListHash() => r'74838334ee25412c4a3f151bc598705145bb659c';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -6,7 +6,7 @@ part of 'anilist.dart';
// RiverpodGenerator
// **************************************************************************
String _$anilistHash() => r'8cd0495c613bbb8c86a61e990f1755b58b983d79';
String _$anilistHash() => r'fafb964252b3a5741e981cb8c2f0f2090b3b86ae';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -6,7 +6,7 @@ part of 'kitsu.dart';
// RiverpodGenerator
// **************************************************************************
String _$kitsuHash() => r'970f2e278a3f0163da30684e030c3a7833bf9ae2';
String _$kitsuHash() => r'e24e9b57cfea974110d1f7c704c306c3b58e3529';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -6,7 +6,7 @@ part of 'myanimelist.dart';
// RiverpodGenerator
// **************************************************************************
String _$myAnimeListHash() => r'98746d3f3ac88a6165b46de18acb268dc32a16f8';
String _$myAnimeListHash() => r'8e21378e2a3ccdf696a0a1c9a88a05123f65eacb';
/// Copied from Dart SDK
class _SystemHash {

248
lib/utils/discord_rpc.dart Normal file
View file

@ -0,0 +1,248 @@
import 'package:flutter_discord_rpc_fork/flutter_discord_rpc.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/modules/more/providers/incognito_mode_state_provider.dart';
import 'package:mangayomi/modules/more/settings/general/providers/general_state_provider.dart';
class DiscordRPC {
/// Id of the Discord Application
final String applicationId;
/// Whether it should reconnect to the discord client
final bool autoRetry;
/// Seconds between each attempt to reconnect
final int retryDelayInSeconds;
/// Start timestamp in millis
final int startAt = DateTime.timestamp().millisecondsSinceEpoch;
/// Start timestamp in millis for the current chapter/episode
int chapterStartAt = 0;
/// End timestamp in millis for the current chapter/episode
int chapterEndAt = 0;
/// Temp var
late bool rpcShowReadingWatchingProgress;
/// Instance of the current RPC activity
final RpcActivity activity = RpcActivity(
assets: const RPCAssets(largeImage: "app-icon", largeText: "Mangayomi"),
buttons: [
const RPCButton(
label: "Get Mangayomi",
url: "https://github.com/kodjodevf/mangayomi",
),
const RPCButton(
label: "Join us",
url: "https://discord.com/invite/EjfBuYahsP",
),
],
details: "Idle",
state: "-----",
timestamps: RPCTimestamps(
start: DateTime.timestamp().millisecondsSinceEpoch,
),
activityType: ActivityType.watching,
);
DiscordRPC({
required this.applicationId,
this.autoRetry = true,
this.retryDelayInSeconds = 10,
});
Future<void> initialize() async {
await FlutterDiscordRPC.initialize(applicationId);
}
Future<void> connect(WidgetRef ref) async {
final enableDiscordRpc = ref.read(enableDiscordRpcStateProvider);
final incognitoMode = ref.read(incognitoModeStateProvider);
final hideDiscordRpcInIncognito = ref.read(
hideDiscordRpcInIncognitoStateProvider,
);
rpcShowReadingWatchingProgress = ref.read(
rpcShowReadingWatchingProgressStateProvider,
);
if (enableDiscordRpc && (!hideDiscordRpcInIncognito || !incognitoMode)) {
await FlutterDiscordRPC.instance.connect(
autoRetry: autoRetry,
retryDelay: Duration(seconds: retryDelayInSeconds),
);
await Future.delayed(Duration(seconds: 3));
showIdleText();
}
}
Future<void> showIdleText() async {
await updateActivity(
details: "Idle",
state: "-----",
assets: const RPCAssets(largeImage: "app-icon", largeText: "Mangayomi"),
);
}
Future<void> showChapterDetails(WidgetRef ref, Chapter chapter) async {
final status = chapter.manga.value!.itemType == ItemType.anime
? "Watching"
: "Reading";
final title = chapter.manga.value!.name;
final chapterTitle = chapter.name;
final rpcShowTitle = ref.read(rpcShowTitleStateProvider);
final rpcShowCoverImage = ref.read(rpcShowCoverImageStateProvider);
await updateActivity(
details: rpcShowTitle ? "$status $title" : "Idle",
state: rpcShowTitle && rpcShowReadingWatchingProgress
? chapterTitle
: "-----",
assets: rpcShowCoverImage
? RPCAssets(
largeImage: chapter.manga.value!.imageUrl,
largeText: rpcShowTitle ? chapter.manga.value!.name : "-----",
smallImage: "app-icon",
smallText: "Mangayomi",
)
: const RPCAssets(largeImage: "app-icon", largeText: "Mangayomi"),
);
}
Future<void> showLargeImage() async {
await updateActivity(
assets: const RPCAssets(largeImage: "app-icon", largeText: "Mangayomi"),
);
}
Future<void> showSmallImage(String largeImage, String largeText) async {
await updateActivity(
assets: RPCAssets(
largeImage: largeImage,
largeText: largeText,
smallImage: "app-icon",
smallText: "Mangayomi",
),
);
}
Future<void> showOriginalTimestamp() async {
await updateActivity(timestamps: RPCTimestamps(start: startAt));
}
Future<void> startChapterTimestamp(
int offsetInMillis,
int durationInMillis,
) async {
if (!rpcShowReadingWatchingProgress) {
return;
}
chapterStartAt = DateTime.timestamp().millisecondsSinceEpoch;
chapterEndAt =
DateTime.timestamp().millisecondsSinceEpoch + durationInMillis;
await updateActivity(
timestamps: RPCTimestamps(
start: chapterStartAt,
end: chapterEndAt - offsetInMillis,
),
);
}
Future<void> updateChapterTimestamp(int newOffsetInMillis) async {
if (!rpcShowReadingWatchingProgress) {
return;
}
await updateActivity(
timestamps: RPCTimestamps(
start: chapterStartAt,
end: chapterEndAt - newOffsetInMillis,
),
);
}
Future<void> updateActivity({
String? state,
String? details,
RPCTimestamps? timestamps,
RPCParty? party,
RPCAssets? assets,
RPCSecrets? secrets,
List<RPCButton>? buttons,
ActivityType? activityType,
}) async {
if (!FlutterDiscordRPC.instance.isConnected) {
return;
}
if (state != null) {
activity.state = state;
}
if (details != null) {
activity.details = details;
}
if (timestamps != null) {
activity.timestamps = timestamps;
}
if (party != null) {
activity.party = party;
}
if (assets != null) {
activity.assets = assets;
}
if (secrets != null) {
activity.secrets = secrets;
}
if (buttons != null) {
activity.buttons = buttons;
}
if (activityType != null) {
activity.activityType = activityType;
}
await FlutterDiscordRPC.instance.setActivity(
activity: activity.toRPCActivity(),
);
}
Future<void> disconnect() async {
await FlutterDiscordRPC.instance.disconnect();
}
Future<void> destroy() async {
await FlutterDiscordRPC.instance.disconnect();
await FlutterDiscordRPC.instance.dispose();
}
}
class RpcActivity {
String? state;
String? details;
RPCTimestamps? timestamps;
RPCParty? party;
RPCAssets? assets;
RPCSecrets? secrets;
List<RPCButton>? buttons;
ActivityType? activityType;
RpcActivity({
this.state,
this.details,
this.timestamps,
this.party,
this.assets,
this.secrets,
this.buttons,
this.activityType,
});
RPCActivity toRPCActivity() {
return RPCActivity(
state: state,
details: details,
timestamps: timestamps,
party: party,
assets: assets,
secrets: secrets,
buttons: buttons,
activityType: activityType,
);
}
}

View file

@ -16,6 +16,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_discord_rpc_fork
rust_lib_mangayomi
)

View file

@ -549,6 +549,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_discord_rpc_fork:
dependency: "direct main"
description:
name: flutter_discord_rpc_fork
sha256: "0aeb2806307212d9ccb85a55272b3ced8d77c09d886b14698ba330f13ee52216"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
flutter_inappwebview:
dependency: "direct main"
description:

View file

@ -94,6 +94,7 @@ dependencies:
d4rt: 0.0.9
hive: ^2.2.3
hive_flutter: ^1.1.0
flutter_discord_rpc_fork: ^1.0.3
dependency_overrides:
ffi: ^2.1.3

View file

@ -21,6 +21,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_discord_rpc_fork
rust_lib_mangayomi
)