Merge pull request #220 from ente-io/home+stories-redesign
Home+stories redesign
This commit is contained in:
commit
b8551ebd9e
|
@ -140,8 +140,8 @@ class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
|
|||
} catch (e) {
|
||||
Logger("ApkDownloader").severe(e);
|
||||
AlertDialog alert = AlertDialog(
|
||||
title: Text("sorry"),
|
||||
content: Text("the download could not be completed"),
|
||||
title: Text("Sorry"),
|
||||
content: Text("The download could not be completed"),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:photos/models/file.dart';
|
||||
import 'package:photos/ui/thumbnail_widget.dart';
|
||||
|
||||
class BlurredFileBackdrop extends StatelessWidget {
|
||||
final File file;
|
||||
|
||||
BlurredFileBackdrop(this.file, {Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ImageFiltered(
|
||||
imageFilter: ImageFilter.blur(sigmaX: 64.0, sigmaY: 64.0),
|
||||
child: ThumbnailWidget(
|
||||
file,
|
||||
fit: BoxFit.cover,
|
||||
key: Key("memory_backdrop" + file.tag()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:extended_image/extended_image.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -265,7 +264,7 @@ class _DetailPageState extends State<DetailPage> {
|
|||
file.ownerID != Configuration.instance.getUserID()) {
|
||||
_logger.severe("Attempt to edit unowned file", UnauthorizedEditError(),
|
||||
StackTrace.current);
|
||||
showErrorDialog(context, "sorry",
|
||||
showErrorDialog(context, "Sorry",
|
||||
"we don't support editing photos and albums that you don't own yet");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -11,13 +11,9 @@ import 'package:photos/models/billing_plan.dart';
|
|||
import 'package:photos/services/billing_service.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:photos/ui/common/dynamicFAB.dart';
|
||||
//import 'package:photos/ui/common/report_bug_popup.dart';
|
||||
//import 'package:photos/ui/common_elements.dart';
|
||||
import 'package:photos/ui/loading_widget.dart';
|
||||
import 'package:photos/ui/web_page.dart';
|
||||
import 'package:photos/utils/data_util.dart';
|
||||
//import 'package:photos/utils/dialog_util.dart';
|
||||
//import 'package:photos/utils/email_util.dart';
|
||||
import 'package:step_progress_indicator/step_progress_indicator.dart';
|
||||
|
||||
class EmailEntryPage extends StatefulWidget {
|
||||
|
|
|
@ -215,7 +215,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
} catch (e, s) {
|
||||
_logger.severe(e, s);
|
||||
hasError = true;
|
||||
showToast("sorry, could not add this to favorites!");
|
||||
showToast("Sorry, could not add this to favorites!");
|
||||
} finally {
|
||||
if (shouldBlockUser) {
|
||||
await dialog.hide();
|
||||
|
@ -227,7 +227,7 @@ class FadingAppBarState extends State<FadingAppBar> {
|
|||
} catch (e, s) {
|
||||
_logger.severe(e, s);
|
||||
hasError = true;
|
||||
showToast("sorry, could not remove this from favorites!");
|
||||
showToast("Sorry, could not remove this from favorites!");
|
||||
}
|
||||
}
|
||||
return hasError ? oldValue : isLiked;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:photos/models/memory.dart';
|
||||
import 'package:photos/services/memories_service.dart';
|
||||
import 'package:photos/ui/blurred_file_backdrop.dart';
|
||||
import 'package:photos/ui/extents_page_view.dart';
|
||||
import 'package:photos/ui/file_widget.dart';
|
||||
import 'package:photos/ui/thumbnail_widget.dart';
|
||||
|
@ -10,6 +8,7 @@ import 'package:photos/utils/date_time_util.dart';
|
|||
import 'package:photos/utils/file_util.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
import 'package:photos/utils/share_util.dart';
|
||||
import 'package:step_progress_indicator/step_progress_indicator.dart';
|
||||
|
||||
class MemoriesWidget extends StatelessWidget {
|
||||
const MemoriesWidget({Key key}) : super(key: key);
|
||||
|
@ -22,9 +21,14 @@ class MemoriesWidget extends StatelessWidget {
|
|||
if (snapshot.hasError || !snapshot.hasData || snapshot.data.isEmpty) {
|
||||
return Container();
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: _buildMemories(snapshot.data),
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildMemories(snapshot.data),
|
||||
Divider(
|
||||
thickness: 1,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -96,7 +100,7 @@ class _MemoryWidgetState extends State<MemoryWidget> {
|
|||
},
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 120,
|
||||
height: 100,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
|
@ -109,10 +113,10 @@ class _MemoryWidgetState extends State<MemoryWidget> {
|
|||
type: MaterialType.transparency,
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white60,
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle1
|
||||
.copyWith(fontSize: 12),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
@ -139,8 +143,8 @@ class _MemoryWidgetState extends State<MemoryWidget> {
|
|||
),
|
||||
child: ClipOval(
|
||||
child: SizedBox(
|
||||
width: isSeen ? 76 : 72,
|
||||
height: isSeen ? 76 : 72,
|
||||
width: isSeen ? 60 : 56,
|
||||
height: isSeen ? 60 : 56,
|
||||
child: Hero(
|
||||
tag: "memories" + memory.file.tag(),
|
||||
child: ThumbnailWidget(
|
||||
|
@ -225,27 +229,69 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
final file = widget.memories[_index].file;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(getFormattedDate(
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.creationTime))),
|
||||
toolbarHeight: 84,
|
||||
automaticallyImplyLeading: false,
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
StepProgressIndicator(
|
||||
totalSteps: widget.memories.length,
|
||||
currentStep: _index + 1,
|
||||
size: 2,
|
||||
selectedColor: Colors.white, //same for both themes
|
||||
unselectedColor: Colors.white.withOpacity(0.4),
|
||||
),
|
||||
SizedBox(
|
||||
height: 18,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
getFormattedDate(
|
||||
DateTime.fromMicrosecondsSinceEpoch(file.creationTime)),
|
||||
style: Theme.of(context).textTheme.subtitle1.copyWith(
|
||||
fontSize: 14, color: Colors.white), //same for both themes
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Colors.black.withOpacity(0.6),
|
||||
Colors.black.withOpacity(0.5),
|
||||
Colors.transparent,
|
||||
],
|
||||
stops: const [0, 0.6, 1],
|
||||
)),
|
||||
),
|
||||
backgroundColor: Color(0x00000000),
|
||||
elevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
onPressed: () {
|
||||
share(context, [file]);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
extendBodyBehindAppBar: true,
|
||||
body: Container(
|
||||
color: Colors.black,
|
||||
child: Stack(children: [
|
||||
BlurredFileBackdrop(file),
|
||||
child: Stack(alignment: Alignment.bottomCenter, children: [
|
||||
_buildSwiper(),
|
||||
bottomGradient(),
|
||||
_buildTitleText(),
|
||||
_buildIndexText(),
|
||||
_buildBottomIcons(),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
@ -256,37 +302,50 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
|||
tag: widget.title,
|
||||
child: Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 0, 160),
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 0, 28),
|
||||
child: AnimatedOpacity(
|
||||
opacity: _opacity,
|
||||
duration: Duration(milliseconds: 500),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
fontSize: 40,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
child: Text(widget.title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headline4
|
||||
.copyWith(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIndexText() {
|
||||
Widget _buildBottomIcons() {
|
||||
final file = widget.memories[_index].file;
|
||||
return Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 0, 20),
|
||||
child: Text(
|
||||
(_index + 1).toString() + " / " + widget.memories.length.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white60,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.bottomRight,
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 26, 20),
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.adaptive.share,
|
||||
color: Colors.white,
|
||||
), //same for both themes
|
||||
onPressed: () {
|
||||
share(context, [file]);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Widget bottomGradient() {
|
||||
return Container(
|
||||
height: 124,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: [
|
||||
Colors.black.withOpacity(0.5), //same for both themes
|
||||
Colors.transparent,
|
||||
],
|
||||
stops: const [0, 0.8],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -701,7 +701,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||
await dialog.hide();
|
||||
if (e is UnsupportedError) {
|
||||
showErrorDialog(context, "insecure device",
|
||||
"sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.");
|
||||
"Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.");
|
||||
} else {
|
||||
showGenericErrorDialog(context);
|
||||
}
|
||||
|
|
|
@ -304,7 +304,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
if (!_isStripeSubscriber &&
|
||||
_hasActiveSubscription &&
|
||||
_currentSubscription.productID != kFreeProductID) {
|
||||
showErrorDialog(context, "sorry",
|
||||
showErrorDialog(context, "Sorry",
|
||||
"please cancel your existing subscription from ${_currentSubscription.paymentProvider} first");
|
||||
return;
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ class _StripeSubscriptionPageState extends State<StripeSubscriptionPage> {
|
|||
await _dialog.hide();
|
||||
if (usage > plan.storage) {
|
||||
showErrorDialog(
|
||||
context, "sorry", "you cannot downgrade to this plan");
|
||||
context, "Sorry", "you cannot downgrade to this plan");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class SubscriptionPage extends StatefulWidget {
|
|||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SubscriptionPage> createState() => _SubscriptionPageState();
|
||||
State<SubscriptionPage> createState() => _SubscriptionPageState();
|
||||
}
|
||||
|
||||
class _SubscriptionPageState extends State<SubscriptionPage> {
|
||||
|
@ -59,8 +59,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
final productID = _isActiveStripeSubscriber
|
||||
? plan.stripeID
|
||||
: Platform.isAndroid
|
||||
? plan.androidID
|
||||
: plan.iosID;
|
||||
? plan.androidID
|
||||
: plan.iosID;
|
||||
return productID != null && productID.isNotEmpty;
|
||||
}).toList();
|
||||
_freePlan = billingPlans.freePlan;
|
||||
|
@ -177,7 +177,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
widgets.add(ValidityWidget(currentSubscription: _currentSubscription));
|
||||
}
|
||||
|
||||
if ( _currentSubscription.productID == kFreeProductID) {
|
||||
if (_currentSubscription.productID == kFreeProductID) {
|
||||
if (widget.isOnboarding) {
|
||||
widgets.add(SkipSubscriptionWidget(freePlan: _freePlan));
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
if (isActive) {
|
||||
return;
|
||||
}
|
||||
showErrorDialog(context, "sorry",
|
||||
showErrorDialog(context, "Sorry",
|
||||
"please visit web.ente.io to manage your subscription");
|
||||
},
|
||||
child: SubscriptionPlanWidget(
|
||||
|
@ -311,13 +311,13 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
if (usage > plan.storage) {
|
||||
await _dialog.hide();
|
||||
showErrorDialog(
|
||||
context, "sorry", "you cannot downgrade to this plan");
|
||||
context, "Sorry", "you cannot downgrade to this plan");
|
||||
return;
|
||||
}
|
||||
}
|
||||
final ProductDetailsResponse response =
|
||||
await InAppPurchaseConnection.instance
|
||||
.queryProductDetails({productID});
|
||||
await InAppPurchaseConnection.instance
|
||||
.queryProductDetails({productID});
|
||||
if (response.notFoundIDs.isNotEmpty) {
|
||||
_logger.severe("Could not find products: " +
|
||||
response.notFoundIDs.toString());
|
||||
|
@ -331,8 +331,8 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
_currentSubscription.productID != plan.androidID;
|
||||
if (isCrossGradingOnAndroid) {
|
||||
final existingProductDetailsResponse =
|
||||
await InAppPurchaseConnection.instance
|
||||
.queryProductDetails({_currentSubscription.productID});
|
||||
await InAppPurchaseConnection.instance
|
||||
.queryProductDetails({_currentSubscription.productID});
|
||||
if (existingProductDetailsResponse.notFoundIDs.isNotEmpty) {
|
||||
_logger.severe("Could not find existing products: " +
|
||||
response.notFoundIDs.toString());
|
||||
|
@ -401,4 +401,3 @@ class _SubscriptionPageState extends State<SubscriptionPage> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,8 +113,8 @@ class _RecoveryPageState extends State<RecoveryPage> {
|
|||
onTap: () {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"sorry",
|
||||
"due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key",
|
||||
"Sorry",
|
||||
"Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key",
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
|
|
|
@ -397,9 +397,9 @@ class _SharingDialogState extends State<SharingDialog> {
|
|||
|
||||
void _showUnSupportedAlert() {
|
||||
AlertDialog alert = AlertDialog(
|
||||
title: Text("sorry"),
|
||||
title: Text("Sorry"),
|
||||
content:
|
||||
Text("sharing is not permitted for free accounts, please subscribe"),
|
||||
Text("Sharing is not permitted for free accounts, please subscribe"),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
|
|
|
@ -72,6 +72,10 @@ String getFullMonth(DateTime dateTime) {
|
|||
return _fullMonths[dateTime.month];
|
||||
}
|
||||
|
||||
String getAbbreviationOfYear(DateTime dateTime) {
|
||||
return (dateTime.year % 100).toString();
|
||||
}
|
||||
|
||||
String getTime(DateTime dateTime) {
|
||||
final hours = dateTime.hour > 9
|
||||
? dateTime.hour.toString()
|
||||
|
@ -95,13 +99,11 @@ String getFormattedTime(DateTime dateTime) {
|
|||
}
|
||||
|
||||
String getFormattedDate(DateTime dateTime) {
|
||||
return getDay(dateTime) +
|
||||
", " +
|
||||
getMonth(dateTime) +
|
||||
return dateTime.day.toString() +
|
||||
" " +
|
||||
dateTime.day.toString() +
|
||||
", " +
|
||||
dateTime.year.toString();
|
||||
getMonth(dateTime) +
|
||||
"'" +
|
||||
getAbbreviationOfYear(dateTime);
|
||||
}
|
||||
|
||||
String daysLeft(int futureTime) {
|
||||
|
|
Loading…
Reference in a new issue