aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2023-05-22 09:00:45 -0600
committerIván Ávalos <avalos@disroot.org>2023-05-22 09:00:45 -0600
commitf0e45a5a510d2b4693580179645bd0bb0b352f86 (patch)
tree1de8c7235e5dba68898a1ac33f5d420a18ac2fff
parent786267d0ebb337ad5b4f1e528fdd4c23731e0606 (diff)
downloadlinkchat-f0e45a5a510d2b4693580179645bd0bb0b352f86.tar.gz
linkchat-f0e45a5a510d2b4693580179645bd0bb0b352f86.tar.bz2
linkchat-f0e45a5a510d2b4693580179645bd0bb0b352f86.zip
Soporte y restricción para links
-rw-r--r--android/app/src/main/AndroidManifest.xml8
-rw-r--r--lib/firebase/database.dart6
-rw-r--r--lib/models/message.dart15
-rw-r--r--lib/screens/chat_screen.dart26
-rw-r--r--lib/widgets/chat_bottom_sheet.dart10
-rw-r--r--lib/widgets/chat_bubble.dart105
-rw-r--r--lib/widgets/recent_chats.dart3
-rw-r--r--linux/flutter/generated_plugin_registrant.cc4
-rw-r--r--linux/flutter/generated_plugins.cmake1
-rw-r--r--pubspec.lock128
-rw-r--r--pubspec.yaml2
11 files changed, 273 insertions, 35 deletions
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index e9957a0..be5d92b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -30,4 +30,12 @@
android:name="flutterEmbedding"
android:value="2" />
</application>
+
+ <queries>
+ <intent>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="https" />
+ </intent>
+ </queries>
</manifest>
diff --git a/lib/firebase/database.dart b/lib/firebase/database.dart
index eb9ef05..46970ce 100644
--- a/lib/firebase/database.dart
+++ b/lib/firebase/database.dart
@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:linkchat/models/group.dart';
import 'package:linkchat/models/message.dart';
+import 'package:simple_link_preview/simple_link_preview.dart';
import '../models/user.dart';
@@ -58,13 +59,14 @@ class Database {
}
Future<void> saveMessage(Message msg, String groupId) async {
+ LinkPreview? preview = await SimpleLinkPreview.getPreview(msg.messageText);
await _firestore
.collection('messages')
.doc(groupId)
.collection('messages')
- .add(msg.toMap());
+ .add(msg.toMap(preview: preview));
await _firestore.collection('groups').doc(groupId).update({
- "recentMessage": msg.toMap(),
+ "recentMessage": msg.toMap(preview: preview),
});
}
diff --git a/lib/models/message.dart b/lib/models/message.dart
index ac31c18..4026215 100644
--- a/lib/models/message.dart
+++ b/lib/models/message.dart
@@ -1,19 +1,29 @@
import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:simple_link_preview/simple_link_preview.dart';
class Message {
final String messageText;
+ final String? linkTitle;
+ final String? linkDescription;
+ final String? linkPhotoURL;
final DateTime sentAt;
final String sentBy;
const Message({
required this.messageText,
+ this.linkTitle,
+ this.linkDescription,
+ this.linkPhotoURL,
required this.sentAt,
required this.sentBy,
});
- Map<String, dynamic> toMap() {
+ Map<String, dynamic> toMap({LinkPreview? preview}) {
return {
"messageText": messageText,
+ "linkTitle": preview?.title,
+ "linkDescription": preview?.description,
+ "linkPhotoURL": preview?.image,
"sentAt": sentAt,
"sentBy": sentBy,
};
@@ -22,6 +32,9 @@ class Message {
factory Message.fromMap(Map<String, dynamic> map) {
return Message(
messageText: map['messageText'],
+ linkTitle: map['linkTitle'],
+ linkDescription: map['linkDescription'],
+ linkPhotoURL: map['linkPhotoURL'],
sentAt: (map['sentAt'] as Timestamp).toDate(),
sentBy: map['sentBy'],
);
diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart
index d67e469..d3d71ad 100644
--- a/lib/screens/chat_screen.dart
+++ b/lib/screens/chat_screen.dart
@@ -66,9 +66,10 @@ class _ChatScreenState extends State<ChatScreen> {
if (snapshot.hasData) {
List<Message> msgs = snapshot.data!;
return ListView.builder(
+ padding: const EdgeInsets.only(bottom: 80.0),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => ChatBubble(
- msgs[index].messageText,
+ msgs[index],
alignment: msgs[index].sentBy == _auth.currentUser?.uid
? ChatBubbleAlignment.end
: ChatBubbleAlignment.start,
@@ -83,13 +84,24 @@ class _ChatScreenState extends State<ChatScreen> {
),
bottomSheet: ChatBottomSheet(
onPressed: (value) {
- _db.saveMessage(
- Message(
- messageText: value,
- sentBy: _auth.currentUser!.uid,
- sentAt: DateTime.now(),
+ if (value.startsWith('https://') && Uri.tryParse(value) != null) {
+ _db.saveMessage(
+ Message(
+ messageText: value,
+ sentBy: _auth.currentUser!.uid,
+ sentAt: DateTime.now(),
+ ),
+ group!.id!);
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ // behavior: SnackBarBehavior.floating,
+ dismissDirection: DismissDirection.none,
+ content: Text('El mensaje debe ser un link.'),
+ duration: Duration(seconds: 1),
),
- group!.id!);
+ );
+ }
},
),
);
diff --git a/lib/widgets/chat_bottom_sheet.dart b/lib/widgets/chat_bottom_sheet.dart
index f1124cf..ac08499 100644
--- a/lib/widgets/chat_bottom_sheet.dart
+++ b/lib/widgets/chat_bottom_sheet.dart
@@ -8,16 +8,8 @@ class ChatBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return Container(
+ return SizedBox(
height: 55,
- decoration: BoxDecoration(boxShadow: [
- BoxShadow(
- color: Colors.grey.withOpacity(0.5),
- spreadRadius: 2,
- blurRadius: 10,
- offset: const Offset(0, 3),
- ),
- ]),
child: Row(
children: [
Padding(
diff --git a/lib/widgets/chat_bubble.dart b/lib/widgets/chat_bubble.dart
index aaac93d..7c2e16c 100644
--- a/lib/widgets/chat_bubble.dart
+++ b/lib/widgets/chat_bubble.dart
@@ -1,14 +1,16 @@
import 'package:custom_clippers/custom_clippers.dart';
import 'package:flutter/material.dart';
+import 'package:linkchat/models/message.dart';
+import 'package:url_launcher/link.dart';
enum ChatBubbleAlignment { start, end }
class ChatBubble extends StatelessWidget {
- final String text;
+ final Message message;
final ChatBubbleAlignment alignment;
const ChatBubble(
- this.text, {
+ this.message, {
super.key,
required this.alignment,
});
@@ -19,17 +21,17 @@ class ChatBubble extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
alignment == ChatBubbleAlignment.start
- ? BubbleLeft(text)
- : BubbleRight(text),
+ ? BubbleLeft(message)
+ : BubbleRight(message),
],
);
}
}
class BubbleLeft extends StatelessWidget {
- final String text;
+ final Message message;
- const BubbleLeft(this.text, {super.key});
+ const BubbleLeft(this.message, {super.key});
@override
Widget build(BuildContext context) {
@@ -42,9 +44,20 @@ class BubbleLeft extends StatelessWidget {
decoration: const BoxDecoration(
color: Color(0xFFE1E1E2),
),
- child: Text(
- text,
- style: const TextStyle(fontSize: 15, color: Colors.black),
+ child: Column(
+ children: [
+ LinkPreview(message),
+ Link(
+ uri: Uri.parse(message.messageText),
+ builder: (context, followLink) => TextButton(
+ onPressed: followLink,
+ child: Text(
+ message.messageText,
+ style: const TextStyle(color: Colors.blue),
+ ),
+ ),
+ ),
+ ],
),
),
),
@@ -53,9 +66,9 @@ class BubbleLeft extends StatelessWidget {
}
class BubbleRight extends StatelessWidget {
- final String text;
+ final Message message;
- const BubbleRight(this.text, {super.key});
+ const BubbleRight(this.message, {super.key});
@override
Widget build(BuildContext context) {
@@ -71,9 +84,21 @@ class BubbleRight extends StatelessWidget {
decoration: const BoxDecoration(
color: Color(0xFF113753),
),
- child: Text(
- text,
- style: const TextStyle(fontSize: 15, color: Colors.white),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LinkPreview(message),
+ Link(
+ uri: Uri.parse(message.messageText),
+ builder: (context, followLink) => TextButton(
+ onPressed: followLink,
+ child: Text(
+ message.messageText,
+ style: const TextStyle(color: Colors.blue),
+ ),
+ ),
+ ),
+ ],
),
),
),
@@ -81,3 +106,55 @@ class BubbleRight extends StatelessWidget {
);
}
}
+
+class LinkPreview extends StatelessWidget {
+ final Message message;
+ const LinkPreview(this.message, {super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return (message.linkTitle != null ||
+ message.linkDescription != null ||
+ message.linkPhotoURL != null)
+ ? Padding(
+ padding: const EdgeInsets.only(bottom: 8.0),
+ child: Card(
+ clipBehavior: Clip.antiAliasWithSaveLayer,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ message.linkPhotoURL != null
+ ? FadeInImage(
+ placeholder: const AssetImage('assets/loading.gif'),
+ image: NetworkImage(message.linkPhotoURL!),
+ imageErrorBuilder: (context, error, stackTrace) =>
+ const SizedBox.shrink(),
+ fit: BoxFit.fill,
+ )
+ : const SizedBox.shrink(),
+ message.linkTitle != null
+ ? Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Text(
+ message.linkTitle!,
+ style: Theme.of(context)
+ .textTheme
+ .bodyLarge!
+ .copyWith(fontWeight: FontWeight.bold),
+ ),
+ )
+ : const SizedBox.shrink(),
+ message.linkDescription != null
+ ? Padding(
+ padding:
+ const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
+ child: Text(message.linkDescription!),
+ )
+ : const SizedBox.shrink(),
+ ],
+ ),
+ ),
+ )
+ : const SizedBox.shrink();
+ }
+}
diff --git a/lib/widgets/recent_chats.dart b/lib/widgets/recent_chats.dart
index 02d4959..b83b5e5 100644
--- a/lib/widgets/recent_chats.dart
+++ b/lib/widgets/recent_chats.dart
@@ -35,11 +35,10 @@ class RecentChats extends StatelessWidget {
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
+ padding: const EdgeInsets.only(bottom: 100.0),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Group group = snapshot.data![index];
- print("Members: ${group.members}");
- print("User ID: ${_auth.currentUser!.uid}");
return FutureBuilder(
future: _db.getUserById(group.members.firstWhere(
(m) => m != _auth.currentUser!.uid,
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index e71a16d..f6f23bf 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
+#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
+ url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index 2e1de87..f16b4c3 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/pubspec.lock b/pubspec.lock
index 9039956..0e6372d 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -41,6 +41,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
clock:
dependency: transitive
description:
@@ -113,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
+ csslib:
+ dependency: transitive
+ description:
+ name: csslib
+ sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.17.2"
custom_clippers:
dependency: "direct main"
description:
@@ -280,6 +296,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ html:
+ dependency: transitive
+ description:
+ name: html
+ sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.15.3"
http:
dependency: transitive
description:
@@ -392,6 +416,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
+ metadata_fetch:
+ dependency: transitive
+ description:
+ name: metadata_fetch
+ sha256: "2c79e69e71cbb051041da6a8a9dd3df0617db84482ab3f36b4952ff779b7580e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.1"
nested:
dependency: transitive
description:
@@ -528,6 +560,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
+ simple_link_preview:
+ dependency: "direct main"
+ description:
+ name: simple_link_preview
+ sha256: "3c822908dc747e92e3c65ace0008dc465c98f16e795d622c9593f2cb3565fabc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.2"
sky_engine:
dependency: transitive
description: flutter
@@ -573,6 +613,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
+ string_validator:
+ dependency: transitive
+ description:
+ name: string_validator
+ sha256: "50dd8ecf91db6a732f4a851eeae81ee12406eedc62d0da72f2d91a04a2d10dd8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.0"
term_glyph:
dependency: transitive
description:
@@ -597,6 +645,86 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
+ universal_html:
+ dependency: transitive
+ description:
+ name: universal_html
+ sha256: f2e0ff0c4af8e4bbda4d273ca4a11be4055414f478fad5c161609b74790ff696
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.2"
+ universal_io:
+ dependency: transitive
+ description:
+ name: universal_io
+ sha256: "06866290206d196064fd61df4c7aea1ffe9a4e7c4ccaa8fcded42dd41948005d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ url_launcher:
+ dependency: "direct main"
+ description:
+ name: url_launcher
+ sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.11"
+ url_launcher_android:
+ dependency: transitive
+ description:
+ name: url_launcher_android
+ sha256: "1a5848f598acc5b7d8f7c18b8cb834ab667e59a13edc3c93e9d09cf38cc6bc87"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.34"
+ url_launcher_ios:
+ dependency: transitive
+ description:
+ name: url_launcher_ios
+ sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.4"
+ url_launcher_linux:
+ dependency: transitive
+ description:
+ name: url_launcher_linux
+ sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.5"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.5"
+ url_launcher_platform_interface:
+ dependency: transitive
+ description:
+ name: url_launcher_platform_interface
+ sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ url_launcher_web:
+ dependency: transitive
+ description:
+ name: url_launcher_web
+ sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.16"
+ url_launcher_windows:
+ dependency: transitive
+ description:
+ name: url_launcher_windows
+ sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.6"
vector_math:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 03c2aa2..8657ed6 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -24,6 +24,8 @@ dependencies:
firebase_storage: ^11.2.1
cloud_firestore: ^4.7.1
custom_clippers: ^2.0.0
+ simple_link_preview: ^1.0.2
+ url_launcher: ^6.1.11
intl: ^0.18.1
dev_dependencies: