mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-31 04:32:32 +00:00
[Release] v4.2.0+40200006 (#296)
TWEAKS - [Sonarr/Upcoming] If an episode has not aired, show it as "Upcoming" instead of "Not Downloaded" FIXES - [Accounts] Register device token to database for future notification support - [Accounts] Improve validation for email and passwords - [Radarr] Fix some durations being displayed twice - [UI/TextField] Show errors as snackbar instead of in-line error
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,5 +1,16 @@
|
||||
# LunaSea Changelog
|
||||
|
||||
## [Beta] v4.2.0 (40200006)
|
||||
|
||||
#### TWEAKS
|
||||
- `[Sonarr/Upcoming]` If an episode has not aired, show it as "Upcoming" instead of "Not Downloaded"
|
||||
|
||||
#### FIXES
|
||||
- `[Accounts]` Register device token to database for future notification support
|
||||
- `[Accounts]` Improve validation for email and passwords
|
||||
- `[Radarr]` Fix some durations being displayed twice
|
||||
- `[UI/TextField]` Show errors as snackbar instead of in-line error
|
||||
|
||||
## [Beta] v4.2.0 (40200005)
|
||||
|
||||
#### NEW
|
||||
|
||||
@@ -1,116 +1,36 @@
|
||||
{
|
||||
"motd": "This update introduces some exciting new features, including a brand new cloud account system! You can register for a free LunaSea account to backup an unlimited amount of configurations and easily restore configurations from previous devices.\n\nBackups also now support backing up the entire database, including customization details for each module.",
|
||||
"motd": "This update includes some minor tweaks and fixes for the last build.",
|
||||
"version": "4.2.0",
|
||||
"build": "40200005",
|
||||
"new": [
|
||||
{
|
||||
"module": "Accounts",
|
||||
"changes": [
|
||||
"Added LunaSea accounts",
|
||||
"Ability to backup, restore, and delete cloud configurations"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Backups",
|
||||
"changes": [
|
||||
"All backups now contain all customization and configuration options in LunaSea"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Changelog",
|
||||
"changes": [
|
||||
"Show changelog on launch if a new version is installed",
|
||||
"Use a new bottom sheet UI for the changelog"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings/Modules",
|
||||
"changes": [
|
||||
"Add an information/help button with module descriptions and links"
|
||||
]
|
||||
}
|
||||
],
|
||||
"build": "40200006",
|
||||
"new": [],
|
||||
"tweaks": [
|
||||
{
|
||||
"module": "Radarr",
|
||||
"module": "Sonarr/Upcoming",
|
||||
"changes": [
|
||||
"Always show the amount of days content will be available instead of limiting it to only content in the next 30 days"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings",
|
||||
"changes": [
|
||||
"Moved \"Backup & Restore\" and \"Logs\" to \"System\" section",
|
||||
"Merged \"Customization\" and \"Modules\" sections into \"Configuration\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings/Sonarr",
|
||||
"changes": [
|
||||
"Removed the need to enable Sonarr to test the connection"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings/Tautulli",
|
||||
"changes": [
|
||||
"Removed the need to enable Tautulli to test the connection"
|
||||
"If an episode has not aired, show it as \"Upcoming\" instead of \"Not Downloaded\""
|
||||
]
|
||||
}
|
||||
],
|
||||
"fixes": [
|
||||
{
|
||||
"module": "In App Purchases",
|
||||
"module": "Accounts",
|
||||
"changes": [
|
||||
"Ensure all in app purchases are marked as \"consumed\""
|
||||
"Register device token to database for future notification support",
|
||||
"Improve validation for email and passwords"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Flutter",
|
||||
"module": "Radarr",
|
||||
"changes": [
|
||||
"Updated packages"
|
||||
"Fix some durations being displayed twice"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Logging",
|
||||
"module": "UI/TextField",
|
||||
"changes": [
|
||||
"Updated Sentry to v4 framework to improve capturing fatal/crashing bugs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings/Logs",
|
||||
"changes": [
|
||||
"Hide exception and stack trace buttons when an error is not available"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Settings/Resources",
|
||||
"changes": [
|
||||
"Updated URL endpoints"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Sonarr/History",
|
||||
"changes": [
|
||||
"Fixed history fetching the oldest entries, not the newest"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "State",
|
||||
"changes": [
|
||||
"Correctly clear state when clearing LunaSea's configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "Tautulli/Activity",
|
||||
"changes": [
|
||||
"Fixed consistency of hardware transcoding indicator compared to the web UI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"module": "UI/Divider",
|
||||
"changes": [
|
||||
"Fix consistency of divider width across regular and AMOLED dark theme"
|
||||
"Show errors as snackbar instead of in-line error"
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
@@ -37,6 +37,7 @@ class LunaFirebaseAuth {
|
||||
try {
|
||||
assert(email != null && password != null);
|
||||
UserCredential _user = await instance.createUserWithEmailAndPassword(email: email, password: password);
|
||||
LunaFirebaseFirestore().addDeviceToken();
|
||||
return LunaFirebaseAuthResponse(state: true, user: _user.user, error: null);
|
||||
} on FirebaseAuthException catch (error) {
|
||||
return LunaFirebaseAuthResponse(state: false, user: null, error: error);
|
||||
@@ -53,6 +54,7 @@ class LunaFirebaseAuth {
|
||||
try {
|
||||
assert(email != null && password != null);
|
||||
UserCredential _user = await instance.signInWithEmailAndPassword(email: email, password: password);
|
||||
LunaFirebaseFirestore().addDeviceToken();
|
||||
return LunaFirebaseAuthResponse(state: true, user: _user.user, error: null);
|
||||
} on FirebaseAuthException catch (error) {
|
||||
return LunaFirebaseAuthResponse(state: false, user: null, error: error);
|
||||
|
||||
@@ -11,7 +11,7 @@ class LunaFirebaseFirestore {
|
||||
///
|
||||
/// If the user is not signed in, returns false.
|
||||
Future<bool> addBackupEntry(String id, int timestamp, { String title = '', String description = '' }) async {
|
||||
if(LunaFirebaseAuth().user == null) return false;
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
LunaFirebaseBackupDocument entry = LunaFirebaseBackupDocument(id: id, title: title, description: description, timestamp: timestamp);
|
||||
instance.doc('users/${LunaFirebaseAuth().uid}/backups/$id').set(entry.toJSON());
|
||||
@@ -26,7 +26,7 @@ class LunaFirebaseFirestore {
|
||||
///
|
||||
/// If the user is not signed in, returns false.
|
||||
Future<bool> deleteBackupEntry(String id) async {
|
||||
if(LunaFirebaseAuth().user == null) return false;
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
await instance.doc('users/${LunaFirebaseAuth().uid}/backups/$id').delete();
|
||||
return true;
|
||||
@@ -49,4 +49,19 @@ class LunaFirebaseFirestore {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the current device token to Firestore. Returns true if successful, and false on any error.
|
||||
Future<bool> addDeviceToken() async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
String token = await LunaFirebaseMessaging.instance.getToken();
|
||||
instance.doc('users/${LunaFirebaseAuth().uid}').set({
|
||||
'devices': FieldValue.arrayUnion([token]),
|
||||
}, SetOptions(merge: true));
|
||||
return true;
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to add device token', error, stack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export 'luna_ui/appbar.dart';
|
||||
export 'luna_ui/decoration.dart';
|
||||
export 'luna_ui/sliver_sticky_header.dart';
|
||||
export 'luna_ui/snackbar.dart';
|
||||
export 'luna_ui/text.dart';
|
||||
|
||||
|
||||
17
lib/core/luna_ui/sliver_sticky_header.dart
Normal file
17
lib/core/luna_ui/sliver_sticky_header.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||
|
||||
class LunaSliverStickyHeader extends SliverStickyHeader {
|
||||
LunaSliverStickyHeader({
|
||||
@required Widget header,
|
||||
@required List<Widget> children,
|
||||
}) : super(
|
||||
header: header,
|
||||
sliver: SliverPadding(
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate(children),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,5 @@ export 'ui/network_image.dart';
|
||||
export 'ui/refresh_indicator.dart';
|
||||
export 'ui/shape.dart';
|
||||
export 'ui/snackbar.dart';
|
||||
export 'ui/sticky_header.dart';
|
||||
export 'ui/table.dart';
|
||||
export 'ui/text.dart';
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
||||
|
||||
class LSStickyHeader extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
final Widget header;
|
||||
|
||||
LSStickyHeader({
|
||||
@required this.header,
|
||||
@required this.children,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SliverStickyHeader(
|
||||
header: header,
|
||||
sliver: SliverPadding(
|
||||
sliver: SliverList(
|
||||
delegate: (SliverChildListDelegate(children)),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -72,8 +71,9 @@ class _State extends State<LunaBIOS> {
|
||||
|
||||
/// Runs the first-step boot sequence that is required for widgets
|
||||
Future<void> _boot() async {
|
||||
LunaFirebaseMessaging().requestNotificationPermissions();
|
||||
if(kDebugMode) print(await LunaFirebaseMessaging.instance.getToken());
|
||||
// Request notifications and register the device token.
|
||||
await LunaFirebaseMessaging().requestNotificationPermissions();
|
||||
LunaFirebaseFirestore().addDeviceToken();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -79,7 +79,7 @@ class _State extends State<LidarrAddSearch> {
|
||||
Widget _list(List<Widget> data) => LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchBar,
|
||||
children: data,
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ class _State extends State<LidarrCatalogue> with AutomaticKeepAliveClientMixin {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchSortBar,
|
||||
children: _children,
|
||||
),
|
||||
|
||||
@@ -118,7 +118,7 @@ class _State extends State<LidarrSearchResults> {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchSortBar,
|
||||
children: _children,
|
||||
)
|
||||
|
||||
@@ -106,7 +106,7 @@ class _State extends State<NZBGetHistory> with AutomaticKeepAliveClientMixin {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchBar,
|
||||
children: _children,
|
||||
),
|
||||
|
||||
@@ -49,7 +49,7 @@ class RadarrMissingData {
|
||||
return '\t•\t$profile';
|
||||
}
|
||||
|
||||
String get runtimeString => '\t${Constants.TEXT_BULLET}\t${runtime.lunaRuntime()}${runtime.lunaRuntime()}';
|
||||
String get runtimeString => runtime.lunaRuntime().isEmpty ? '' : '\t${Constants.TEXT_BULLET}\t${runtime.lunaRuntime()}';
|
||||
|
||||
List<TextSpan> get subtitle {
|
||||
DateTime now = DateTime.now();
|
||||
|
||||
@@ -79,7 +79,7 @@ class _State extends State<RadarrAddSearch> {
|
||||
Widget _list(List<Widget> data) => LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchBar,
|
||||
children: data,
|
||||
),
|
||||
|
||||
@@ -114,7 +114,7 @@ class _State extends State<RadarrCatalogue> with AutomaticKeepAliveClientMixin {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchSortBar,
|
||||
children: _children,
|
||||
),
|
||||
|
||||
@@ -118,7 +118,7 @@ class _State extends State<RadarrSearchResults> {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchSortBar,
|
||||
children: _children,
|
||||
)
|
||||
|
||||
@@ -106,7 +106,7 @@ class _State extends State<SABnzbdHistory> with AutomaticKeepAliveClientMixin {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchBar,
|
||||
children: _children,
|
||||
),
|
||||
|
||||
@@ -95,7 +95,7 @@ class _State extends State<SearchResults> {
|
||||
return LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchSortBar,
|
||||
children: _children,
|
||||
)
|
||||
|
||||
@@ -73,7 +73,7 @@ class _State extends State<SearchSearch> {
|
||||
Widget _list(List<Widget> data) => LSListViewStickyHeader(
|
||||
controller: _scrollController,
|
||||
slivers: <Widget>[
|
||||
LSStickyHeader(
|
||||
LunaSliverStickyHeader(
|
||||
header: _searchBar,
|
||||
children: data,
|
||||
),
|
||||
|
||||
@@ -7,116 +7,135 @@ class SettingsAccountSignedOutBody extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _State extends State<SettingsAccountSignedOutBody> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
final TextEditingController _emailController = TextEditingController();
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
LunaLoadingState _state = LunaLoadingState.INACTIVE;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Form(
|
||||
key: _formKey,
|
||||
child: LSListView(
|
||||
children: [
|
||||
Padding(
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/branding/splash.png',
|
||||
width: 200.0,
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||
),
|
||||
AutofillGroup(
|
||||
child: LSCard(
|
||||
child: Column(
|
||||
children: [
|
||||
LSTextInputBar(
|
||||
controller: _emailController,
|
||||
isFormField: true,
|
||||
margin: EdgeInsets.only(top: 12.0, bottom: 6.0, left: 12.0, right: 12.0),
|
||||
labelIcon: Icons.person,
|
||||
labelText: 'Email...',
|
||||
action: TextInputAction.next,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
autofillHints: [AutofillHints.username, AutofillHints.email],
|
||||
onChanged: (value, updateController) => setState(() {
|
||||
if(updateController) _emailController.text = value;
|
||||
}),
|
||||
validator: (value) {
|
||||
if(value.isEmpty) return 'Email Required';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
LSTextInputBar(
|
||||
controller: _passwordController,
|
||||
isFormField: true,
|
||||
margin: EdgeInsets.only(top: 6.0, bottom: 12.0, left: 12.0, right: 12.0),
|
||||
labelIcon: Icons.vpn_key,
|
||||
labelText: 'Password...',
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.text,
|
||||
autofillHints: [AutofillHints.password, AutofillHints.newPassword],
|
||||
action: TextInputAction.done,
|
||||
onChanged: (value, updateController) => setState(() {
|
||||
if(updateController) _passwordController.text = value;
|
||||
}),
|
||||
validator: (value) {
|
||||
if(value.isEmpty) return 'Password Required';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget build(BuildContext context) => LSListView(
|
||||
children: [
|
||||
Padding(
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/branding/splash.png',
|
||||
width: 200.0,
|
||||
),
|
||||
),
|
||||
LSContainerRow(
|
||||
children: [
|
||||
Expanded(
|
||||
child: LSButton(
|
||||
text: 'Register',
|
||||
backgroundColor: LunaColours.blueGrey,
|
||||
onTap: _register,
|
||||
reducedMargin: true,
|
||||
isLoading: _state == LunaLoadingState.ACTIVE,
|
||||
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||
),
|
||||
AutofillGroup(
|
||||
child: LSCard(
|
||||
child: Column(
|
||||
children: [
|
||||
LSTextInputBar(
|
||||
controller: _emailController,
|
||||
isFormField: true,
|
||||
margin: EdgeInsets.all(12.0),
|
||||
labelIcon: Icons.person,
|
||||
labelText: 'Email',
|
||||
action: TextInputAction.next,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
autofillHints: [AutofillHints.username, AutofillHints.email],
|
||||
onChanged: (value, updateController) => setState(() {
|
||||
if(updateController) _emailController.text = value;
|
||||
}),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: LSButton(
|
||||
text: 'Sign In',
|
||||
onTap: _signIn,
|
||||
reducedMargin: true,
|
||||
isLoading: _state == LunaLoadingState.ACTIVE,
|
||||
LSTextInputBar(
|
||||
controller: _passwordController,
|
||||
isFormField: true,
|
||||
margin: EdgeInsets.only(bottom: 12.0, left: 12.0, right: 12.0),
|
||||
labelIcon: Icons.vpn_key,
|
||||
labelText: 'Password',
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.text,
|
||||
autofillHints: [AutofillHints.password, AutofillHints.newPassword],
|
||||
action: TextInputAction.done,
|
||||
onChanged: (value, updateController) => setState(() {
|
||||
if(updateController) _passwordController.text = value;
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
LSContainerRow(
|
||||
children: [
|
||||
Expanded(
|
||||
child: LSButton(
|
||||
text: 'Register',
|
||||
backgroundColor: LunaColours.blueGrey,
|
||||
onTap: _register,
|
||||
reducedMargin: true,
|
||||
isLoading: _state == LunaLoadingState.ACTIVE,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: LSButton(
|
||||
text: 'Sign In',
|
||||
onTap: _signIn,
|
||||
reducedMargin: true,
|
||||
isLoading: _state == LunaLoadingState.ACTIVE,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
bool _validateEmailAddress({ bool showSnackBarOnFailure = true }) {
|
||||
const _regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)";
|
||||
if(!RegExp(_regex).hasMatch(_emailController.text)) {
|
||||
if(showSnackBarOnFailure) showLunaErrorSnackBar(
|
||||
context: context,
|
||||
title: 'Invalid Email',
|
||||
message: 'The email address is invalid',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _validatePassword({ bool showSnackBarOnFailure = true }) {
|
||||
if(_passwordController.text.isEmpty) {
|
||||
if(showSnackBarOnFailure) showLunaErrorSnackBar(
|
||||
context: context,
|
||||
title: 'Invalid Password',
|
||||
message: 'The password is invalid',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> _register() async {
|
||||
// Set button state
|
||||
if(!_validateEmailAddress() || !_validatePassword()) return;
|
||||
if(mounted) setState(() => _state = LunaLoadingState.ACTIVE);
|
||||
// Check form, then register user
|
||||
if(_formKey.currentState.validate()) await LunaFirebaseAuth().registerUser(_emailController.text, _passwordController.text)
|
||||
.then((response) => response.state
|
||||
await LunaFirebaseAuth().registerUser(_emailController.text, _passwordController.text)
|
||||
.then((response) {
|
||||
if(mounted) setState(() => _state = LunaLoadingState.INACTIVE);
|
||||
response.state
|
||||
? showLunaSuccessSnackBar(context: context, title: 'Successfully Registered', message: response.user.email)
|
||||
: showLunaErrorSnackBar(context: context, title: 'Failed to Register', message: response.error?.message ?? 'Unknown Error'))
|
||||
.catchError((error, stack) => showLunaErrorSnackBar(context: context, title: 'Failed to Register', error: error));
|
||||
// Set button state
|
||||
if(mounted) setState(() => _state = LunaLoadingState.INACTIVE);
|
||||
: showLunaErrorSnackBar(context: context, title: 'Failed to Register', message: response.error?.message ?? 'Unknown Error');
|
||||
})
|
||||
.catchError((error, stack) {
|
||||
if(mounted) setState(() => _state = LunaLoadingState.INACTIVE);
|
||||
showLunaErrorSnackBar(context: context, title: 'Failed to Register', error: error);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _signIn() async {
|
||||
// Set button state
|
||||
if(!_validateEmailAddress() || !_validatePassword()) return;
|
||||
if(mounted) setState(() => _state = LunaLoadingState.ACTIVE);
|
||||
// Check form, then login user
|
||||
if(_formKey.currentState.validate()) await LunaFirebaseAuth().signInUser(_emailController.text, _passwordController.text)
|
||||
.then((response) => response.state
|
||||
await LunaFirebaseAuth().signInUser(_emailController.text, _passwordController.text)
|
||||
.then((response) {
|
||||
if(mounted) setState(() => _state = LunaLoadingState.INACTIVE);
|
||||
response.state
|
||||
? showLunaSuccessSnackBar(context: context, title: 'Successfully Signed In', message: response.user.email)
|
||||
: showLunaErrorSnackBar(context: context, title: 'Failed to Sign In', message: response.error?.message ?? 'Unknown Error'))
|
||||
.catchError((error, stack) => showLunaErrorSnackBar(context: context, title: 'Failed to Sign In', error: error));
|
||||
// Set button state
|
||||
if(mounted) setState(() => _state = LunaLoadingState.INACTIVE);
|
||||
: showLunaErrorSnackBar(context: context, title: 'Failed to Sign In', message: response.error?.message ?? 'Unknown Error');
|
||||
})
|
||||
.catchError((error, stack) {
|
||||
if(mounted) setState(() => _state = LunaLoadingState.INACTIVE);
|
||||
showLunaErrorSnackBar(context: context, title: 'Failed to Sign In', error: error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,9 @@ extension SonarrCalendarExtension on SonarrCalendar {
|
||||
: DateFormat('hh:mm a').format(this.airDateUtc.toLocal());
|
||||
return Constants.TEXT_EMDASH;
|
||||
}
|
||||
}
|
||||
|
||||
bool get lunaHasAired {
|
||||
if(this.airDateUtc != null) return DateTime.now().isAfter(this.airDateUtc.toLocal());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +138,9 @@ class _State extends State<SonarrUpcomingTile> {
|
||||
children: [
|
||||
if(!widget.record.hasFile) TextSpan(
|
||||
style: TextStyle(
|
||||
color: LunaColours.red,
|
||||
color: widget.record.lunaHasAired ? LunaColours.red : LunaColours.blue,
|
||||
),
|
||||
text: 'Not Downloaded'
|
||||
text: widget.record.lunaHasAired ? 'Not Downloaded' : 'Upcoming',
|
||||
),
|
||||
if(widget.record.hasFile) TextSpan(
|
||||
style: TextStyle(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: lunasea
|
||||
description: Self-Hosted Controller
|
||||
version: 4.2.0+40200005
|
||||
version: 4.2.0+40200006
|
||||
publish_to: 'none'
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
||||
Reference in New Issue
Block a user