mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-31 12:42:34 +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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user