diff --git a/Backend/Database/Init.go b/Backend/Database/Init.go
index 4481002..f4b6fb9 100644
--- a/Backend/Database/Init.go
+++ b/Backend/Database/Init.go
@@ -20,6 +20,7 @@ var DB *gorm.DB
func getModels() []interface{} {
return []interface{}{
&Models.Session{},
+ &Models.Attachment{},
&Models.User{},
&Models.FriendRequest{},
&Models.MessageData{},
diff --git a/Backend/Models/Attachments.go b/Backend/Models/Attachments.go
new file mode 100644
index 0000000..34304a7
--- /dev/null
+++ b/Backend/Models/Attachments.go
@@ -0,0 +1,8 @@
+package Models
+
+// Attachment holds the attachment data
+type Attachment struct {
+ Base
+ FilePath string `gorm:"not null" json:"-"`
+ Mimetype string `gorm:"not null" json:"mimetype"`
+}
diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist
index d3ba628..f2762fc 100644
--- a/mobile/ios/Runner/Info.plist
+++ b/mobile/ios/Runner/Info.plist
@@ -47,5 +47,11 @@
NSCameraUsageDescription
This app needs camera access to scan QR codes
+ NSPhotoLibraryUsageDescription
+ Upload images for screen background
+ NSCameraUsageDescription
+ Upload image from camera for screen background
+ NSMicrophoneUsageDescription
+ Post videos to profile
diff --git a/mobile/lib/components/file_picker.dart b/mobile/lib/components/file_picker.dart
new file mode 100644
index 0000000..6c56310
--- /dev/null
+++ b/mobile/lib/components/file_picker.dart
@@ -0,0 +1,97 @@
+import 'package:flutter/material.dart';
+import 'package:image_picker/image_picker.dart';
+
+class FilePicker extends StatelessWidget {
+ FilePicker({
+ Key? key,
+ this.cameraHandle,
+ this.galleryHandleSingle,
+ this.galleryHandleMultiple,
+ this.fileHandle,
+ }) : super(key: key);
+
+ final Function()? cameraHandle;
+ final Function()? galleryHandleSingle;
+ final Function(List images)? galleryHandleMultiple;
+ final Function()? fileHandle;
+
+ final ImagePicker _picker = ImagePicker();
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 10, left: 5, right: 5),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ _filePickerSelection(
+ hasHandle: cameraHandle != null,
+ icon: Icons.camera_alt,
+ onTap: () {
+ },
+ context: context,
+ ),
+ _filePickerSelection(
+ hasHandle: galleryHandleSingle != null,
+ icon: Icons.image,
+ onTap: () async {
+ final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
+ print(image);
+ },
+ context: context,
+ ),
+ _filePickerSelection(
+ hasHandle: galleryHandleMultiple != null,
+ icon: Icons.image,
+ onTap: () async {
+ final List? images = await _picker.pickMultiImage();
+ if (images == null) {
+ return;
+ }
+ galleryHandleMultiple!(images);
+ },
+ context: context,
+ ),
+ _filePickerSelection(
+ hasHandle: fileHandle != null,
+ icon: Icons.file_present_sharp,
+ onTap: () {
+ },
+ context: context,
+ ),
+ ],
+ )
+ );
+ }
+
+ Widget _filePickerSelection({
+ required bool hasHandle,
+ required IconData icon,
+ required Function() onTap,
+ required BuildContext context
+ }) {
+ if (!hasHandle) {
+ return const SizedBox.shrink();
+ }
+
+ return Padding(
+ padding: const EdgeInsets.only(left: 5, right: 5),
+ child: GestureDetector(
+ onTap: onTap,
+ child: Container(
+ height: 75,
+ width: 75,
+ decoration: BoxDecoration(
+ color: Theme.of(context).primaryColor,
+ borderRadius: BorderRadius.circular(25),
+ ),
+ child: Icon(
+ icon,
+ size: 40,
+ ),
+ ),
+ ),
+ );
+ }
+}
+
diff --git a/mobile/lib/models/conversations.dart b/mobile/lib/models/conversations.dart
index e7d760d..9aa7c33 100644
--- a/mobile/lib/models/conversations.dart
+++ b/mobile/lib/models/conversations.dart
@@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:typed_data';
import 'package:Envelope/models/messages.dart';
+import 'package:Envelope/models/text_messages.dart';
import 'package:pointycastle/export.dart';
import 'package:sqflite/sqflite.dart';
import 'package:uuid/uuid.dart';
@@ -348,11 +349,11 @@ class Conversation {
return null;
}
- return Message(
+ return TextMessage(
id: maps[0]['id'],
symmetricKey: maps[0]['symmetric_key'],
userSymmetricKey: maps[0]['user_symmetric_key'],
- data: maps[0]['data'],
+ text: maps[0]['data'],
senderId: maps[0]['sender_id'],
senderUsername: maps[0]['sender_username'],
associationKey: maps[0]['association_key'],
diff --git a/mobile/lib/models/image_message.dart b/mobile/lib/models/image_message.dart
new file mode 100644
index 0000000..d430e2d
--- /dev/null
+++ b/mobile/lib/models/image_message.dart
@@ -0,0 +1,131 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:pointycastle/pointycastle.dart';
+import 'package:uuid/uuid.dart';
+
+import '/models/conversations.dart';
+import '/models/messages.dart';
+import '/utils/encryption/aes_helper.dart';
+import '/utils/encryption/crypto_utils.dart';
+import '/utils/strings.dart';
+
+class ImageMessage extends Message {
+ String text;
+
+ ImageMessage({
+ id,
+ symmetricKey,
+ userSymmetricKey,
+ senderId,
+ senderUsername,
+ associationKey,
+ createdAt,
+ failedToSend,
+ required this.text,
+ }) : super(
+ id: id,
+ symmetricKey: symmetricKey,
+ userSymmetricKey: userSymmetricKey,
+ senderId: senderId,
+ senderUsername: senderUsername,
+ associationKey: associationKey,
+ createdAt: createdAt,
+ failedToSend: failedToSend,
+ );
+
+ factory ImageMessage.fromJson(Map json, RSAPrivateKey privKey) {
+ var userSymmetricKey = CryptoUtils.rsaDecrypt(
+ base64.decode(json['symmetric_key']),
+ privKey,
+ );
+
+ var symmetricKey = AesHelper.aesDecrypt(
+ userSymmetricKey,
+ base64.decode(json['message_data']['symmetric_key']),
+ );
+
+ var senderId = AesHelper.aesDecrypt(
+ base64.decode(symmetricKey),
+ base64.decode(json['message_data']['sender_id']),
+ );
+
+ var data = AesHelper.aesDecrypt(
+ base64.decode(symmetricKey),
+ base64.decode(json['message_data']['data']),
+ );
+
+ return ImageMessage(
+ id: json['id'],
+ symmetricKey: symmetricKey,
+ userSymmetricKey: base64.encode(userSymmetricKey),
+ senderId: senderId,
+ senderUsername: 'Unknown',
+ associationKey: json['association_key'],
+ createdAt: json['created_at'],
+ failedToSend: false,
+ text: data,
+ );
+ }
+
+ Map toMap() {
+ return {
+ 'id': id,
+ 'symmetric_key': symmetricKey,
+ 'user_symmetric_key': userSymmetricKey,
+ 'data': text,
+ 'sender_id': senderId,
+ 'sender_username': senderUsername,
+ 'association_key': associationKey,
+ 'created_at': createdAt,
+ 'failed_to_send': failedToSend ? 1 : 0,
+ };
+ }
+
+ Future