mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 10:58:40 +00:00
fix #200
This commit is contained in:
parent
036c4cab77
commit
588a790554
24 changed files with 171 additions and 97 deletions
|
|
@ -3,6 +3,7 @@ import 'package:twonly/globals.dart';
|
|||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||
import 'package:twonly/src/providers/connection.provider.dart';
|
||||
import 'package:twonly/src/providers/settings.provider.dart';
|
||||
import 'package:twonly/src/services/api/media_send.dart';
|
||||
import 'package:twonly/src/services/notification.service.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/onboarding.view.dart';
|
||||
|
|
@ -71,7 +72,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||
|
||||
Future initAsync() async {
|
||||
setUserPlan();
|
||||
apiService.connect();
|
||||
await apiService.connect();
|
||||
// call this function so invalid media files are get purged
|
||||
retryMediaUpload(true);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ void main() async {
|
|||
apiService = ApiService();
|
||||
twonlyDB = TwonlyDatabase();
|
||||
await twonlyDB.messagesDao.resetPendingDownloadState();
|
||||
await twonlyDB.messagesDao.handleMediaFilesOlderThan7Days();
|
||||
|
||||
// purge media files in the background
|
||||
purgeReceivedMediaFiles();
|
||||
|
|
|
|||
|
|
@ -69,6 +69,22 @@ class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
|
|||
.write(MessagesCompanion(contentJson: Value(null)));
|
||||
}
|
||||
|
||||
Future handleMediaFilesOlderThan7Days() {
|
||||
/// media files will be deleted by the server after 7 days, so delete them here also
|
||||
return (update(messages)
|
||||
..where(
|
||||
(t) => (t.kind.equals(MessageKind.media.name) &
|
||||
t.openedAt.isNull() &
|
||||
t.messageOtherId.isNull() &
|
||||
(t.sendAt.isSmallerThanValue(
|
||||
DateTime.now().subtract(
|
||||
Duration(days: 8),
|
||||
),
|
||||
))),
|
||||
))
|
||||
.write(MessagesCompanion(errorWhileSending: Value(true)));
|
||||
}
|
||||
|
||||
Future<List<Message>> getAllMessagesPendingDownloading() {
|
||||
return (select(messages)
|
||||
..where(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ enum MessageKind {
|
|||
flameSync,
|
||||
opened,
|
||||
ack,
|
||||
pushKey
|
||||
pushKey,
|
||||
receiveMediaError,
|
||||
}
|
||||
|
||||
enum DownloadState {
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@
|
|||
"checkoutOptions": "Optionen",
|
||||
"checkoutPayYearly": "Jährlich bezahlen",
|
||||
"checkoutTotal": "Gesamt",
|
||||
"selectPaymentMethode": "Zahlungsmethode auswählen",
|
||||
"selectPaymentMethod": "Zahlungsmethode auswählen",
|
||||
"twonlyCredit": "twonly-Guthaben",
|
||||
"notEnoughCredit": "Du hast nicht genügend Guthaben!",
|
||||
"chargeCredit": "Guthaben aufladen",
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@
|
|||
"refund": "Refund",
|
||||
"checkoutPayYearly": "Pay yearly",
|
||||
"checkoutTotal": "Total",
|
||||
"selectPaymentMethode": "Select Payment Method",
|
||||
"selectPaymentMethod": "Select Payment Method",
|
||||
"twonlyCredit": "twonly-Credit",
|
||||
"notEnoughCredit": "You do not have enough credit!",
|
||||
"chargeCredit": "Charge credit",
|
||||
|
|
|
|||
|
|
@ -1382,11 +1382,11 @@ abstract class AppLocalizations {
|
|||
/// **'Total'**
|
||||
String get checkoutTotal;
|
||||
|
||||
/// No description provided for @selectPaymentMethode.
|
||||
/// No description provided for @selectPaymentMethod.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Payment Method'**
|
||||
String get selectPaymentMethode;
|
||||
String get selectPaymentMethod;
|
||||
|
||||
/// No description provided for @twonlyCredit.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get checkoutTotal => 'Gesamt';
|
||||
|
||||
@override
|
||||
String get selectPaymentMethode => 'Zahlungsmethode auswählen';
|
||||
String get selectPaymentMethod => 'Zahlungsmethode auswählen';
|
||||
|
||||
@override
|
||||
String get twonlyCredit => 'twonly-Guthaben';
|
||||
|
|
|
|||
|
|
@ -720,7 +720,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get checkoutTotal => 'Total';
|
||||
|
||||
@override
|
||||
String get selectPaymentMethode => 'Select Payment Method';
|
||||
String get selectPaymentMethod => 'Select Payment Method';
|
||||
|
||||
@override
|
||||
String get twonlyCredit => 'twonly-Credit';
|
||||
|
|
|
|||
|
|
@ -39,11 +39,12 @@ class MessageJson {
|
|||
final int? messageId;
|
||||
DateTime timestamp;
|
||||
|
||||
MessageJson(
|
||||
{required this.kind,
|
||||
this.messageId,
|
||||
required this.content,
|
||||
required this.timestamp});
|
||||
MessageJson({
|
||||
required this.kind,
|
||||
this.messageId,
|
||||
required this.content,
|
||||
required this.timestamp,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class ApiService {
|
|||
|
||||
// reconnection params
|
||||
Timer? reconnectionTimer;
|
||||
// int _reconnectionDelay = 5;
|
||||
int _reconnectionDelay = 5;
|
||||
|
||||
final HashMap<Int64, server.ServerToClient?> messagesV0 = HashMap();
|
||||
IOWebSocketChannel? _channel;
|
||||
|
|
@ -81,7 +81,7 @@ class ApiService {
|
|||
if (!globalIsAppInBackground) {
|
||||
retransmitRawBytes();
|
||||
tryTransmitMessages();
|
||||
retryMediaUpload();
|
||||
retryMediaUpload(false);
|
||||
tryDownloadAllMediaFiles();
|
||||
notifyContactsAboutProfileChange();
|
||||
twonlyDB.markUpdated();
|
||||
|
|
@ -92,6 +92,7 @@ class ApiService {
|
|||
|
||||
Future onConnected() async {
|
||||
await authenticate();
|
||||
_reconnectionDelay = 5;
|
||||
globalCallbackConnectionState(true);
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +101,12 @@ class ApiService {
|
|||
isAuthenticated = false;
|
||||
globalCallbackConnectionState(false);
|
||||
await twonlyDB.messagesDao.resetPendingDownloadState();
|
||||
reconnectionTimer ??= Timer(Duration(seconds: _reconnectionDelay), () {
|
||||
Log.info("starting with reconnection.");
|
||||
reconnectionTimer = null;
|
||||
connect();
|
||||
});
|
||||
_reconnectionDelay += 5;
|
||||
}
|
||||
|
||||
Future close(Function callback) async {
|
||||
|
|
@ -113,11 +120,13 @@ class ApiService {
|
|||
callback();
|
||||
}
|
||||
|
||||
Future<bool> connect() async {
|
||||
Future<bool> connect({bool force = false}) async {
|
||||
if (reconnectionTimer != null && !force) {
|
||||
return false;
|
||||
}
|
||||
reconnectionTimer?.cancel();
|
||||
final user = await getUser();
|
||||
if (user != null && user.isDemoUser) {
|
||||
print("DEMO user");
|
||||
// the demo user should not be able to connect to the API server...
|
||||
globalCallbackConnectionState(true);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -126,9 +135,7 @@ class ApiService {
|
|||
return true;
|
||||
}
|
||||
// ensure that the connect function is not called again by the timer.
|
||||
if (reconnectionTimer != null) {
|
||||
reconnectionTimer!.cancel();
|
||||
}
|
||||
reconnectionTimer?.cancel();
|
||||
|
||||
isAuthenticated = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ import 'package:http/http.dart' as http;
|
|||
// import 'package:twonly/src/providers/api/api_utils.dart';
|
||||
import 'package:twonly/src/services/api/media_send.dart';
|
||||
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/client_to_server.pb.dart'
|
||||
as client;
|
||||
import 'package:twonly/src/services/api/utils.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
||||
|
|
@ -75,7 +74,8 @@ Future<bool> isAllowedToDownload(bool isVideo) async {
|
|||
return false;
|
||||
}
|
||||
|
||||
Future startDownloadMedia(Message message, bool force) async {
|
||||
Future startDownloadMedia(Message message, bool force,
|
||||
{int retryCounter = 0}) async {
|
||||
if (message.contentJson == null) return;
|
||||
if (downloadStartedForMediaReceived[message.messageId] != null) {
|
||||
DateTime started = downloadStartedForMediaReceived[message.messageId]!;
|
||||
|
|
@ -151,12 +151,10 @@ Future startDownloadMedia(Message message, bool force) async {
|
|||
}, onDone: () async {
|
||||
if (r.statusCode != 200) {
|
||||
Log.error("Download error: $r");
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
message.messageId,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
if (r.statusCode == 418) {
|
||||
Log.error("Got custom error code: ${chunks.toList()}");
|
||||
handleMediaError(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -172,13 +170,18 @@ Future startDownloadMedia(Message message, bool force) async {
|
|||
offset += chunk.length;
|
||||
}
|
||||
await writeMediaFile(message.messageId, "encrypted", bytes);
|
||||
handleEncryptedFile(message, encryptedBytesTmp: bytes);
|
||||
handleEncryptedFile(message,
|
||||
encryptedBytesTmp: bytes, retryCounter: retryCounter);
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future handleEncryptedFile(Message msg, {Uint8List? encryptedBytesTmp}) async {
|
||||
Future handleEncryptedFile(
|
||||
Message msg, {
|
||||
Uint8List? encryptedBytesTmp,
|
||||
int retryCounter = 0,
|
||||
}) async {
|
||||
Uint8List? encryptedBytes =
|
||||
encryptedBytesTmp ?? await readMediaFile(msg.messageId, "encrypted");
|
||||
|
||||
|
|
@ -211,16 +214,16 @@ Future handleEncryptedFile(Message msg, {Uint8List? encryptedBytesTmp}) async {
|
|||
|
||||
await writeMediaFile(msg.messageId, "png", imageBytes);
|
||||
} catch (e) {
|
||||
Log.error("Decryption error: $e");
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
msg.messageId,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
// answers with ok, so the server will delete the message
|
||||
var ok = client.Response_Ok()..none = true;
|
||||
return client.Response()..ok = ok;
|
||||
if (retryCounter >= 1) {
|
||||
Log.error(
|
||||
"could not decrypt the media file in the second try. reporting error to user: $e");
|
||||
handleMediaError(msg);
|
||||
return;
|
||||
}
|
||||
Log.error("could not decrypt the media file trying again: $e");
|
||||
startDownloadMedia(msg, true, retryCounter: retryCounter + 1);
|
||||
// try downloading again....
|
||||
return;
|
||||
}
|
||||
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ Future<bool> checkForFailedUploads() async {
|
|||
}
|
||||
|
||||
final lockingHandleMediaFile = Mutex();
|
||||
Future retryMediaUpload({int maxRetries = 3}) async {
|
||||
Future retryMediaUpload(bool appRestarted, {int maxRetries = 3}) async {
|
||||
if (maxRetries == 0) {
|
||||
Log.error("retried media upload 3 times. abort retrying");
|
||||
return;
|
||||
|
|
@ -116,16 +116,23 @@ Future retryMediaUpload({int maxRetries = 3}) async {
|
|||
Log.info("re uploading ${mediaFiles.length} media files.");
|
||||
for (final mediaFile in mediaFiles) {
|
||||
if (mediaFile.messageIds == null || mediaFile.metadata == null) {
|
||||
// the media upload was canceled,
|
||||
if (mediaFile.uploadTokens != null) {
|
||||
/// the file was already uploaded.
|
||||
/// notify the server to remove the upload
|
||||
apiService.getDownloadTokens(mediaFile.uploadTokens!.uploadToken, 0);
|
||||
if (appRestarted) {
|
||||
/// When the app got restarted and the messageIds or the metadata is not
|
||||
/// set then the app was closed before the images was send.
|
||||
|
||||
// the media upload was canceled,
|
||||
if (mediaFile.uploadTokens != null) {
|
||||
/// the file was already uploaded.
|
||||
/// notify the server to remove the upload
|
||||
apiService.getDownloadTokens(
|
||||
mediaFile.uploadTokens!.uploadToken, 0);
|
||||
}
|
||||
await twonlyDB.mediaUploadsDao
|
||||
.deleteMediaUpload(mediaFile.mediaUploadId);
|
||||
Log.info(
|
||||
"upload can be removed, the finalized function was never called...",
|
||||
);
|
||||
}
|
||||
await twonlyDB.mediaUploadsDao
|
||||
.deleteMediaUpload(mediaFile.mediaUploadId);
|
||||
Log.info(
|
||||
"upload can be removed, the finalized function was never called...");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +145,7 @@ Future retryMediaUpload({int maxRetries = 3}) async {
|
|||
return false;
|
||||
});
|
||||
if (retry) {
|
||||
await retryMediaUpload(maxRetries: maxRetries - 1);
|
||||
await retryMediaUpload(false, maxRetries: maxRetries - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ Future<Map<String, dynamic>> getAllMessagesForRetransmitting() async {
|
|||
|
||||
Future<Result> sendRetransmitMessage(
|
||||
String stateId, RetransmitMessage msg) async {
|
||||
Log.info("Sending ${msg.messageId}");
|
||||
Result resp =
|
||||
await apiService.sendTextMessage(msg.userId, msg.bytes, msg.pushData);
|
||||
|
||||
|
|
@ -130,8 +131,8 @@ Future<Result> sendRetransmitMessage(
|
|||
}
|
||||
|
||||
if (resp.isSuccess) {
|
||||
retry = false;
|
||||
if (msg.messageId != null) {
|
||||
retry = false;
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
msg.messageId!,
|
||||
MessagesCompanion(acknowledgeByServer: Value(true)),
|
||||
|
|
|
|||
|
|
@ -86,23 +86,35 @@ Future<client.Response> handleNewMessage(int fromUserId, Uint8List body) async {
|
|||
}
|
||||
}
|
||||
|
||||
case MessageKind.opened:
|
||||
final update = MessagesCompanion(openedAt: Value(message.timestamp));
|
||||
await twonlyDB.messagesDao.updateMessageByOtherUser(
|
||||
fromUserId,
|
||||
message.messageId!,
|
||||
update,
|
||||
);
|
||||
final openedMessage = await twonlyDB.messagesDao
|
||||
.getMessageByMessageId(message.messageId!)
|
||||
.getSingleOrNull();
|
||||
if (openedMessage != null &&
|
||||
openedMessage.kind == MessageKind.textMessage) {
|
||||
await twonlyDB.messagesDao.openedAllNonMediaMessagesFromOtherUser(
|
||||
case MessageKind.receiveMediaError:
|
||||
if (message.messageId != null) {
|
||||
await twonlyDB.messagesDao.updateMessageByOtherUser(
|
||||
fromUserId,
|
||||
message.messageId!,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
case MessageKind.opened:
|
||||
if (message.messageId != null) {
|
||||
final update = MessagesCompanion(openedAt: Value(message.timestamp));
|
||||
await twonlyDB.messagesDao.updateMessageByOtherUser(
|
||||
fromUserId,
|
||||
message.messageId!,
|
||||
update,
|
||||
);
|
||||
final openedMessage = await twonlyDB.messagesDao
|
||||
.getMessageByMessageId(message.messageId!)
|
||||
.getSingleOrNull();
|
||||
if (openedMessage != null &&
|
||||
openedMessage.kind == MessageKind.textMessage) {
|
||||
await twonlyDB.messagesDao.openedAllNonMediaMessagesFromOtherUser(
|
||||
fromUserId,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageKind.rejectRequest:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/model/json/message.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/client_to_server.pb.dart'
|
||||
as client;
|
||||
|
|
@ -66,3 +68,23 @@ Future rejectUser(int contactId) async {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future handleMediaError(Message message) async {
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
message.messageId,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
if (message.messageOtherId != null) {
|
||||
encryptAndSendMessageAsync(
|
||||
null,
|
||||
message.contactId,
|
||||
MessageJson(
|
||||
kind: MessageKind.receiveMediaError,
|
||||
timestamp: DateTime.now(),
|
||||
content: MessageContent(),
|
||||
messageId: message.messageOtherId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ String formatDuration(int seconds) {
|
|||
}
|
||||
}
|
||||
|
||||
InputDecoration getInputDecoration(context, hintText) {
|
||||
InputDecoration getInputDecoration(BuildContext context, String hintText) {
|
||||
final primaryColor =
|
||||
Theme.of(context).colorScheme.primary; // Get the primary color
|
||||
return InputDecoration(
|
||||
|
|
@ -279,7 +279,7 @@ Future insertDemoContacts() async {
|
|||
if (config['accepted'] ?? false) {
|
||||
for (var i = 0; i < 20; i++) {
|
||||
int chatId = Random().nextInt(chatMessages.length);
|
||||
int? messageId = await twonlyDB.messagesDao.insertMessage(
|
||||
await twonlyDB.messagesDao.insertMessage(
|
||||
MessagesCompanion(
|
||||
contactId: Value(userId),
|
||||
kind: Value(MessageKind.textMessage),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/src/model/json/userdata.dart';
|
||||
import 'package:twonly/src/providers/connection.provider.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
||||
Future<bool> isUserCreated() async {
|
||||
|
|
@ -28,6 +31,17 @@ Future<UserData?> getUser() async {
|
|||
}
|
||||
}
|
||||
|
||||
Future updateUsersPlan(BuildContext context, String planId) async {
|
||||
context.read<CustomChangeProvider>().plan = planId;
|
||||
var user = await getUser();
|
||||
if (user != null) {
|
||||
user.subscriptionPlan = planId;
|
||||
await updateUser(user);
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
context.read<CustomChangeProvider>().updatePlan(planId);
|
||||
}
|
||||
|
||||
Future updateUser(UserData userData) async {
|
||||
final storage = FlutterSecureStorage();
|
||||
storage.write(key: "userData", value: jsonEncode(userData));
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ class _ChatListViewState extends State<ChatListView> {
|
|||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await apiService.close(() {});
|
||||
await apiService.connect();
|
||||
await apiService.connect(force: true);
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
},
|
||||
child: ListView.builder(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import 'package:lottie/lottie.dart';
|
|||
import 'package:no_screenshot/no_screenshot.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/services/api/utils.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||
|
|
@ -252,13 +253,9 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
|
||||
if ((imageBytes == null && !content.isVideo) ||
|
||||
(content.isVideo && videoController == null)) {
|
||||
Log.error("media files are not found...");
|
||||
// When the message should be downloaded but imageBytes are null then a error happened
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
current.messageId,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
await handleMediaError(current);
|
||||
return nextMediaOrExit();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class HomeViewState extends State<HomeView> {
|
|||
offsetRatio = offsetFromOne.abs();
|
||||
});
|
||||
}
|
||||
if (cameraController == null && !initCameraStarted) {
|
||||
if (cameraController == null && !initCameraStarted && offsetRatio < 1) {
|
||||
initCameraStarted = true;
|
||||
selectCamera(selectedCameraDetails.cameraId, false, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class _CheckoutViewState extends State<CheckoutView> {
|
|||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(context.lang.selectPaymentMethode)),
|
||||
child: Text(context.lang.selectPaymentMethod)),
|
||||
),
|
||||
SizedBox(height: 20)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/providers/connection.provider.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/settings/subscription/subscription.view.dart';
|
||||
|
|
@ -82,7 +80,7 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
|
|||
(balanceInCents == null || balanceInCents! >= checkoutInCents));
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.lang.selectPaymentMethode),
|
||||
title: Text(context.lang.selectPaymentMethod),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
|
|
@ -222,21 +220,12 @@ class _SelectPaymentViewState extends State<SelectPaymentView> {
|
|||
widget.planId!, widget.payMonthly!, tryAutoRenewal);
|
||||
if (!context.mounted) return;
|
||||
if (res.isSuccess) {
|
||||
context.read<CustomChangeProvider>().plan =
|
||||
widget.planId!;
|
||||
var user = await getUser();
|
||||
if (user != null) {
|
||||
user.subscriptionPlan = widget.planId!;
|
||||
await updateUser(user);
|
||||
}
|
||||
await updateUsersPlan(context, widget.planId!);
|
||||
if (!context.mounted) return;
|
||||
context
|
||||
.read<CustomChangeProvider>()
|
||||
.updatePlan(widget.planId!);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content:
|
||||
Text(context.lang.planSuccessUpgraded)),
|
||||
content: Text(context.lang.planSuccessUpgraded),
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -556,12 +556,12 @@ Future redeemUserInviteCode(BuildContext context, String newPlan) async {
|
|||
if (res.isSuccess) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.lang.redeemUserInviteCodeSuccess)),
|
||||
content: Text(context.lang.redeemUserInviteCodeSuccess),
|
||||
),
|
||||
);
|
||||
// reconnect to load new plan.
|
||||
apiService.close(() {
|
||||
apiService.connect();
|
||||
});
|
||||
await apiService.close(() {});
|
||||
await apiService.connect();
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(errorCodeToText(context, res.error))),
|
||||
|
|
|
|||
Loading…
Reference in a new issue