mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
fixing multiple issues
This commit is contained in:
parent
e9fd8726c9
commit
a71eb1fe94
7 changed files with 120 additions and 110 deletions
|
|
@ -33,7 +33,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "eu.twonly.testing"
|
||||
applicationId = "eu.twonly"
|
||||
multiDexEnabled true
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
|
|
@ -53,7 +53,7 @@ android {
|
|||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
applicationIdSuffix ".testing"
|
||||
}
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
|
|
|
|||
|
|
@ -497,11 +497,18 @@ class ApiService {
|
|||
return sendRequestSync(req);
|
||||
}
|
||||
|
||||
Future<Result> getUserData(String username) async {
|
||||
Future<Response_UserData?> getUserData(String username) async {
|
||||
final get = ApplicationData_GetUserByUsername()..username = username;
|
||||
final appData = ApplicationData()..getuserbyusername = get;
|
||||
final req = createClientToServerFromApplicationData(appData);
|
||||
return sendRequestSync(req);
|
||||
final res = await sendRequestSync(req);
|
||||
if (res.isSuccess) {
|
||||
final ok = res.value as server.Response_Ok;
|
||||
if (ok.hasUserdata()) {
|
||||
return ok.userdata;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Response_PlanBallance?> getPlanBallance() async {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
|||
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
|
|
@ -20,11 +21,8 @@ import 'package:twonly/src/utils/log.dart';
|
|||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
|
||||
Map<int, DateTime> downloadStartedForMediaReceived = {};
|
||||
|
||||
Future<void> tryDownloadAllMediaFiles({bool force = false}) async {
|
||||
// This is called when WebSocket is newly connected, so allow all downloads to be restarted.
|
||||
downloadStartedForMediaReceived = {};
|
||||
final messages =
|
||||
await twonlyDB.messagesDao.getAllMessagesPendingDownloading();
|
||||
|
||||
|
|
@ -102,13 +100,15 @@ Future<void> handleDownloadStatusUpdate(TaskStatusUpdate update) async {
|
|||
}
|
||||
|
||||
Future<void> handleDownloadStatusUpdateInternal(
|
||||
int messageId, bool failed) async {
|
||||
int messageId,
|
||||
bool failed,
|
||||
) async {
|
||||
if (failed) {
|
||||
Log.error('Download failed for $messageId');
|
||||
final message = await twonlyDB.messagesDao
|
||||
.getMessageByMessageId(messageId)
|
||||
.getSingleOrNull();
|
||||
if (message != null) {
|
||||
if (message != null && message.downloadState != DownloadState.downloaded) {
|
||||
await handleMediaError(message);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -117,18 +117,10 @@ Future<void> handleDownloadStatusUpdateInternal(
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> startDownloadMedia(Message message, bool force,
|
||||
{int retryCounter = 0}) async {
|
||||
Mutex protectDownload = Mutex();
|
||||
|
||||
Future<void> startDownloadMedia(Message message, bool force) async {
|
||||
if (message.contentJson == null) return;
|
||||
if (downloadStartedForMediaReceived[message.messageId] != null &&
|
||||
retryCounter == 0) {
|
||||
final started = downloadStartedForMediaReceived[message.messageId]!;
|
||||
final elapsed = DateTime.now().difference(started);
|
||||
if (elapsed <= const Duration(seconds: 60)) {
|
||||
Log.error('Download already started...');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final content = MessageContent.fromJson(
|
||||
message.kind, jsonDecode(message.contentJson!) as Map);
|
||||
|
|
@ -157,16 +149,33 @@ Future<void> startDownloadMedia(Message message, bool force,
|
|||
return;
|
||||
}
|
||||
|
||||
if (message.downloadState != DownloadState.downloaded) {
|
||||
final isBlocked = await protectDownload.protect<bool>(() async {
|
||||
final msg = await twonlyDB.messagesDao
|
||||
.getMessageByMessageId(message.messageId)
|
||||
.getSingleOrNull();
|
||||
|
||||
if (msg == null) return true;
|
||||
|
||||
if (msg.downloadState != DownloadState.pending) {
|
||||
Log.error(
|
||||
'${message.messageId} is already downloaded or is downloading.');
|
||||
return true;
|
||||
}
|
||||
|
||||
await twonlyDB.messagesDao.updateMessageByMessageId(
|
||||
message.messageId,
|
||||
const MessagesCompanion(
|
||||
downloadState: Value(DownloadState.downloading),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
downloadStartedForMediaReceived[message.messageId] = DateTime.now();
|
||||
return false;
|
||||
});
|
||||
|
||||
if (isBlocked) {
|
||||
Log.info('Download for ${message.messageId} already started.');
|
||||
return;
|
||||
}
|
||||
|
||||
final downloadToken = uint8ListToHex(content.downloadToken!);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,14 @@ class CameraSendToViewState extends State<CameraSendToView> {
|
|||
return cameraController;
|
||||
}
|
||||
|
||||
/// same function also in home.view.dart
|
||||
Future<void> toggleSelectedCamera() async {
|
||||
await cameraController?.dispose();
|
||||
if (cameraController == null) return;
|
||||
// do not allow switching camera when recording
|
||||
if (cameraController!.value.isRecordingVideo == true) {
|
||||
return;
|
||||
}
|
||||
await cameraController!.dispose();
|
||||
cameraController = null;
|
||||
await selectCamera((selectedCameraDetails.cameraId + 1) % 2, false, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class _LocationFilterState extends State<LocationFilter> {
|
|||
for (final item in imageIndex) {
|
||||
if (item.imageSrc.contains('/cities/$normalizedCountry/')) {
|
||||
// Check if the item matches the normalized city
|
||||
if (item.imageSrc.endsWith('$normalizedCity.png')) {
|
||||
if (item.imageSrc.contains('$normalizedCity.')) {
|
||||
if (item.imageSrc.startsWith('/api/')) {
|
||||
_imageUrl = 'https://twonly.eu/${item.imageSrc}';
|
||||
if (mounted) setState(() {});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// ignore_for_file: avoid_dynamic_calls
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart' hide Column;
|
||||
|
|
@ -11,7 +9,6 @@ import 'package:twonly/src/database/daos/contacts_dao.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/websocket/server_to_client.pb.dart';
|
||||
import 'package:twonly/src/model/protobuf/push_notification/push_notification.pbserver.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/api/utils.dart';
|
||||
|
|
@ -41,7 +38,11 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initStreams();
|
||||
contactsStream = twonlyDB.contactsDao
|
||||
.watchNotAcceptedContacts()
|
||||
.listen((update) => setState(() {
|
||||
contacts = update;
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -50,18 +51,9 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
void initStreams() {
|
||||
contactsStream =
|
||||
twonlyDB.contactsDao.watchNotAcceptedContacts().listen((update) {
|
||||
setState(() {
|
||||
contacts = update;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _addNewUser(BuildContext context) async {
|
||||
final user = await getUser();
|
||||
if (user == null || user.username == searchUserName.text) {
|
||||
if (user == null || user.username == searchUserName.text || !mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -69,40 +61,46 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
_isLoading = true;
|
||||
});
|
||||
|
||||
final res = await apiService.getUserData(searchUserName.text);
|
||||
final userdata = await apiService.getUserData(searchUserName.text);
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.isSuccess) {
|
||||
final addUser = await showAlertDialog(
|
||||
context, context.lang.userFound, context.lang.userFoundBody);
|
||||
if (!addUser || !context.mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
|
||||
if (userdata == null) {
|
||||
await showAlertDialog(context, context.lang.searchUsernameNotFound,
|
||||
context.lang.searchUsernameNotFoundBody(searchUserName.text));
|
||||
return;
|
||||
}
|
||||
|
||||
final addUser = await showAlertDialog(
|
||||
context,
|
||||
context.lang.userFound,
|
||||
context.lang.userFoundBody,
|
||||
);
|
||||
|
||||
if (!addUser || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final added = await twonlyDB.contactsDao.insertContact(
|
||||
ContactsCompanion(
|
||||
username: Value(searchUserName.text),
|
||||
userId: Value(res.value.userdata.userId.toInt() as int),
|
||||
userId: Value(userdata.userId.toInt()),
|
||||
requested: const Value(false),
|
||||
),
|
||||
);
|
||||
|
||||
if (added > 0) {
|
||||
if (await createNewSignalSession(
|
||||
res.value.userdata as Response_UserData)) {
|
||||
if (await createNewSignalSession(userdata)) {
|
||||
// before notifying the other party, add
|
||||
await setupNotificationWithUsers(
|
||||
forceContact: res.value.userdata.userId.toInt() as int,
|
||||
forceContact: userdata.userId.toInt(),
|
||||
);
|
||||
await encryptAndSendMessageAsync(
|
||||
null,
|
||||
res.value.userdata.userId.toInt() as int,
|
||||
userdata.userId.toInt(),
|
||||
MessageJson(
|
||||
kind: MessageKind.contactRequest,
|
||||
timestamp: DateTime.now(),
|
||||
|
|
@ -112,27 +110,18 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await showAlertDialog(context, context.lang.searchUsernameNotFound,
|
||||
context.lang.searchUsernameNotFoundBody(searchUserName.text));
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
InputDecoration getInputDecoration(String hintText) {
|
||||
final primaryColor =
|
||||
Theme.of(context).colorScheme.primary; // Get the primary color
|
||||
return InputDecoration(
|
||||
hintText: hintText,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
borderSide: BorderSide(color: primaryColor),
|
||||
borderSide: BorderSide(color: context.color.primary),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Theme.of(context).colorScheme.outline),
|
||||
borderSide: BorderSide(color: context.color.outline),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 15, horizontal: 20),
|
||||
);
|
||||
|
|
@ -146,8 +135,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(bottom: 20, left: 10, top: 20, right: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
|
|
@ -186,10 +174,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
floatingActionButton: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: FloatingActionButton(
|
||||
foregroundColor: Colors.white,
|
||||
onPressed: () {
|
||||
if (!_isLoading) _addNewUser(context);
|
||||
},
|
||||
onPressed: _isLoading ? null : () async => _addNewUser(context),
|
||||
child: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: const FaIcon(FontAwesomeIcons.magnifyingGlassPlus),
|
||||
|
|
@ -199,17 +184,11 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
|||
}
|
||||
}
|
||||
|
||||
class ContactsListView extends StatefulWidget {
|
||||
class ContactsListView extends StatelessWidget {
|
||||
const ContactsListView(this.contacts, {super.key});
|
||||
|
||||
final List<Contact> contacts;
|
||||
|
||||
@override
|
||||
State<ContactsListView> createState() => _ContactsListViewState();
|
||||
}
|
||||
|
||||
class _ContactsListViewState extends State<ContactsListView> {
|
||||
List<Widget> sendRequestActions(Contact contact) {
|
||||
List<Widget> sendRequestActions(BuildContext context, Contact contact) {
|
||||
return [
|
||||
Tooltip(
|
||||
message: context.lang.searchUserNameArchiveUserTooltip,
|
||||
|
|
@ -225,7 +204,7 @@ class _ContactsListViewState extends State<ContactsListView> {
|
|||
];
|
||||
}
|
||||
|
||||
List<Widget> requestedActions(Contact contact) {
|
||||
List<Widget> requestedActions(BuildContext context, Contact contact) {
|
||||
return [
|
||||
Tooltip(
|
||||
message: context.lang.searchUserNameBlockUserTooltip,
|
||||
|
|
@ -272,9 +251,9 @@ class _ContactsListViewState extends State<ContactsListView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
itemCount: widget.contacts.length,
|
||||
itemCount: contacts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final contact = widget.contacts[index];
|
||||
final contact = contacts[index];
|
||||
final displayName = getContactDisplayName(contact);
|
||||
return ListTile(
|
||||
title: Text(displayName),
|
||||
|
|
@ -282,8 +261,8 @@ class _ContactsListViewState extends State<ContactsListView> {
|
|||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: contact.requested
|
||||
? requestedActions(contact)
|
||||
: sendRequestActions(contact),
|
||||
? requestedActions(context, contact)
|
||||
: sendRequestActions(context, contact),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -112,7 +112,10 @@ class HomeViewState extends State<HomeView> {
|
|||
}
|
||||
|
||||
Future<CameraController?> selectCamera(
|
||||
int sCameraId, bool init, bool enableAudio) async {
|
||||
int sCameraId,
|
||||
bool init,
|
||||
bool enableAudio,
|
||||
) async {
|
||||
final opts = await initializeCameraController(
|
||||
selectedCameraDetails, sCameraId, init, enableAudio);
|
||||
if (opts != null) {
|
||||
|
|
@ -124,8 +127,14 @@ class HomeViewState extends State<HomeView> {
|
|||
return cameraController;
|
||||
}
|
||||
|
||||
/// same function also in camera_send_to_view
|
||||
Future<void> toggleSelectedCamera() async {
|
||||
await cameraController?.dispose();
|
||||
if (cameraController == null) return;
|
||||
// do not allow switching camera when recording
|
||||
if (cameraController!.value.isRecordingVideo == true) {
|
||||
return;
|
||||
}
|
||||
await cameraController!.dispose();
|
||||
cameraController = null;
|
||||
await selectCamera((selectedCameraDetails.cameraId + 1) % 2, false, false);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue