sync_indicator.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:photos/core/configuration.dart';
  4. import 'package:photos/core/event_bus.dart';
  5. import 'package:photos/events/sync_status_update_event.dart';
  6. import 'package:photos/services/sync_service.dart';
  7. import 'package:photos/ui/common_elements.dart';
  8. import 'package:photos/ui/subscription_page.dart';
  9. import 'package:photos/utils/file_uploader.dart';
  10. class SyncIndicator extends StatefulWidget {
  11. const SyncIndicator({Key key}) : super(key: key);
  12. @override
  13. _SyncIndicatorState createState() => _SyncIndicatorState();
  14. }
  15. class _SyncIndicatorState extends State<SyncIndicator> {
  16. SyncStatusUpdate _event;
  17. double _containerHeight = 48;
  18. StreamSubscription<SyncStatusUpdate> _subscription;
  19. @override
  20. void initState() {
  21. _subscription = Bus.instance.on<SyncStatusUpdate>().listen((event) {
  22. setState(() {
  23. _event = event;
  24. });
  25. });
  26. _event = SyncService.instance.getLastSyncStatusEvent();
  27. super.initState();
  28. }
  29. @override
  30. void dispose() {
  31. _subscription.cancel();
  32. super.dispose();
  33. }
  34. @override
  35. Widget build(BuildContext context) {
  36. if (Configuration.instance.hasConfiguredAccount() && _event != null) {
  37. if (_event.status == SyncStatus.completed) {
  38. Future.delayed(Duration(milliseconds: 3000), () {
  39. if (mounted) {
  40. setState(() {
  41. _containerHeight = 0;
  42. });
  43. }
  44. });
  45. } else {
  46. _containerHeight = 48;
  47. }
  48. if (_event.status != SyncStatus.error) {
  49. var icon;
  50. if (_event.status == SyncStatus.completed) {
  51. icon = Icon(
  52. Icons.cloud_done_outlined,
  53. color: Theme.of(context).accentColor,
  54. );
  55. } else {
  56. icon = CircularProgressIndicator(strokeWidth: 2);
  57. }
  58. return AnimatedContainer(
  59. duration: Duration(milliseconds: 300),
  60. height: _containerHeight,
  61. width: double.infinity,
  62. padding: EdgeInsets.all(8),
  63. alignment: Alignment.center,
  64. child: SingleChildScrollView(
  65. physics: NeverScrollableScrollPhysics(),
  66. child: Column(
  67. mainAxisAlignment: MainAxisAlignment.center,
  68. crossAxisAlignment: CrossAxisAlignment.center,
  69. children: [
  70. Row(
  71. mainAxisAlignment: MainAxisAlignment.center,
  72. crossAxisAlignment: CrossAxisAlignment.center,
  73. children: [
  74. Container(
  75. width: 20,
  76. height: 20,
  77. child: icon,
  78. ),
  79. Padding(
  80. padding: const EdgeInsets.fromLTRB(12, 4, 0, 0),
  81. child: Text(_getRefreshingText()),
  82. ),
  83. ],
  84. ),
  85. Padding(padding: EdgeInsets.all(4)),
  86. Divider(),
  87. ],
  88. ),
  89. ),
  90. );
  91. } else {
  92. return _getErrorWidget();
  93. }
  94. }
  95. return Container();
  96. }
  97. Widget _getErrorWidget() {
  98. if (_event.error is NoActiveSubscriptionError) {
  99. return Container(
  100. child: Column(
  101. children: [
  102. Row(
  103. mainAxisAlignment: MainAxisAlignment.center,
  104. crossAxisAlignment: CrossAxisAlignment.center,
  105. children: [
  106. Icon(
  107. Icons.error_outline,
  108. color: Theme.of(context).accentColor,
  109. ),
  110. Padding(padding: EdgeInsets.all(4)),
  111. Text("your subscription has expired"),
  112. ],
  113. ),
  114. Padding(padding: EdgeInsets.all(6)),
  115. Container(
  116. width: double.infinity,
  117. height: 64,
  118. padding: const EdgeInsets.fromLTRB(80, 0, 80, 0),
  119. child: button("subscribe", onPressed: () {
  120. Navigator.of(context).push(
  121. MaterialPageRoute(
  122. builder: (BuildContext context) {
  123. return SubscriptionPage();
  124. },
  125. ),
  126. );
  127. }),
  128. ),
  129. Padding(padding: EdgeInsets.all(8)),
  130. ],
  131. ),
  132. );
  133. } else {
  134. return Row(
  135. children: [
  136. Icon(
  137. Icons.error_outline,
  138. color: Theme.of(context).accentColor,
  139. ),
  140. Text(_event.reason ?? "upload failed"),
  141. ],
  142. );
  143. }
  144. }
  145. String _getRefreshingText() {
  146. if (_event == null ||
  147. _event.status == SyncStatus.applying_local_diff ||
  148. _event.status == SyncStatus.applying_remote_diff) {
  149. return "syncing...";
  150. }
  151. if (_event.status == SyncStatus.preparing_for_upload) {
  152. return "encrypting backup...";
  153. }
  154. if (_event.status == SyncStatus.in_progress) {
  155. return _event.completed.toString() +
  156. "/" +
  157. _event.total.toString() +
  158. " memories preserved";
  159. }
  160. if (_event.status == SyncStatus.paused) {
  161. return _event.reason;
  162. }
  163. if (_event.status == SyncStatus.completed) {
  164. if (_event.wasStopped) {
  165. return "sync stopped";
  166. } else {
  167. return "all memories preserved";
  168. }
  169. }
  170. // _event.status == SyncStatus.error
  171. return _event.reason ?? "upload failed";
  172. }
  173. }