twonly-app/lib/src/services/intent/links.intent.dart
otsmr 230809290a
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
fixes #340 and continue with #333
2025-12-27 15:09:36 +01:00

180 lines
5.2 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart';
import 'package:flutter_sharing_intent/model/sharing_file.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
import 'package:twonly/src/services/signal/session.signal.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
import 'package:twonly/src/views/chats/add_new_user.view.dart';
import 'package:twonly/src/views/components/alert_dialog.dart';
import 'package:twonly/src/views/contact/contact.view.dart';
import 'package:twonly/src/views/public_profile.view.dart';
Future<void> handleIntentUrl(BuildContext context, Uri uri) async {
if (!uri.scheme.startsWith('http')) return;
if (uri.host != 'me.twonly.eu') return;
if (uri.hasEmptyPath) return;
final publicKey = uri.hasFragment ? uri.fragment : null;
final userPaths = uri.path.split('/');
if (userPaths.length != 2) return;
final username = userPaths[1];
if (!context.mounted) return;
if (username == gUser.username) {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return const PublicProfileView();
},
),
);
return;
}
Log.info(
'Opened via deep link!: username = $username public_key = ${uri.fragment}',
);
final contacts = await twonlyDB.contactsDao.getContactsByUsername(username);
if (contacts.isEmpty) {
if (!context.mounted) return;
Uint8List? publicKeyBytes;
if (publicKey != null) {
publicKeyBytes = base64Url.decode(publicKey);
}
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return AddNewUserView(
username: username,
publicKey: publicKeyBytes,
);
},
),
);
} else if (publicKey != null) {
try {
final contact = contacts.first;
final storedPublicKey = await getPublicKeyFromContact(contact.userId);
final receivedPublicKey = base64Url.decode(publicKey);
if (storedPublicKey == null ||
receivedPublicKey.isEmpty ||
!context.mounted) {
return;
}
if (storedPublicKey.equals(receivedPublicKey)) {
if (!contact.verified) {
final markAsVerified = await showAlertDialog(
context,
context.lang.linkFromUsername(contact.username),
context.lang.linkFromUsernameLong,
customOk: context.lang.gotLinkFromFriend,
);
if (markAsVerified) {
await twonlyDB.contactsDao.updateContact(
contact.userId,
const ContactsCompanion(
verified: Value(true),
),
);
}
} else {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ContactView(contact.userId);
},
),
);
}
} else {
await showAlertDialog(
context,
context.lang.couldNotVerifyUsername(contact.username),
context.lang.linkPubkeyDoesNotMatch,
customCancel: '',
);
}
} catch (e) {
Log.warn(e);
}
}
}
Future<void> handleIntentMediaFile(
BuildContext context,
String filePath,
MediaType type,
) async {
final file = File(filePath);
if (!file.existsSync()) {
Log.error('The shared intent file does not exits.');
return;
}
final newMediaService = await initializeMediaUpload(
type,
gUser.defaultShowTime,
);
if (newMediaService == null) {
Log.error('Could not create new media file for intent shared file');
return;
}
file.copySync(newMediaService.originalPath.path);
if (!context.mounted) return;
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ShareImageEditorView(
mediaFileService: newMediaService,
sharedFromGallery: true,
),
),
);
}
Future<void> handleIntentSharedFile(
BuildContext context,
List<SharedFile> files,
) async {
for (final file in files) {
if (file.value == null) {
Log.error(
'Got shared media, but value is empty: getMediaStream ${file.mimeType}',
);
continue;
}
Log.info('got file via intent ${file.type} ${file.value}');
switch (file.type) {
case SharedMediaType.URL:
await handleIntentUrl(context, Uri.parse(file.value!));
case SharedMediaType.IMAGE:
var type = MediaType.image;
if (file.value!.endsWith('.gif')) {
type = MediaType.gif;
}
await handleIntentMediaFile(context, file.value!, type);
case SharedMediaType.VIDEO:
await handleIntentMediaFile(context, file.value!, MediaType.video);
// ignore: no_default_cases
default:
}
break; // only handle one file...
}
}