mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-31 04:32:32 +00:00
refactor(platform): guard usages of dart:io and dart:html for future compatability
This commit is contained in:
@@ -1,59 +1 @@
|
|||||||
{
|
{}
|
||||||
"motd": "Welcome to LunaSea v10.0.0!\n\nThis update does not have a lot of user-facing changes but introduces substantial reworkings of core integrations and processes that will help make new releases of LunaSea more stable and easier to deploy in the future.",
|
|
||||||
"new": [
|
|
||||||
{
|
|
||||||
"module": "Networking",
|
|
||||||
"changes": [
|
|
||||||
"(HTTP) Set user-agent to \"LunaSea/{version} {build}\""
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"module": "Radarr",
|
|
||||||
"changes": [
|
|
||||||
"(Files) Show additional media metadata"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"module": "Settings",
|
|
||||||
"changes": [
|
|
||||||
"(Resources) Add test build information"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"module": "Sonarr",
|
|
||||||
"changes": [
|
|
||||||
"(Series) Support for searching entire series for monitored episodes"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"module": "UI/UX",
|
|
||||||
"changes": [
|
|
||||||
"(Block) Support skeleton-state loading"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tweaks": [],
|
|
||||||
"fixes": [
|
|
||||||
{
|
|
||||||
"module": "Database",
|
|
||||||
"changes": [
|
|
||||||
"Database entries could be unloaded from memory in some cases"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"module": "Radarr",
|
|
||||||
"changes": [
|
|
||||||
"(Availability) Prevent using preDB availability for movies",
|
|
||||||
"(System Status) Hide irrelevant lines on about page for some installations"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"module": "UI/UX",
|
|
||||||
"changes": [
|
|
||||||
"(ListView) Correctly handle pull-to-refresh drag action on all platforms",
|
|
||||||
"(Input Bar) Saving inputs would not actually update the underlying value in some cases"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"platform": []
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
import 'package:firebase_storage/firebase_storage.dart';
|
import 'package:firebase_storage/firebase_storage.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import 'package:lunasea/modules/tautulli.dart';
|
|||||||
import '../modules/dashboard/core/database.dart';
|
import '../modules/dashboard/core/database.dart';
|
||||||
import '../modules/dashboard/core/state.dart' as dashboard_state;
|
import '../modules/dashboard/core/state.dart' as dashboard_state;
|
||||||
import '../modules/dashboard/routes/dashboard/route.dart' as dashboard_home;
|
import '../modules/dashboard/routes/dashboard/route.dart' as dashboard_home;
|
||||||
import '../modules/wake_on_lan/api/api.dart';
|
import '../modules/wake_on_lan/api/wake_on_lan.dart';
|
||||||
|
|
||||||
part 'modules.g.dart';
|
part 'modules.g.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/main.dart';
|
import 'package:lunasea/main.dart';
|
||||||
@@ -19,11 +18,7 @@ import '../../modules/dashboard/routes/routes.dart' as dashboard;
|
|||||||
|
|
||||||
class LunaRouter {
|
class LunaRouter {
|
||||||
static FluroRouter router = FluroRouter();
|
static FluroRouter router = FluroRouter();
|
||||||
static TransitionType get transitionType {
|
static TransitionType get transitionType => TransitionType.native;
|
||||||
// Required otherwise no transition is executed
|
|
||||||
if (Platform.isMacOS) return TransitionType.cupertino;
|
|
||||||
return TransitionType.native;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls `defineAllRoutes()` on all module routers that implement [LunaModuleRouter].
|
/// Calls `defineAllRoutes()` on all module routers that implement [LunaModuleRouter].
|
||||||
void initialize() {
|
void initialize() {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
export 'system/configuration.dart';
|
export 'system/configuration.dart';
|
||||||
export 'system/desktop_window.dart';
|
export 'system/desktop_window.dart';
|
||||||
export 'system/filesystem.dart';
|
|
||||||
export 'system/flavor.dart';
|
export 'system/flavor.dart';
|
||||||
export 'system/in_app_purchases.dart';
|
export 'system/in_app_purchases.dart';
|
||||||
export 'system/localization.dart';
|
export 'system/localization.dart';
|
||||||
export 'system/networking.dart';
|
|
||||||
export 'system/profile.dart';
|
export 'system/profile.dart';
|
||||||
export 'system/quick_actions.dart';
|
export 'system/quick_actions.dart';
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
|
import 'platform.dart';
|
||||||
|
|
||||||
class LunaDesktopWindow {
|
class LunaDesktopWindow {
|
||||||
static const double _MINIMUM_WINDOW_SIZE = 400;
|
static const double _MINIMUM_WINDOW_SIZE = 400;
|
||||||
static const double _INITIAL_WINDOW_SIZE = 700;
|
static const double _INITIAL_WINDOW_SIZE = 700;
|
||||||
|
|
||||||
static bool get isPlatformCompatible =>
|
static bool get isSupported => LunaPlatform().isDesktop;
|
||||||
Platform.isWindows || Platform.isMacOS || Platform.isLinux;
|
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
if (isPlatformCompatible) {
|
if (isSupported) {
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
windowManager.waitUntilReadyToShow().then((_) async {
|
windowManager.waitUntilReadyToShow().then((_) async {
|
||||||
const Size minSize = Size(_MINIMUM_WINDOW_SIZE, _MINIMUM_WINDOW_SIZE);
|
const Size minSize = Size(_MINIMUM_WINDOW_SIZE, _MINIMUM_WINDOW_SIZE);
|
||||||
@@ -27,7 +27,7 @@ class LunaDesktopWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setWindowTitle(String title) async {
|
Future<void> setWindowTitle(String title) async {
|
||||||
if (isPlatformCompatible) {
|
if (isSupported) {
|
||||||
windowManager
|
windowManager
|
||||||
.waitUntilReadyToShow()
|
.waitUntilReadyToShow()
|
||||||
.then((_) async => await windowManager.setTitle(title));
|
.then((_) async => await windowManager.setTitle(title));
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
export 'filesystem/file.dart';
|
|
||||||
export 'filesystem/filesystem.dart';
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'file.dart';
|
import 'file.dart';
|
||||||
import './platform/filesystem_stub.dart'
|
import 'platform/filesystem_stub.dart'
|
||||||
if (dart.library.io) './platform/filesystem_io.dart'
|
if (dart.library.io) 'platform/filesystem_io.dart'
|
||||||
if (dart.library.html) './platform/filesystem_web.dart';
|
if (dart.library.html) 'platform/filesystem_html.dart';
|
||||||
|
|
||||||
abstract class LunaFileSystem {
|
abstract class LunaFileSystem {
|
||||||
static bool get isSupported => isPlatformSupported();
|
static bool get isSupported => isPlatformSupported();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import '../filesystem.dart';
|
import '../filesystem.dart';
|
||||||
import './platform_web.dart';
|
import 'platform_web.dart';
|
||||||
|
|
||||||
bool isPlatformSupported() => false;
|
bool isPlatformSupported() => false;
|
||||||
LunaFileSystem getFileSystem() => Web();
|
LunaFileSystem getFileSystem() => Web();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import '../filesystem.dart';
|
import '../filesystem.dart';
|
||||||
import './platform_desktop.dart';
|
import 'platform_desktop.dart';
|
||||||
import './platform_mobile.dart';
|
import 'platform_mobile.dart';
|
||||||
|
|
||||||
bool isPlatformSupported() {
|
bool isPlatformSupported() {
|
||||||
switch (defaultTargetPlatform) {
|
switch (defaultTargetPlatform) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
|
||||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
import 'platform.dart';
|
||||||
|
|
||||||
extension ProductDetailsExtension on ProductDetails {
|
extension ProductDetailsExtension on ProductDetails {
|
||||||
IconData get icon {
|
IconData get icon {
|
||||||
@@ -54,7 +54,7 @@ class LunaInAppPurchases {
|
|||||||
static List<ProductDetails> donationIAPs = [];
|
static List<ProductDetails> donationIAPs = [];
|
||||||
static bool isAvailable = false;
|
static bool isAvailable = false;
|
||||||
|
|
||||||
static bool get isPlatformCompatible => Platform.isAndroid || Platform.isIOS;
|
static bool get isSupported => LunaPlatform().isMobile;
|
||||||
|
|
||||||
/// Callback function for [purchaseStream].
|
/// Callback function for [purchaseStream].
|
||||||
static Future<void> _purchasedCallback(
|
static Future<void> _purchasedCallback(
|
||||||
@@ -74,7 +74,7 @@ class LunaInAppPurchases {
|
|||||||
/// - Attach [InAppPurchaseConnection.instance.purchaseUpdateStream] listener for automatically completing IAP purchases
|
/// - Attach [InAppPurchaseConnection.instance.purchaseUpdateStream] listener for automatically completing IAP purchases
|
||||||
/// - Fetch and store donation IAPs
|
/// - Fetch and store donation IAPs
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
if (isPlatformCompatible) {
|
if (isSupported) {
|
||||||
isAvailable = await connection.isAvailable();
|
isAvailable = await connection.isAvailable();
|
||||||
if (isAvailable) {
|
if (isAvailable) {
|
||||||
// Open listener
|
// Open listener
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:lunasea/core.dart';
|
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
|
||||||
|
|
||||||
class LunaNetworking extends HttpOverrides {
|
|
||||||
/// Initialize the default HTTP client by setting [HttpOverrides]'s global HTTP client to [LunaNetworking].
|
|
||||||
void initialize() {
|
|
||||||
HttpOverrides.global = LunaNetworking();
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateUserAgent(PackageInfo info) {
|
|
||||||
return '${info.appName}/${info.version} ${info.buildNumber}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overrides the default [createHttpClient] function and ensures all HTTPS connections allow bad certificate connections.
|
|
||||||
///
|
|
||||||
/// Returns a new [HttpClient] using the given [context].
|
|
||||||
@override
|
|
||||||
HttpClient createHttpClient(SecurityContext? context) {
|
|
||||||
final HttpClient client = super.createHttpClient(context);
|
|
||||||
|
|
||||||
// Disable TLS validation
|
|
||||||
if (!LunaDatabaseValue.NETWORKING_TLS_VALIDATION.data)
|
|
||||||
client.badCertificateCallback = (cert, host, port) => true;
|
|
||||||
|
|
||||||
// Set User-Agent
|
|
||||||
PackageInfo.fromPlatform()
|
|
||||||
.then((info) => client.userAgent = generateUserAgent(info))
|
|
||||||
.catchError((_) => client.userAgent = 'LunaSea/Unknown');
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overrides the default [findProxyFromEnvironment] function.
|
|
||||||
///
|
|
||||||
/// Currently just calls the super function to use the default finding function.
|
|
||||||
@override
|
|
||||||
String findProxyFromEnvironment(Uri url, Map<String, String>? environment) {
|
|
||||||
return super.findProxyFromEnvironment(url, environment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
lib/core/system/networking/networking.dart
Normal file
10
lib/core/system/networking/networking.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'platform/networking_stub.dart'
|
||||||
|
if (dart.library.io) 'platform/networking_io.dart'
|
||||||
|
if (dart.library.html) 'platform/networking_html.dart';
|
||||||
|
|
||||||
|
abstract class LunaNetworking {
|
||||||
|
static bool get isSupported => isPlatformSupported();
|
||||||
|
factory LunaNetworking() => getNetworking();
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
}
|
||||||
5
lib/core/system/networking/platform/networking_html.dart
Normal file
5
lib/core/system/networking/platform/networking_html.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import '../networking.dart';
|
||||||
|
|
||||||
|
bool isPlatformSupported() => false;
|
||||||
|
LunaNetworking getNetworking() =>
|
||||||
|
throw UnsupportedError('LunaNetworking unsupported');
|
||||||
35
lib/core/system/networking/platform/networking_io.dart
Normal file
35
lib/core/system/networking/platform/networking_io.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import '../../../../vendor.dart';
|
||||||
|
import '../../../database/luna_database.dart';
|
||||||
|
import '../networking.dart';
|
||||||
|
|
||||||
|
bool isPlatformSupported() => true;
|
||||||
|
LunaNetworking getNetworking() => IO();
|
||||||
|
|
||||||
|
class IO extends HttpOverrides implements LunaNetworking {
|
||||||
|
@override
|
||||||
|
void initialize() {
|
||||||
|
HttpOverrides.global = IO();
|
||||||
|
}
|
||||||
|
|
||||||
|
String generateUserAgent(PackageInfo info) {
|
||||||
|
return '${info.appName}/${info.version} ${info.buildNumber}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
HttpClient createHttpClient(SecurityContext? context) {
|
||||||
|
final HttpClient client = super.createHttpClient(context);
|
||||||
|
|
||||||
|
// Disable TLS validation
|
||||||
|
if (!LunaDatabaseValue.NETWORKING_TLS_VALIDATION.data)
|
||||||
|
client.badCertificateCallback = (cert, host, port) => true;
|
||||||
|
|
||||||
|
// Set User-Agent
|
||||||
|
PackageInfo.fromPlatform()
|
||||||
|
.then((info) => client.userAgent = generateUserAgent(info))
|
||||||
|
.catchError((_) => client.userAgent = 'LunaSea/Unknown');
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
lib/core/system/networking/platform/networking_stub.dart
Normal file
5
lib/core/system/networking/platform/networking_stub.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import '../networking.dart';
|
||||||
|
|
||||||
|
bool isPlatformSupported() => false;
|
||||||
|
LunaNetworking getNetworking() =>
|
||||||
|
throw UnsupportedError('LunaNetworking unsupported');
|
||||||
15
lib/core/system/platform.dart
Normal file
15
lib/core/system/platform.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class LunaPlatform {
|
||||||
|
bool get isAndroid => defaultTargetPlatform == TargetPlatform.android;
|
||||||
|
bool get isIOS => defaultTargetPlatform == TargetPlatform.iOS;
|
||||||
|
|
||||||
|
bool get isLinux => defaultTargetPlatform == TargetPlatform.linux;
|
||||||
|
bool get isMacOS => defaultTargetPlatform == TargetPlatform.macOS;
|
||||||
|
bool get isWindows => defaultTargetPlatform == TargetPlatform.windows;
|
||||||
|
|
||||||
|
bool get isWeb => kIsWeb;
|
||||||
|
|
||||||
|
bool get isMobile => isAndroid || isIOS;
|
||||||
|
bool get isDesktop => isLinux || isMacOS || isWindows;
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:quick_actions/quick_actions.dart';
|
import 'package:quick_actions/quick_actions.dart';
|
||||||
|
|
||||||
|
import 'platform.dart';
|
||||||
|
|
||||||
class LunaQuickActions {
|
class LunaQuickActions {
|
||||||
static const QuickActions _quickActions = QuickActions();
|
static const QuickActions _quickActions = QuickActions();
|
||||||
|
|
||||||
static bool get isPlatformCompatible => Platform.isAndroid || Platform.isIOS;
|
static bool get isPlatformCompatible => LunaPlatform().isMobile;
|
||||||
|
|
||||||
/// Initialize the quick actions by setting the action handler.
|
/// Initialize the quick actions by setting the action handler.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
import '../../../modules/wake_on_lan/api/api.dart';
|
import '../../../modules/wake_on_lan/api/wake_on_lan.dart';
|
||||||
|
|
||||||
class LunaDrawer extends StatelessWidget {
|
class LunaDrawer extends StatelessWidget {
|
||||||
final String page;
|
final String page;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
|
||||||
|
|
||||||
class LunaChangelogSheet extends LunaBottomModalSheet {
|
class LunaChangelogSheet extends LunaBottomModalSheet {
|
||||||
late _Changelog _changelog;
|
late _Changelog _changelog;
|
||||||
@@ -64,10 +62,6 @@ class LunaChangelogSheet extends LunaBottomModalSheet {
|
|||||||
'lunasea.Fixes'.tr(),
|
'lunasea.Fixes'.tr(),
|
||||||
_changelog.changesFixes,
|
_changelog.changesFixes,
|
||||||
),
|
),
|
||||||
..._buildChangeBlock(
|
|
||||||
'lunasea.PlatformSpecific'.tr(),
|
|
||||||
_changelog.changesPlatform,
|
|
||||||
),
|
|
||||||
const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2),
|
const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2),
|
||||||
],
|
],
|
||||||
actionBar: LunaBottomActionBar(
|
actionBar: LunaBottomActionBar(
|
||||||
@@ -110,50 +104,21 @@ class _Changelog {
|
|||||||
List<_Change> changesNew = [];
|
List<_Change> changesNew = [];
|
||||||
List<_Change> changesTweaks = [];
|
List<_Change> changesTweaks = [];
|
||||||
List<_Change> changesFixes = [];
|
List<_Change> changesFixes = [];
|
||||||
List<_Change> changesPlatform = [];
|
|
||||||
|
|
||||||
static _Changelog fromJson(Map<String, dynamic> json) {
|
static _Changelog fromJson(Map<String, dynamic> json) {
|
||||||
_Changelog changelog = _Changelog();
|
_Changelog changelog = _Changelog();
|
||||||
int _index = -1;
|
|
||||||
List<dynamic>? data;
|
List<dynamic>? data;
|
||||||
|
|
||||||
changelog.motd = json['motd'] ?? '';
|
changelog.motd = json['motd'] ?? 'Welcome to LunaSea!';
|
||||||
|
|
||||||
data = json['new'];
|
data = json['new'];
|
||||||
data?.forEach((change) {
|
data?.forEach((c) => changelog.changesNew.add(_Change.fromJson(c)));
|
||||||
_Change _change = _Change.fromJson(change);
|
|
||||||
changelog.changesNew.add(_change);
|
|
||||||
});
|
|
||||||
|
|
||||||
data = json['tweaks'];
|
data = json['tweaks'];
|
||||||
data?.forEach((change) {
|
data?.forEach((c) => changelog.changesTweaks.add(_Change.fromJson(c)));
|
||||||
_Change _change = _Change.fromJson(change);
|
|
||||||
changelog.changesTweaks.add(_change);
|
|
||||||
});
|
|
||||||
|
|
||||||
data = json['fixes'];
|
data = json['fixes'];
|
||||||
data?.forEach((change) {
|
data?.forEach((c) => changelog.changesFixes.add(_Change.fromJson(c)));
|
||||||
_Change _change = _Change.fromJson(change);
|
|
||||||
changelog.changesFixes.add(_change);
|
|
||||||
});
|
|
||||||
|
|
||||||
_index = data?.indexWhere((e) => e['module'] == 'macOS') ?? -1;
|
|
||||||
if (Platform.isMacOS && _index >= 0) {
|
|
||||||
_Change _change = _Change.fromJson(data![_index]);
|
|
||||||
changelog.changesFixes.add(_change);
|
|
||||||
}
|
|
||||||
|
|
||||||
_index = data?.indexWhere((e) => e['module'] == 'Android') ?? -1;
|
|
||||||
if (Platform.isAndroid && _index >= 0) {
|
|
||||||
_Change _change = _Change.fromJson(data![_index]);
|
|
||||||
changelog.changesFixes.add(_change);
|
|
||||||
}
|
|
||||||
|
|
||||||
_index = data?.indexWhere((e) => e['module'] == 'iOS') ?? -1;
|
|
||||||
if (Platform.isIOS && _index >= 0) {
|
|
||||||
_Change _change = _Change.fromJson(data![_index]);
|
|
||||||
changelog.changesFixes.add(_change);
|
|
||||||
}
|
|
||||||
|
|
||||||
return changelog;
|
return changelog;
|
||||||
}
|
}
|
||||||
@@ -167,8 +132,8 @@ class _Change {
|
|||||||
|
|
||||||
static _Change fromJson(Map<String, dynamic> json) {
|
static _Change fromJson(Map<String, dynamic> json) {
|
||||||
_Change change = _Change._();
|
_Change change = _Change._();
|
||||||
change.module = json['module']!;
|
change.module = json['module'] as String;
|
||||||
change.changes = (json['changes'] as List?)?.cast<String>() ?? [];
|
change.changes = (json['changes'] as List).cast<String>();
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:device_preview/device_preview.dart';
|
import 'package:device_preview/device_preview.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|
||||||
|
import 'core/system/networking/networking.dart';
|
||||||
|
import 'core/system/platform.dart';
|
||||||
import 'modules/dashboard/routes/dashboard/route.dart' show HomeRouter;
|
import 'modules/dashboard/routes/dashboard/route.dart' show HomeRouter;
|
||||||
|
|
||||||
/// LunaSea Entry Point: Initialize & Run Application
|
/// LunaSea Entry Point: Initialize & Run Application
|
||||||
@@ -21,7 +21,7 @@ Future<void> main() async {
|
|||||||
LunaLogger().initialize();
|
LunaLogger().initialize();
|
||||||
LunaTheme().initialize();
|
LunaTheme().initialize();
|
||||||
await LunaDesktopWindow().initialize();
|
await LunaDesktopWindow().initialize();
|
||||||
LunaNetworking().initialize();
|
if (LunaNetworking.isSupported) LunaNetworking().initialize();
|
||||||
LunaImageCache().initialize();
|
LunaImageCache().initialize();
|
||||||
LunaRouter().initialize();
|
LunaRouter().initialize();
|
||||||
await LunaInAppPurchases().initialize();
|
await LunaInAppPurchases().initialize();
|
||||||
@@ -45,7 +45,7 @@ class LunaBIOS extends StatelessWidget {
|
|||||||
LunaRouter router = LunaRouter();
|
LunaRouter router = LunaRouter();
|
||||||
|
|
||||||
return DevicePreview(
|
return DevicePreview(
|
||||||
enabled: kDebugMode && Platform.isMacOS,
|
enabled: kDebugMode && LunaPlatform().isDesktop,
|
||||||
builder: (context) => EasyLocalization(
|
builder: (context) => EasyLocalization(
|
||||||
supportedLocales: localization.supportedLocales as List<Locale>,
|
supportedLocales: localization.supportedLocales as List<Locale>,
|
||||||
path: localization.fileDirectory,
|
path: localization.fileDirectory,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import '../../../../../../core/database/luna_database.dart';
|
|||||||
import '../../../../../../core/modules.dart';
|
import '../../../../../../core/modules.dart';
|
||||||
import '../../../../../../core/system/profile.dart';
|
import '../../../../../../core/system/profile.dart';
|
||||||
import '../../../../../../core/ui.dart';
|
import '../../../../../../core/ui.dart';
|
||||||
import '../../../../wake_on_lan/api/api.dart';
|
import '../../../../wake_on_lan/api/wake_on_lan.dart';
|
||||||
import '../widgets/navigation_bar.dart';
|
import '../widgets/navigation_bar.dart';
|
||||||
|
|
||||||
class ModulesPage extends StatefulWidget {
|
class ModulesPage extends StatefulWidget {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/nzbget.dart';
|
import 'package:lunasea/modules/nzbget.dart';
|
||||||
|
|
||||||
|
import '../../../core/system/filesystem/file.dart';
|
||||||
|
import '../../../core/system/filesystem/filesystem.dart';
|
||||||
|
|
||||||
class NZBGet extends StatefulWidget {
|
class NZBGet extends StatefulWidget {
|
||||||
static const ROUTE_NAME = '/nzbget';
|
static const ROUTE_NAME = '/nzbget';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/nzbget.dart';
|
import 'package:lunasea/modules/nzbget.dart';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/radarr.dart';
|
import 'package:lunasea/modules/radarr.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/sabnzbd.dart';
|
import 'package:lunasea/modules/sabnzbd.dart';
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/sabnzbd.dart';
|
import 'package:lunasea/modules/sabnzbd.dart';
|
||||||
|
|
||||||
|
import '../../../core/system/filesystem/file.dart';
|
||||||
|
import '../../../core/system/filesystem/filesystem.dart';
|
||||||
|
|
||||||
class SABnzbd extends StatefulWidget {
|
class SABnzbd extends StatefulWidget {
|
||||||
static const ROUTE_NAME = '/sabnzbd';
|
static const ROUTE_NAME = '/sabnzbd';
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import 'package:lunasea/modules/nzbget.dart';
|
|||||||
import 'package:lunasea/modules/sabnzbd.dart';
|
import 'package:lunasea/modules/sabnzbd.dart';
|
||||||
import 'package:lunasea/modules/search.dart';
|
import 'package:lunasea/modules/search.dart';
|
||||||
|
|
||||||
|
import '../../../../core/system/filesystem/filesystem.dart';
|
||||||
|
|
||||||
enum SearchDownloadType {
|
enum SearchDownloadType {
|
||||||
NZBGET,
|
NZBGET,
|
||||||
SABNZBD,
|
SABNZBD,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/settings.dart';
|
import 'package:lunasea/modules/settings.dart';
|
||||||
|
|
||||||
|
import '../../../../core/system/networking/networking.dart';
|
||||||
|
import '../../../../core/system/platform.dart';
|
||||||
|
|
||||||
class SettingsConfigurationRouter extends SettingsPageRouter {
|
class SettingsConfigurationRouter extends SettingsPageRouter {
|
||||||
SettingsConfigurationRouter() : super('/settings/configuration');
|
SettingsConfigurationRouter() : super('/settings/configuration');
|
||||||
|
|
||||||
@@ -79,14 +81,15 @@ class _State extends State<_Widget> with LunaScrollControllerMixin {
|
|||||||
onTap: () async =>
|
onTap: () async =>
|
||||||
SettingsConfigurationLocalizationRouter().navigateTo(context),
|
SettingsConfigurationLocalizationRouter().navigateTo(context),
|
||||||
),
|
),
|
||||||
LunaBlock(
|
if (LunaNetworking.isSupported)
|
||||||
title: 'settings.Network'.tr(),
|
LunaBlock(
|
||||||
body: [TextSpan(text: 'settings.NetworkDescription'.tr())],
|
title: 'settings.Network'.tr(),
|
||||||
trailing: const LunaIconButton(icon: Icons.network_check_rounded),
|
body: [TextSpan(text: 'settings.NetworkDescription'.tr())],
|
||||||
onTap: () async =>
|
trailing: const LunaIconButton(icon: Icons.network_check_rounded),
|
||||||
SettingsConfigurationNetworkRouter().navigateTo(context),
|
onTap: () async =>
|
||||||
),
|
SettingsConfigurationNetworkRouter().navigateTo(context),
|
||||||
if (Platform.isIOS)
|
),
|
||||||
|
if (LunaPlatform().isIOS)
|
||||||
LunaDatabaseValue.SELECTED_BROWSER.listen(
|
LunaDatabaseValue.SELECTED_BROWSER.listen(
|
||||||
builder: (context, box, widget) {
|
builder: (context, box, widget) {
|
||||||
LunaDatabaseValue _db = LunaDatabaseValue.SELECTED_BROWSER;
|
LunaDatabaseValue _db = LunaDatabaseValue.SELECTED_BROWSER;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/settings.dart';
|
import 'package:lunasea/modules/settings.dart';
|
||||||
|
|
||||||
|
import '../../../../core/system/networking/networking.dart';
|
||||||
|
|
||||||
class SettingsConfigurationNetworkRouter extends SettingsPageRouter {
|
class SettingsConfigurationNetworkRouter extends SettingsPageRouter {
|
||||||
SettingsConfigurationNetworkRouter()
|
SettingsConfigurationNetworkRouter()
|
||||||
: super('/settings/configuration/network');
|
: super('/settings/configuration/network');
|
||||||
@@ -58,7 +60,10 @@ class _State extends State<_Widget> with LunaScrollControllerMixin {
|
|||||||
],
|
],
|
||||||
trailing: LunaSwitch(
|
trailing: LunaSwitch(
|
||||||
value: _db.data,
|
value: _db.data,
|
||||||
onChanged: _db.put,
|
onChanged: (data) {
|
||||||
|
_db.put(data);
|
||||||
|
if (LunaNetworking.isSupported) LunaNetworking().initialize();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class _State extends State<_Widget> with LunaScrollControllerMixin {
|
|||||||
onTap: () async => SettingsProfilesRouter().navigateTo(context),
|
onTap: () async => SettingsProfilesRouter().navigateTo(context),
|
||||||
),
|
),
|
||||||
LunaDivider(),
|
LunaDivider(),
|
||||||
if (LunaInAppPurchases.isPlatformCompatible)
|
if (LunaInAppPurchases.isSupported)
|
||||||
LunaBlock(
|
LunaBlock(
|
||||||
title: 'settings.Donations'.tr(),
|
title: 'settings.Donations'.tr(),
|
||||||
body: [TextSpan(text: 'settings.DonationsDescription'.tr())],
|
body: [TextSpan(text: 'settings.DonationsDescription'.tr())],
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/settings.dart';
|
import 'package:lunasea/modules/settings.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
|
||||||
|
|
||||||
class SettingsSystemRouter extends SettingsPageRouter {
|
class SettingsSystemRouter extends SettingsPageRouter {
|
||||||
SettingsSystemRouter() : super('/settings/system');
|
SettingsSystemRouter() : super('/settings/system');
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/settings.dart';
|
import 'package:lunasea/modules/settings.dart';
|
||||||
|
|
||||||
|
import '../../../../../core/system/filesystem/filesystem.dart';
|
||||||
|
|
||||||
class SettingsSystemBackupRestoreBackupTile extends StatelessWidget {
|
class SettingsSystemBackupRestoreBackupTile extends StatelessWidget {
|
||||||
const SettingsSystemBackupRestoreBackupTile({
|
const SettingsSystemBackupRestoreBackupTile({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/settings.dart';
|
import 'package:lunasea/modules/settings.dart';
|
||||||
|
|
||||||
|
import '../../../../../core/system/filesystem/file.dart';
|
||||||
|
import '../../../../../core/system/filesystem/filesystem.dart';
|
||||||
|
|
||||||
class SettingsSystemBackupRestoreRestoreTile extends StatelessWidget {
|
class SettingsSystemBackupRestoreRestoreTile extends StatelessWidget {
|
||||||
const SettingsSystemBackupRestoreRestoreTile({
|
const SettingsSystemBackupRestoreRestoreTile({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/settings.dart';
|
import 'package:lunasea/modules/settings.dart';
|
||||||
|
|
||||||
|
import '../../../../core/system/filesystem/filesystem.dart';
|
||||||
|
|
||||||
class SettingsSystemLogsRouter extends SettingsPageRouter {
|
class SettingsSystemLogsRouter extends SettingsPageRouter {
|
||||||
SettingsSystemLogsRouter() : super('/settings/logs');
|
SettingsSystemLogsRouter() : super('/settings/logs');
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/sonarr.dart';
|
import 'package:lunasea/modules/sonarr.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/sonarr.dart';
|
import 'package:lunasea/modules/sonarr.dart';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/sonarr.dart';
|
import 'package:lunasea/modules/sonarr.dart';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart' show IterableExtension;
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'package:lunasea/core.dart';
|
import 'package:lunasea/core.dart';
|
||||||
import 'package:lunasea/modules/tautulli.dart';
|
import 'package:lunasea/modules/tautulli.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import './platform/wake_on_lan_stub.dart'
|
|
||||||
if (dart.library.io) './platform/wake_on_lan_io.dart'
|
|
||||||
if (dart.library.html) './platform/wake_on_lan_web.dart';
|
|
||||||
|
|
||||||
abstract class LunaWakeOnLAN {
|
|
||||||
static bool get isSupported => isPlatformSupported();
|
|
||||||
factory LunaWakeOnLAN() => getWakeOnLAN();
|
|
||||||
|
|
||||||
Future<void> wake();
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../api.dart';
|
import '../wake_on_lan.dart';
|
||||||
|
|
||||||
bool isPlatformSupported() => false;
|
bool isPlatformSupported() => false;
|
||||||
LunaWakeOnLAN getWakeOnLAN() =>
|
LunaWakeOnLAN getWakeOnLAN() =>
|
||||||
@@ -4,12 +4,12 @@ import '../../../../core/models/configuration/profile.dart';
|
|||||||
import '../../../../core/system/profile.dart';
|
import '../../../../core/system/profile.dart';
|
||||||
import '../../../../core/ui.dart';
|
import '../../../../core/ui.dart';
|
||||||
import '../../../../core/utilities/logger.dart';
|
import '../../../../core/utilities/logger.dart';
|
||||||
import '../api.dart';
|
import '../wake_on_lan.dart';
|
||||||
|
|
||||||
bool isPlatformSupported() => true;
|
bool isPlatformSupported() => true;
|
||||||
LunaWakeOnLAN getWakeOnLAN() => API();
|
LunaWakeOnLAN getWakeOnLAN() => IO();
|
||||||
|
|
||||||
class API implements LunaWakeOnLAN {
|
class IO implements LunaWakeOnLAN {
|
||||||
@override
|
@override
|
||||||
Future<void> wake() async {
|
Future<void> wake() async {
|
||||||
ProfileHiveObject profile = LunaProfile.current;
|
ProfileHiveObject profile = LunaProfile.current;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../api.dart';
|
import '../wake_on_lan.dart';
|
||||||
|
|
||||||
bool isPlatformSupported() => false;
|
bool isPlatformSupported() => false;
|
||||||
LunaWakeOnLAN getWakeOnLAN() =>
|
LunaWakeOnLAN getWakeOnLAN() =>
|
||||||
|
|||||||
10
lib/modules/wake_on_lan/api/wake_on_lan.dart
Normal file
10
lib/modules/wake_on_lan/api/wake_on_lan.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'platform/wake_on_lan_stub.dart'
|
||||||
|
if (dart.library.io) 'platform/wake_on_lan_io.dart'
|
||||||
|
if (dart.library.html) 'platform/wake_on_lan_html.dart';
|
||||||
|
|
||||||
|
abstract class LunaWakeOnLAN {
|
||||||
|
static bool get isSupported => isPlatformSupported();
|
||||||
|
factory LunaWakeOnLAN() => getWakeOnLAN();
|
||||||
|
|
||||||
|
Future<void> wake();
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
// Dart
|
// Dart
|
||||||
|
export 'dart:async';
|
||||||
export 'dart:convert';
|
export 'dart:convert';
|
||||||
export 'dart:math';
|
export 'dart:math';
|
||||||
|
export 'dart:typed_data';
|
||||||
// Packages
|
// Packages
|
||||||
export 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
export 'package:dio/dio.dart';
|
export 'package:dio/dio.dart';
|
||||||
export 'package:easy_localization/easy_localization.dart';
|
export 'package:easy_localization/easy_localization.dart';
|
||||||
export 'package:expandable/expandable.dart';
|
export 'package:expandable/expandable.dart';
|
||||||
@@ -15,6 +16,7 @@ export 'package:hive_flutter/hive_flutter.dart';
|
|||||||
export 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
export 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
export 'package:json_annotation/json_annotation.dart';
|
export 'package:json_annotation/json_annotation.dart';
|
||||||
export 'package:layout/layout.dart';
|
export 'package:layout/layout.dart';
|
||||||
|
export 'package:package_info_plus/package_info_plus.dart';
|
||||||
export 'package:provider/provider.dart';
|
export 'package:provider/provider.dart';
|
||||||
export 'package:simple_gesture_detector/simple_gesture_detector.dart';
|
export 'package:simple_gesture_detector/simple_gesture_detector.dart';
|
||||||
export 'package:stash/stash_api.dart';
|
export 'package:stash/stash_api.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user