mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 19:58:41 +00:00
fix #217
This commit is contained in:
parent
ff96a80373
commit
434945cb54
10 changed files with 117 additions and 30 deletions
70
lib/app.dart
70
lib/app.dart
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
|
|
@ -5,6 +6,7 @@ import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||||
import 'package:twonly/src/providers/connection.provider.dart';
|
import 'package:twonly/src/providers/connection.provider.dart';
|
||||||
import 'package:twonly/src/providers/settings.provider.dart';
|
import 'package:twonly/src/providers/settings.provider.dart';
|
||||||
import 'package:twonly/src/services/api/media_upload.dart';
|
import 'package:twonly/src/services/api/media_upload.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/onboarding/onboarding.view.dart';
|
import 'package:twonly/src/views/onboarding/onboarding.view.dart';
|
||||||
import 'package:twonly/src/views/home.view.dart';
|
import 'package:twonly/src/views/home.view.dart';
|
||||||
|
|
@ -12,6 +14,7 @@ import 'package:twonly/src/views/onboarding/register.view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
// these two callbacks are called on updated to the corresponding database
|
// these two callbacks are called on updated to the corresponding database
|
||||||
|
|
||||||
|
|
@ -24,6 +27,7 @@ class App extends StatefulWidget {
|
||||||
|
|
||||||
class _AppState extends State<App> with WidgetsBindingObserver {
|
class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
bool wasPaused = false;
|
bool wasPaused = false;
|
||||||
|
bool appIsOutdated = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -62,6 +66,12 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
|
|
||||||
Future initAsync() async {
|
Future initAsync() async {
|
||||||
setUserPlan();
|
setUserPlan();
|
||||||
|
globalCallbackAppIsOutdated = () async {
|
||||||
|
context.read<CustomChangeProvider>().updateConnectionState(false);
|
||||||
|
setState(() {
|
||||||
|
appIsOutdated = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
await apiService.connect(force: true);
|
await apiService.connect(force: true);
|
||||||
apiService.listenToNetworkChanges();
|
apiService.listenToNetworkChanges();
|
||||||
// call this function so invalid media files are get purged
|
// call this function so invalid media files are get purged
|
||||||
|
|
@ -87,6 +97,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
globalCallbackConnectionState = (a) {};
|
globalCallbackConnectionState = (a) {};
|
||||||
|
globalCallbackAppIsOutdated = () {};
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,8 +144,10 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
themeMode: context.watch<SettingsChangeProvider>().themeMode,
|
themeMode: context.watch<SettingsChangeProvider>().themeMode,
|
||||||
initialRoute: '/',
|
initialRoute: '/',
|
||||||
routes: {
|
routes: {
|
||||||
"/": (context) => AppMainWidget(initialPage: 1),
|
"/": (context) =>
|
||||||
"/chats": (context) => AppMainWidget(initialPage: 0)
|
AppMainWidget(initialPage: 1, appIsOutdated: appIsOutdated),
|
||||||
|
"/chats": (context) =>
|
||||||
|
AppMainWidget(initialPage: 0, appIsOutdated: appIsOutdated)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -143,8 +156,10 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppMainWidget extends StatefulWidget {
|
class AppMainWidget extends StatefulWidget {
|
||||||
const AppMainWidget({super.key, required this.initialPage});
|
const AppMainWidget(
|
||||||
|
{super.key, required this.initialPage, required this.appIsOutdated});
|
||||||
final int initialPage;
|
final int initialPage;
|
||||||
|
final bool appIsOutdated;
|
||||||
@override
|
@override
|
||||||
State<AppMainWidget> createState() => _AppMainWidgetState();
|
State<AppMainWidget> createState() => _AppMainWidgetState();
|
||||||
}
|
}
|
||||||
|
|
@ -187,6 +202,55 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (widget.appIsOutdated)
|
||||||
|
Positioned(
|
||||||
|
top: 60,
|
||||||
|
left: 30,
|
||||||
|
right: 30,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
context.lang.appOutdated,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
softWrap: true,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.copyWith(color: Colors.white, fontSize: 16),
|
||||||
|
),
|
||||||
|
if (Platform.isAndroid) SizedBox(height: 5),
|
||||||
|
if (Platform.isAndroid)
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
launchUrl(Uri.parse(
|
||||||
|
"https://play.google.com/store/apps/details?id=eu.twonly"));
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
context.lang.appOutdatedBtn,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.copyWith(color: Colors.white, fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ bool gIsDemoUser = false;
|
||||||
|
|
||||||
// This callback called by the apiProvider
|
// This callback called by the apiProvider
|
||||||
Function(bool) globalCallbackConnectionState = (a) {};
|
Function(bool) globalCallbackConnectionState = (a) {};
|
||||||
|
Function() globalCallbackAppIsOutdated = () {};
|
||||||
|
|
||||||
bool globalIsAppInBackground = true;
|
bool globalIsAppInBackground = true;
|
||||||
int globalBestFriendUserId = -1;
|
int globalBestFriendUserId = -1;
|
||||||
|
|
|
||||||
|
|
@ -298,5 +298,7 @@
|
||||||
"backupTwonlySaveNow": "Jetzt speichern",
|
"backupTwonlySaveNow": "Jetzt speichern",
|
||||||
"inviteFriends": "Freunde einladen",
|
"inviteFriends": "Freunde einladen",
|
||||||
"inviteFriendsShareBtn": "Teilen",
|
"inviteFriendsShareBtn": "Teilen",
|
||||||
"inviteFriendsShareText": "Wechseln wir zu twonly: {url}"
|
"inviteFriendsShareText": "Wechseln wir zu twonly: {url}",
|
||||||
|
"appOutdated": "Deine Version von twonly ist veraltet.",
|
||||||
|
"appOutdatedBtn": "Jetzt aktualisieren."
|
||||||
}
|
}
|
||||||
|
|
@ -455,5 +455,7 @@
|
||||||
"twonlySafeRecoverBtn": "Restore backup",
|
"twonlySafeRecoverBtn": "Restore backup",
|
||||||
"inviteFriends": "Invite your friends",
|
"inviteFriends": "Invite your friends",
|
||||||
"inviteFriendsShareBtn": "Share",
|
"inviteFriendsShareBtn": "Share",
|
||||||
"inviteFriendsShareText": "Let's switch to twonly: {url}"
|
"inviteFriendsShareText": "Let's switch to twonly: {url}",
|
||||||
|
"appOutdated": "Your version of twonly is out of date.",
|
||||||
|
"appOutdatedBtn": "Update Now"
|
||||||
}
|
}
|
||||||
|
|
@ -1831,6 +1831,18 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Let\'s switch to twonly: {url}'**
|
/// **'Let\'s switch to twonly: {url}'**
|
||||||
String inviteFriendsShareText(Object url);
|
String inviteFriendsShareText(Object url);
|
||||||
|
|
||||||
|
/// No description provided for @appOutdated.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Your version of twonly is out of date.'**
|
||||||
|
String get appOutdated;
|
||||||
|
|
||||||
|
/// No description provided for @appOutdatedBtn.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Update Now'**
|
||||||
|
String get appOutdatedBtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -974,4 +974,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String inviteFriendsShareText(Object url) {
|
String inviteFriendsShareText(Object url) {
|
||||||
return 'Wechseln wir zu twonly: $url';
|
return 'Wechseln wir zu twonly: $url';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appOutdated => 'Deine Version von twonly ist veraltet.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appOutdatedBtn => 'Jetzt aktualisieren.';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -968,4 +968,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String inviteFriendsShareText(Object url) {
|
String inviteFriendsShareText(Object url) {
|
||||||
return 'Let\'s switch to twonly: $url';
|
return 'Let\'s switch to twonly: $url';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appOutdated => 'Your version of twonly is out of date.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appOutdatedBtn => 'Update Now';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class ApiService {
|
||||||
final String apiHost = (kDebugMode) ? "10.99.0.140:3030" : "api.twonly.eu";
|
final String apiHost = (kDebugMode) ? "10.99.0.140:3030" : "api.twonly.eu";
|
||||||
final String apiSecure = (kDebugMode) ? "" : "s";
|
final String apiSecure = (kDebugMode) ? "" : "s";
|
||||||
|
|
||||||
|
bool appIsOutdated = false;
|
||||||
bool isAuthenticated = false;
|
bool isAuthenticated = false;
|
||||||
ApiService();
|
ApiService();
|
||||||
|
|
||||||
|
|
@ -60,6 +61,7 @@ class ApiService {
|
||||||
StreamSubscription<List<ConnectivityResult>>? connectivitySubscription;
|
StreamSubscription<List<ConnectivityResult>>? connectivitySubscription;
|
||||||
|
|
||||||
Future<bool> _connectTo(String apiUrl) async {
|
Future<bool> _connectTo(String apiUrl) async {
|
||||||
|
if (appIsOutdated) return false;
|
||||||
try {
|
try {
|
||||||
var channel = IOWebSocketChannel.connect(
|
var channel = IOWebSocketChannel.connect(
|
||||||
Uri.parse(apiUrl),
|
Uri.parse(apiUrl),
|
||||||
|
|
@ -303,6 +305,13 @@ class ApiService {
|
||||||
Result res = asResult(await _waitForResponse(seq));
|
Result res = asResult(await _waitForResponse(seq));
|
||||||
if (res.isError) {
|
if (res.isError) {
|
||||||
Log.error("got error from server: ${res.error}");
|
Log.error("got error from server: ${res.error}");
|
||||||
|
if (res.error == ErrorCode.AppVersionOutdated) {
|
||||||
|
globalCallbackAppIsOutdated();
|
||||||
|
Log.error("App Version is OUTDATED.");
|
||||||
|
appIsOutdated = true;
|
||||||
|
await close(() {});
|
||||||
|
return Result.error(ErrorCode.InternalError);
|
||||||
|
}
|
||||||
if (res.error == ErrorCode.SessionNotAuthenticated) {
|
if (res.error == ErrorCode.SessionNotAuthenticated) {
|
||||||
isAuthenticated = false;
|
isAuthenticated = false;
|
||||||
if (authenticated) {
|
if (authenticated) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
||||||
class SaveToGalleryButton extends StatefulWidget {
|
class SaveToGalleryButton extends StatefulWidget {
|
||||||
final Future<Uint8List?> Function() getMergedImage;
|
final Future<Uint8List?> Function() getMergedImage;
|
||||||
final String? sendNextMediaToUserName;
|
final bool displayButtonLabel;
|
||||||
final File? videoFilePath;
|
final File? videoFilePath;
|
||||||
final int? mediaUploadId;
|
final int? mediaUploadId;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
|
|
@ -20,7 +20,7 @@ class SaveToGalleryButton extends StatefulWidget {
|
||||||
super.key,
|
super.key,
|
||||||
required this.getMergedImage,
|
required this.getMergedImage,
|
||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
this.sendNextMediaToUserName,
|
required this.displayButtonLabel,
|
||||||
this.mediaUploadId,
|
this.mediaUploadId,
|
||||||
this.videoFilePath,
|
this.videoFilePath,
|
||||||
});
|
});
|
||||||
|
|
@ -107,8 +107,8 @@ class SaveToGalleryButtonState extends State<SaveToGalleryButton> {
|
||||||
: _imageSaved
|
: _imageSaved
|
||||||
? Icon(Icons.check)
|
? Icon(Icons.check)
|
||||||
: FaIcon(FontAwesomeIcons.floppyDisk),
|
: FaIcon(FontAwesomeIcons.floppyDisk),
|
||||||
if (widget.sendNextMediaToUserName == null) SizedBox(width: 10),
|
if (widget.displayButtonLabel) SizedBox(width: 10),
|
||||||
if (widget.sendNextMediaToUserName == null)
|
if (widget.displayButtonLabel)
|
||||||
Text(_imageSaved
|
Text(_imageSaved
|
||||||
? context.lang.shareImagedEditorSavedImage
|
? context.lang.shareImagedEditorSavedImage
|
||||||
: context.lang.shareImagedEditorSaveImage)
|
: context.lang.shareImagedEditorSaveImage)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/globals.dart';
|
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'
|
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'
|
||||||
show ErrorCode;
|
show ErrorCode;
|
||||||
import 'package:twonly/src/services/api/media_upload.dart';
|
import 'package:twonly/src/services/api/media_upload.dart';
|
||||||
|
|
@ -56,7 +55,6 @@ class ShareImageEditorView extends StatefulWidget {
|
||||||
class _ShareImageEditorView extends State<ShareImageEditorView> {
|
class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
bool _isRealTwonly = false;
|
bool _isRealTwonly = false;
|
||||||
int maxShowTime = gMediaShowInfinite;
|
int maxShowTime = gMediaShowInfinite;
|
||||||
String? sendNextMediaToUserName;
|
|
||||||
double tabDownPosition = 0;
|
double tabDownPosition = 0;
|
||||||
bool sendingOrLoadingImage = true;
|
bool sendingOrLoadingImage = true;
|
||||||
bool loadingImage = true;
|
bool loadingImage = true;
|
||||||
|
|
@ -138,15 +136,6 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future updateAsync(int userId) async {
|
|
||||||
if (sendNextMediaToUserName != null) return;
|
|
||||||
Contact? contact =
|
|
||||||
await twonlyDB.contactsDao.getContactByUserId(userId).getSingleOrNull();
|
|
||||||
if (contact != null) {
|
|
||||||
sendNextMediaToUserName = getContactDisplayName(contact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> get actionsAtTheRight {
|
List<Widget> get actionsAtTheRight {
|
||||||
if (layers.isNotEmpty &&
|
if (layers.isNotEmpty &&
|
||||||
layers.last.isEditing &&
|
layers.last.isEditing &&
|
||||||
|
|
@ -449,10 +438,6 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
pixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
if (widget.sendTo != null) {
|
|
||||||
sendNextMediaToUserName = getContactDisplayName(widget.sendTo!);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
widget.sharedFromGallery ? null : Colors.white.withAlpha(0),
|
widget.sharedFromGallery ? null : Colors.white.withAlpha(0),
|
||||||
|
|
@ -553,11 +538,11 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
getMergedImage: getMergedImage,
|
getMergedImage: getMergedImage,
|
||||||
mediaUploadId: mediaUploadId,
|
mediaUploadId: mediaUploadId,
|
||||||
videoFilePath: widget.videoFilePath,
|
videoFilePath: widget.videoFilePath,
|
||||||
sendNextMediaToUserName: sendNextMediaToUserName,
|
displayButtonLabel: widget.sendTo == null,
|
||||||
isLoading: loadingImage,
|
isLoading: loadingImage,
|
||||||
),
|
),
|
||||||
if (sendNextMediaToUserName != null) SizedBox(width: 10),
|
if (widget.sendTo != null) SizedBox(width: 10),
|
||||||
if (sendNextMediaToUserName != null)
|
if (widget.sendTo != null)
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
iconColor: Theme.of(context).colorScheme.primary,
|
iconColor: Theme.of(context).colorScheme.primary,
|
||||||
|
|
@ -566,7 +551,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
onPressed: pushShareImageView,
|
onPressed: pushShareImageView,
|
||||||
child: FaIcon(FontAwesomeIcons.userPlus),
|
child: FaIcon(FontAwesomeIcons.userPlus),
|
||||||
),
|
),
|
||||||
SizedBox(width: sendNextMediaToUserName == null ? 20 : 10),
|
SizedBox(width: widget.sendTo == null ? 20 : 10),
|
||||||
FilledButton.icon(
|
FilledButton.icon(
|
||||||
icon: sendingOrLoadingImage
|
icon: sendingOrLoadingImage
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
|
|
@ -589,9 +574,9 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
label: Text(
|
label: Text(
|
||||||
(sendNextMediaToUserName == null)
|
(widget.sendTo == null)
|
||||||
? context.lang.shareImagedEditorShareWith
|
? context.lang.shareImagedEditorShareWith
|
||||||
: sendNextMediaToUserName!,
|
: getContactDisplayName(widget.sendTo!),
|
||||||
style: TextStyle(fontSize: 17),
|
style: TextStyle(fontSize: 17),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue