mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-18 14:22:53 +00:00
new: screen lock
This commit is contained in:
parent
f419b3709d
commit
391646d243
15 changed files with 257 additions and 15 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## 0.1.4
|
||||
|
||||
- New: Screen lock for twonly (Can be enabled in the settings.)
|
||||
- Fix: Several minor issues with the user interface
|
||||
|
||||
## 0.1.3
|
||||
|
|
|
|||
30
lib/app.dart
30
lib/app.dart
|
|
@ -19,6 +19,7 @@ import 'package:twonly/src/views/home.view.dart';
|
|||
import 'package:twonly/src/views/onboarding/onboarding.view.dart';
|
||||
import 'package:twonly/src/views/onboarding/register.view.dart';
|
||||
import 'package:twonly/src/views/settings/backup/setup_backup.view.dart';
|
||||
import 'package:twonly/src/views/unlock_twonly.view.dart';
|
||||
|
||||
class App extends StatefulWidget {
|
||||
const App({super.key});
|
||||
|
|
@ -36,9 +37,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
globalCallbackConnectionState = ({required isConnected}) async {
|
||||
await context
|
||||
.read<CustomChangeProvider>()
|
||||
.updateConnectionState(isConnected);
|
||||
await context.read<CustomChangeProvider>().updateConnectionState(
|
||||
isConnected,
|
||||
);
|
||||
await setUserPlan();
|
||||
};
|
||||
|
||||
|
|
@ -54,8 +55,8 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||
if (user != null && mounted) {
|
||||
if (mounted) {
|
||||
context.read<PurchasesProvider>().updatePlan(
|
||||
planFromString(user.subscriptionPlan),
|
||||
);
|
||||
planFromString(user.subscriptionPlan),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,6 +135,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
|||
bool _showOnboarding = true;
|
||||
bool _isLoaded = false;
|
||||
bool _skipBackup = false;
|
||||
bool _isTwonlyLocked = true;
|
||||
int _initialPage = 0;
|
||||
|
||||
(Future<int>?, bool) _proofOfWork = (null, false);
|
||||
|
|
@ -149,6 +151,10 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
|||
_isUserCreated = await isUserCreated();
|
||||
|
||||
if (_isUserCreated) {
|
||||
if (_isTwonlyLocked) {
|
||||
// do not change in case twonly was already unlocked at some point
|
||||
_isTwonlyLocked = gUser.screenLockEnabled;
|
||||
}
|
||||
if (gUser.appVersion < 62) {
|
||||
_showDatabaseMigration = true;
|
||||
}
|
||||
|
|
@ -164,8 +170,10 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
|||
if (proof != null) {
|
||||
Log.info('Starting with proof of work calculation.');
|
||||
// Starting with the proof of work.
|
||||
_proofOfWork =
|
||||
(calculatePoW(proof.prefix, proof.difficulty.toInt()), false);
|
||||
_proofOfWork = (
|
||||
calculatePoW(proof.prefix, proof.difficulty.toInt()),
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
_proofOfWork = (null, disabled);
|
||||
}
|
||||
|
|
@ -187,7 +195,13 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
|||
if (_showDatabaseMigration) {
|
||||
child = const Center(child: Text('Please reinstall twonly.'));
|
||||
} else if (_isUserCreated) {
|
||||
if (gUser.twonlySafeBackup == null && !_skipBackup) {
|
||||
if (_isTwonlyLocked) {
|
||||
child = UnlockTwonlyView(
|
||||
callbackOnSuccess: () => setState(() {
|
||||
_isTwonlyLocked = false;
|
||||
}),
|
||||
);
|
||||
} else if (gUser.twonlySafeBackup == null && !_skipBackup) {
|
||||
child = SetupBackupView(
|
||||
callBack: () {
|
||||
_skipBackup = true;
|
||||
|
|
|
|||
|
|
@ -3087,6 +3087,48 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Your QR code'**
|
||||
String get profileYourQrCode;
|
||||
|
||||
/// No description provided for @settingsScreenLock.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Screen lock'**
|
||||
String get settingsScreenLock;
|
||||
|
||||
/// No description provided for @settingsScreenLockSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'To open twonly, you\'ll need to use your smartphone\'s unlock feature.'**
|
||||
String get settingsScreenLockSubtitle;
|
||||
|
||||
/// No description provided for @settingsScreenLockAuthMessageEnable.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Use the screen lock from twonly.'**
|
||||
String get settingsScreenLockAuthMessageEnable;
|
||||
|
||||
/// No description provided for @settingsScreenLockAuthMessageDisable.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Disable the screen lock from twonly.'**
|
||||
String get settingsScreenLockAuthMessageDisable;
|
||||
|
||||
/// No description provided for @unlockTwonly.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unlock twonly'**
|
||||
String get unlockTwonly;
|
||||
|
||||
/// No description provided for @unlockTwonlyTryAgain.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Try again'**
|
||||
String get unlockTwonlyTryAgain;
|
||||
|
||||
/// No description provided for @unlockTwonlyDesc.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Use your phone\'s unlock settings to unlock twonly'**
|
||||
String get unlockTwonlyDesc;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1729,4 +1729,29 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get profileYourQrCode => 'Dein QR-Code';
|
||||
|
||||
@override
|
||||
String get settingsScreenLock => 'Bildschirmsperre';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockSubtitle =>
|
||||
'Um twonly zu öffnen, wird die Entsperrfunktion deines Smartphones verwenden.';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockAuthMessageEnable =>
|
||||
'Bildschirmsperre von twonly verwenden';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockAuthMessageDisable =>
|
||||
'Bildschirmsperre von twonly deaktivieren.';
|
||||
|
||||
@override
|
||||
String get unlockTwonly => 'twonly entsperren';
|
||||
|
||||
@override
|
||||
String get unlockTwonlyTryAgain => 'Erneut versuchen';
|
||||
|
||||
@override
|
||||
String get unlockTwonlyDesc =>
|
||||
'Entsperre twonly über die Sperreinstellungen deines Handys';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1717,4 +1717,29 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get profileYourQrCode => 'Your QR code';
|
||||
|
||||
@override
|
||||
String get settingsScreenLock => 'Screen lock';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockSubtitle =>
|
||||
'To open twonly, you\'ll need to use your smartphone\'s unlock feature.';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockAuthMessageEnable =>
|
||||
'Use the screen lock from twonly.';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockAuthMessageDisable =>
|
||||
'Disable the screen lock from twonly.';
|
||||
|
||||
@override
|
||||
String get unlockTwonly => 'Unlock twonly';
|
||||
|
||||
@override
|
||||
String get unlockTwonlyTryAgain => 'Try again';
|
||||
|
||||
@override
|
||||
String get unlockTwonlyDesc =>
|
||||
'Use your phone\'s unlock settings to unlock twonly';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1717,4 +1717,29 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get profileYourQrCode => 'Your QR code';
|
||||
|
||||
@override
|
||||
String get settingsScreenLock => 'Screen lock';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockSubtitle =>
|
||||
'To open twonly, you\'ll need to use your smartphone\'s unlock feature.';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockAuthMessageEnable =>
|
||||
'Use the screen lock from twonly.';
|
||||
|
||||
@override
|
||||
String get settingsScreenLockAuthMessageDisable =>
|
||||
'Disable the screen lock from twonly.';
|
||||
|
||||
@override
|
||||
String get unlockTwonly => 'Unlock twonly';
|
||||
|
||||
@override
|
||||
String get unlockTwonlyTryAgain => 'Try again';
|
||||
|
||||
@override
|
||||
String get unlockTwonlyDesc =>
|
||||
'Use your phone\'s unlock settings to unlock twonly';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ class UserData {
|
|||
@JsonKey(defaultValue: false)
|
||||
bool allowErrorTrackingViaSentry = false;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
bool screenLockEnabled = false;
|
||||
|
||||
// -- Custom DATA --
|
||||
|
||||
@JsonKey(defaultValue: 100_000)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
|||
..requestedAudioPermission =
|
||||
json['requestedAudioPermission'] as bool? ?? false
|
||||
..videoStabilizationEnabled =
|
||||
json['videoStabilizationEnabled'] as bool? ?? false
|
||||
json['videoStabilizationEnabled'] as bool? ?? true
|
||||
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
|
||||
..showShowImagePreviewWhenSending =
|
||||
json['showShowImagePreviewWhenSending'] as bool? ?? false
|
||||
|
|
@ -62,6 +62,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
|||
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
|
||||
..allowErrorTrackingViaSentry =
|
||||
json['allowErrorTrackingViaSentry'] as bool? ?? false
|
||||
..screenLockEnabled = json['screenLockEnabled'] as bool? ?? false
|
||||
..currentPreKeyIndexStart =
|
||||
(json['currentPreKeyIndexStart'] as num?)?.toInt() ?? 100000
|
||||
..currentSignedPreKeyIndexStart =
|
||||
|
|
@ -123,6 +124,7 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
|||
'signalLastSignedPreKeyUpdated': instance.signalLastSignedPreKeyUpdated
|
||||
?.toIso8601String(),
|
||||
'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry,
|
||||
'screenLockEnabled': instance.screenLockEnabled,
|
||||
'currentPreKeyIndexStart': instance.currentPreKeyIndexStart,
|
||||
'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart,
|
||||
'lastChangeLogHash': instance.lastChangeLogHash,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
|
@ -14,13 +15,13 @@ import 'package:twonly/src/model/memory_item.model.dart';
|
|||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/blink.component.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/chat_group_action.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/chat_list_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_date_chip.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_input.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/blink.component.dart';
|
||||
import 'package:twonly/src/views/components/flame.dart';
|
||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_con
|
|||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_media_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_text_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_unkown.entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_unknown.entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/common.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_reply_drag.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_reply_drag.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class _SlidingResponseWidgetState extends State<MessageReplyDrag> {
|
|||
_animatedScale = 1.3;
|
||||
});
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 50));
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
|||
hasLoader = true;
|
||||
}
|
||||
|
||||
if (message.mediaStored) {
|
||||
if (message.mediaStored && message.openedAt != null) {
|
||||
icon = FaIcon(FontAwesomeIcons.floppyDisk, size: 12, color: color);
|
||||
text = context.lang.messageStoredInGallery;
|
||||
}
|
||||
|
|
@ -275,7 +275,6 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
|||
),
|
||||
];
|
||||
}
|
||||
// Log.info("DISPLAY REACTION");
|
||||
}
|
||||
}
|
||||
if (widget.group != null &&
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
||||
class PrivacyView extends StatefulWidget {
|
||||
const PrivacyView({super.key});
|
||||
|
|
@ -17,6 +18,20 @@ class _PrivacyViewState extends State<PrivacyView> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
Future<void> toggleAuthRequirementOnStartup() async {
|
||||
final isAuth = await authenticateUser(
|
||||
gUser.screenLockEnabled
|
||||
? context.lang.settingsScreenLockAuthMessageDisable
|
||||
: context.lang.settingsScreenLockAuthMessageEnable,
|
||||
);
|
||||
if (!isAuth) return;
|
||||
await updateUserdata((u) {
|
||||
u.screenLockEnabled = !u.screenLockEnabled;
|
||||
return u;
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
@ -43,6 +58,15 @@ class _PrivacyViewState extends State<PrivacyView> {
|
|||
),
|
||||
onTap: () => context.push(Routes.settingsPrivacyBlockUsers),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsScreenLock),
|
||||
subtitle: Text(context.lang.settingsScreenLockSubtitle),
|
||||
onTap: toggleAuthRequirementOnStartup,
|
||||
trailing: Switch(
|
||||
value: gUser.screenLockEnabled,
|
||||
onChanged: (a) => toggleAuthRequirementOnStartup(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.lang.contactVerifyNumberTitle),
|
||||
onTap: () async {
|
||||
|
|
|
|||
81
lib/src/views/unlock_twonly.view.dart
Normal file
81
lib/src/views/unlock_twonly.view.dart
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
class UnlockTwonlyView extends StatefulWidget {
|
||||
const UnlockTwonlyView({required this.callbackOnSuccess, super.key});
|
||||
|
||||
final void Function() callbackOnSuccess;
|
||||
|
||||
@override
|
||||
State<UnlockTwonlyView> createState() => _UnlockTwonlyViewState();
|
||||
}
|
||||
|
||||
class _UnlockTwonlyViewState extends State<UnlockTwonlyView> {
|
||||
Future<void> _unlockTwonly() async {
|
||||
final isAuth = await authenticateUser(context.lang.unlockTwonly);
|
||||
if (isAuth) {
|
||||
widget.callbackOnSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// _unlockTwonly();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Spacer(),
|
||||
|
||||
const Icon(
|
||||
FontAwesomeIcons.lock,
|
||||
size: 40,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
Text(
|
||||
context.lang.unlockTwonly,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
|
||||
const Spacer(),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(30),
|
||||
child: Text(
|
||||
context.lang.unlockTwonlyDesc,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Center(
|
||||
child: FilledButton(
|
||||
onPressed: _unlockTwonly,
|
||||
child: Text(context.lang.unlockTwonlyTryAgain),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue