add support for sending media
Still needs camera support.
This commit is contained in:
parent
a8285548e8
commit
c4f63d18b5
13 changed files with 303 additions and 122 deletions
|
|
@ -48,7 +48,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
|
||||||
- [x] Replies
|
- [x] Replies
|
||||||
- [x] Choose ping on/off
|
- [x] Choose ping on/off
|
||||||
- [x] Per message profiles
|
- [x] Per message profiles
|
||||||
- [ ] Attachments
|
- [x] Attachments
|
||||||
- [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391)
|
- [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391)
|
||||||
- [x] Mentions
|
- [x] Mentions
|
||||||
- [x] Users
|
- [x] Users
|
||||||
|
|
|
||||||
41
lib/controllers/attachment.dart
Normal file
41
lib/controllers/attachment.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import "package:file_selector/file_selector.dart";
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/client.dart";
|
||||||
|
import "package:nexus/controllers/rooms.dart";
|
||||||
|
import "package:nexus/models/content/content.dart";
|
||||||
|
import "package:nexus/models/content/message.dart";
|
||||||
|
import "package:path/path.dart";
|
||||||
|
|
||||||
|
class AttachmentController extends Notifier<(String, MessageContent?)?> {
|
||||||
|
final String roomId;
|
||||||
|
AttachmentController(this.roomId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Null build() => null;
|
||||||
|
|
||||||
|
Future<void> add(XFile file) async {
|
||||||
|
final filename = basename(file.path);
|
||||||
|
state = (filename, null);
|
||||||
|
|
||||||
|
final room = ref.watch(
|
||||||
|
RoomsController.provider.select((value) => value[roomId]),
|
||||||
|
);
|
||||||
|
|
||||||
|
final content = await ref
|
||||||
|
.watch(ClientController.provider.notifier)
|
||||||
|
.uploadMedia(
|
||||||
|
.new(
|
||||||
|
path: file.path,
|
||||||
|
encrypted:
|
||||||
|
room?.state[EventType.encryption.name]?.isNotEmpty == true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
state = (filename, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final provider = NotifierProvider.family
|
||||||
|
.autoDispose<AttachmentController, (String, MessageContent?)?, String>(
|
||||||
|
AttachmentController.new,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import "package:nexus/controllers/sync_status.dart";
|
||||||
import "package:nexus/controllers/top_level_spaces.dart";
|
import "package:nexus/controllers/top_level_spaces.dart";
|
||||||
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
|
import "package:nexus/models/content/message.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
import "package:nexus/models/oauth_auth_code_response.dart";
|
import "package:nexus/models/oauth_auth_code_response.dart";
|
||||||
import "package:nexus/models/paginate.dart";
|
import "package:nexus/models/paginate.dart";
|
||||||
|
|
@ -32,6 +33,7 @@ import "package:nexus/models/requests/send_event.dart";
|
||||||
import "package:nexus/models/requests/send_message.dart";
|
import "package:nexus/models/requests/send_message.dart";
|
||||||
import "package:nexus/models/requests/set_membership.dart";
|
import "package:nexus/models/requests/set_membership.dart";
|
||||||
import "package:nexus/models/requests/set_state.dart";
|
import "package:nexus/models/requests/set_state.dart";
|
||||||
|
import "package:nexus/models/requests/upload_media.dart";
|
||||||
import "package:nexus/models/room.dart";
|
import "package:nexus/models/room.dart";
|
||||||
import "package:nexus/models/sync_data.dart";
|
import "package:nexus/models/sync_data.dart";
|
||||||
import "package:nexus/src/third_party/gomuks.g.dart";
|
import "package:nexus/src/third_party/gomuks.g.dart";
|
||||||
|
|
@ -266,6 +268,9 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
Future<void> setMembership(SetMembershipRequest request) =>
|
Future<void> setMembership(SetMembershipRequest request) =>
|
||||||
_sendCommand("set_membership", request.toJson());
|
_sendCommand("set_membership", request.toJson());
|
||||||
|
|
||||||
|
Future<MessageContent> uploadMedia(UploadMediaRequest request) async =>
|
||||||
|
.fromJson(await _sendCommand("upload_media", request.toJson()));
|
||||||
|
|
||||||
Future<void> logout() => _sendCommand("logout");
|
Future<void> logout() => _sendCommand("logout");
|
||||||
|
|
||||||
Future<void> markRead(Room room) async {
|
Future<void> markRead(Room room) async {
|
||||||
|
|
|
||||||
11
lib/controllers/image_picker.dart
Normal file
11
lib/controllers/image_picker.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:image_picker/image_picker.dart";
|
||||||
|
|
||||||
|
class ImagePickerController extends Notifier<ImagePicker> {
|
||||||
|
@override
|
||||||
|
ImagePicker build() => .new();
|
||||||
|
|
||||||
|
static final provider = NotifierProvider<ImagePickerController, ImagePicker>(
|
||||||
|
ImagePickerController.new,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import "package:collection/collection.dart";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:fluttertagger/fluttertagger.dart";
|
import "package:fluttertagger/fluttertagger.dart";
|
||||||
|
import "package:nexus/controllers/attachment.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
import "package:nexus/controllers/client.dart";
|
||||||
import "package:nexus/controllers/rooms.dart";
|
import "package:nexus/controllers/rooms.dart";
|
||||||
import "package:nexus/models/content/content.dart";
|
import "package:nexus/models/content/content.dart";
|
||||||
|
|
@ -120,6 +121,10 @@ class RoomChatController extends AsyncNotifier<IList<Event>?> {
|
||||||
required RelationType relationType,
|
required RelationType relationType,
|
||||||
Event? relation,
|
Event? relation,
|
||||||
}) async {
|
}) async {
|
||||||
|
final attachment = ref.watch(AttachmentController.provider(roomId));
|
||||||
|
|
||||||
|
if (attachment != null && attachment.$2 == null) return;
|
||||||
|
|
||||||
var taggedMessage = text;
|
var taggedMessage = text;
|
||||||
|
|
||||||
for (final tag in tags) {
|
for (final tag in tags) {
|
||||||
|
|
@ -136,6 +141,7 @@ class RoomChatController extends AsyncNotifier<IList<Event>?> {
|
||||||
final event = await client.sendMessage(
|
final event = await client.sendMessage(
|
||||||
SendMessageRequest(
|
SendMessageRequest(
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
|
baseContent: attachment?.$2,
|
||||||
mentions: Mentions(
|
mentions: Mentions(
|
||||||
userIds: [
|
userIds: [
|
||||||
if (shouldMention == true &&
|
if (shouldMention == true &&
|
||||||
|
|
@ -152,6 +158,8 @@ class RoomChatController extends AsyncNotifier<IList<Event>?> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ref.invalidate(AttachmentController.provider(roomId));
|
||||||
|
|
||||||
ref
|
ref
|
||||||
.watch(RoomsController.provider.notifier)
|
.watch(RoomsController.provider.notifier)
|
||||||
.update(
|
.update(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:freezed_annotation/freezed_annotation.dart";
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
import "package:nexus/models/content/content.dart";
|
||||||
import "package:nexus/models/relation_type.dart";
|
import "package:nexus/models/relation_type.dart";
|
||||||
part "send_message.freezed.dart";
|
part "send_message.freezed.dart";
|
||||||
part "send_message.g.dart";
|
part "send_message.g.dart";
|
||||||
|
|
@ -9,6 +10,7 @@ abstract class SendMessageRequest with _$SendMessageRequest {
|
||||||
const factory SendMessageRequest({
|
const factory SendMessageRequest({
|
||||||
required String roomId,
|
required String roomId,
|
||||||
required String text,
|
required String text,
|
||||||
|
Content? baseContent,
|
||||||
@Default(Mentions()) @JsonKey(name: "mentions") Mentions mentions,
|
@Default(Mentions()) @JsonKey(name: "mentions") Mentions mentions,
|
||||||
@JsonKey(name: "relates_to") Relation? relation,
|
@JsonKey(name: "relates_to") Relation? relation,
|
||||||
}) = _SendMessageRequest;
|
}) = _SendMessageRequest;
|
||||||
|
|
|
||||||
24
lib/models/requests/upload_media.dart
Normal file
24
lib/models/requests/upload_media.dart
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
part "upload_media.freezed.dart";
|
||||||
|
part "upload_media.g.dart";
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class UploadMediaRequest with _$UploadMediaRequest {
|
||||||
|
const factory UploadMediaRequest({
|
||||||
|
required String path,
|
||||||
|
required bool encrypted,
|
||||||
|
String? filename,
|
||||||
|
@Default(false) @JsonKey(name: "voice_message") bool isVoiceMessage,
|
||||||
|
@Default(false) bool forceFile,
|
||||||
|
|
||||||
|
// Below params only work if encodeTo is set
|
||||||
|
String? encodeTo,
|
||||||
|
int? resizeWidth,
|
||||||
|
int? resizeHeight,
|
||||||
|
int? resizePercent,
|
||||||
|
@Default(80) int quality,
|
||||||
|
}) = _UploadMediaRequest;
|
||||||
|
|
||||||
|
factory UploadMediaRequest.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$UploadMediaRequestFromJson(json);
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
|
import "dart:io";
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
|
import "package:file_selector/file_selector.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter/services.dart";
|
import "package:flutter/services.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:fluttertagger/fluttertagger.dart";
|
import "package:fluttertagger/fluttertagger.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/attachment.dart";
|
||||||
|
import "package:nexus/controllers/image_picker.dart";
|
||||||
import "package:nexus/controllers/power_level.dart";
|
import "package:nexus/controllers/power_level.dart";
|
||||||
import "package:nexus/models/content/message.dart";
|
import "package:nexus/models/content/message.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
@ -11,6 +15,7 @@ import "package:nexus/models/relation_type.dart";
|
||||||
import "package:nexus/widgets/composer/mention_overlay.dart";
|
import "package:nexus/widgets/composer/mention_overlay.dart";
|
||||||
import "package:nexus/widgets/composer/relation_preview.dart";
|
import "package:nexus/widgets/composer/relation_preview.dart";
|
||||||
import "package:nexus/widgets/emoji_picker_button.dart";
|
import "package:nexus/widgets/emoji_picker_button.dart";
|
||||||
|
import "package:nexus/main.dart";
|
||||||
|
|
||||||
class Composer extends HookConsumerWidget {
|
class Composer extends HookConsumerWidget {
|
||||||
final String roomId;
|
final String roomId;
|
||||||
|
|
@ -68,131 +73,190 @@ class Composer extends HookConsumerWidget {
|
||||||
fontWeight: .bold,
|
fontWeight: .bold,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final attachment = ref.watch(AttachmentController.provider(roomId));
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: .all(12),
|
padding: .all(12),
|
||||||
child: ClipRRect(
|
child: Column(
|
||||||
borderRadius: .all(.circular(12)),
|
children: [
|
||||||
child: Column(
|
if (attachment != null)
|
||||||
children: [
|
Card(
|
||||||
RelationPreview(
|
margin: .only(bottom: 8),
|
||||||
relatedEvent,
|
child: ListTile(
|
||||||
shouldMention: shouldMention.value,
|
leading: attachment.$2 == null
|
||||||
toggleShouldMention: () =>
|
? SizedBox(
|
||||||
shouldMention.value = !shouldMention.value,
|
width: 16,
|
||||||
relationType: relationType,
|
height: 16,
|
||||||
onDismiss: onDismiss,
|
child: CircularProgressIndicator(),
|
||||||
|
)
|
||||||
|
: Icon(Icons.file_copy),
|
||||||
|
title: Text(attachment.$1),
|
||||||
|
trailing: IconButton(
|
||||||
|
onPressed: () =>
|
||||||
|
ref.invalidate(AttachmentController.provider(roomId)),
|
||||||
|
icon: Icon(Icons.close),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Container(
|
ClipRRect(
|
||||||
color: theme.colorScheme.surfaceContainerHighest,
|
borderRadius: .all(.circular(12)),
|
||||||
padding: .symmetric(horizontal: 8),
|
child: Column(
|
||||||
child: Row(
|
children: [
|
||||||
spacing: 8,
|
RelationPreview(
|
||||||
mainAxisAlignment: .center,
|
relatedEvent,
|
||||||
children:
|
shouldMention: shouldMention.value,
|
||||||
ref.watch(
|
toggleShouldMention: () =>
|
||||||
PowerLevelController.provider(
|
shouldMention.value = !shouldMention.value,
|
||||||
.new(eventType: .message, roomId: roomId),
|
relationType: relationType,
|
||||||
),
|
onDismiss: onDismiss,
|
||||||
)
|
),
|
||||||
? [
|
Container(
|
||||||
EmojiPickerButton(
|
color: theme.colorScheme.surfaceContainerHighest,
|
||||||
context: context,
|
padding: .symmetric(horizontal: 8),
|
||||||
onSelection: (_) => node?.requestFocus(),
|
child: Row(
|
||||||
controller: controller.value,
|
spacing: 8,
|
||||||
),
|
mainAxisAlignment: .center,
|
||||||
PopupMenuButton(
|
children:
|
||||||
tooltip: "Add media",
|
ref.watch(
|
||||||
itemBuilder: (context) => [
|
PowerLevelController.provider(
|
||||||
PopupMenuItem(
|
.new(eventType: .message, roomId: roomId),
|
||||||
child: ListTile(
|
),
|
||||||
title: Text("Camera"),
|
)
|
||||||
leading: Icon(Icons.add_a_photo),
|
? [
|
||||||
|
EmojiPickerButton(
|
||||||
|
context: context,
|
||||||
|
onSelection: (_) => node?.requestFocus(),
|
||||||
|
controller: controller.value,
|
||||||
|
),
|
||||||
|
PopupMenuButton(
|
||||||
|
tooltip: "Add media",
|
||||||
|
enabled: attachment == null,
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
if (Platform.isAndroid || Platform.isIOS)
|
||||||
|
PopupMenuItem(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text("Camera"),
|
||||||
|
leading: Icon(Icons.add_a_photo),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text("Gallery"),
|
||||||
|
leading: Icon(Icons.add_photo_alternate),
|
||||||
|
),
|
||||||
|
onTap: () async => ref
|
||||||
|
.watch(
|
||||||
|
AttachmentController.provider(
|
||||||
|
roomId,
|
||||||
|
).notifier,
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
(await ref
|
||||||
|
.watch(
|
||||||
|
ImagePickerController.provider,
|
||||||
|
)
|
||||||
|
.pickImage(source: .gallery))!,
|
||||||
|
)
|
||||||
|
.onError(showError),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
onTap: () async => ref
|
||||||
|
.watch(
|
||||||
|
AttachmentController.provider(
|
||||||
|
roomId,
|
||||||
|
).notifier,
|
||||||
|
)
|
||||||
|
.add((await openFile())!)
|
||||||
|
.onError(showError),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text("Files"),
|
||||||
|
leading: Icon(Icons.attachment),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
icon: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: FlutterTagger(
|
||||||
|
triggerStrategy: .eager,
|
||||||
|
overlay: MentionOverlay(
|
||||||
|
roomId,
|
||||||
|
query: query.value,
|
||||||
|
triggerCharacter: triggerCharacter.value,
|
||||||
|
addTag: ({required id, required name}) {
|
||||||
|
controller.value.addTag(id: id, name: name);
|
||||||
|
node?.requestFocus();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
controller: controller.value,
|
||||||
|
onSearch: (newQuery, newTriggerCharacter) {
|
||||||
|
triggerCharacter.value = newTriggerCharacter;
|
||||||
|
query.value = newQuery;
|
||||||
|
},
|
||||||
|
triggerCharacterAndStyles: {
|
||||||
|
"@": style,
|
||||||
|
"#": style,
|
||||||
|
},
|
||||||
|
builder: (context, key) => Focus(
|
||||||
|
onKeyEvent: (_, event) {
|
||||||
|
if (event is KeyDownEvent &&
|
||||||
|
event.logicalKey ==
|
||||||
|
LogicalKeyboardKey.enter) {
|
||||||
|
final shiftPressed = HardwareKeyboard
|
||||||
|
.instance
|
||||||
|
.isShiftPressed;
|
||||||
|
|
||||||
|
if (!shiftPressed) {
|
||||||
|
send();
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KeyEventResult.ignored;
|
||||||
|
},
|
||||||
|
child: TextField(
|
||||||
|
maxLines: 12,
|
||||||
|
minLines: 1,
|
||||||
|
autofocus: true,
|
||||||
|
decoration: .new(
|
||||||
|
hintText: "Your message here...",
|
||||||
|
border: .none,
|
||||||
|
),
|
||||||
|
controller: controller.value,
|
||||||
|
key: key,
|
||||||
|
focusNode: node,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
IconButton(
|
||||||
child: ListTile(
|
onPressed:
|
||||||
title: Text("Gallery"),
|
attachment != null && attachment.$2 == null
|
||||||
leading: Icon(Icons.add_photo_alternate),
|
? null
|
||||||
),
|
: send,
|
||||||
|
icon: Icon(Icons.send),
|
||||||
|
tooltip: "Send message",
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
]
|
||||||
child: ListTile(
|
: [
|
||||||
title: Text("Files"),
|
Expanded(
|
||||||
leading: Icon(Icons.attachment),
|
child: Padding(
|
||||||
|
padding: .symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"You don't have permission to send messages in this room...",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
icon: Icon(Icons.add),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
],
|
||||||
child: FlutterTagger(
|
|
||||||
triggerStrategy: .eager,
|
|
||||||
overlay: MentionOverlay(
|
|
||||||
roomId,
|
|
||||||
query: query.value,
|
|
||||||
triggerCharacter: triggerCharacter.value,
|
|
||||||
addTag: ({required id, required name}) {
|
|
||||||
controller.value.addTag(id: id, name: name);
|
|
||||||
node?.requestFocus();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
controller: controller.value,
|
|
||||||
onSearch: (newQuery, newTriggerCharacter) {
|
|
||||||
triggerCharacter.value = newTriggerCharacter;
|
|
||||||
query.value = newQuery;
|
|
||||||
},
|
|
||||||
triggerCharacterAndStyles: {"@": style, "#": style},
|
|
||||||
builder: (context, key) => Focus(
|
|
||||||
onKeyEvent: (_, event) {
|
|
||||||
if (event is KeyDownEvent &&
|
|
||||||
event.logicalKey ==
|
|
||||||
LogicalKeyboardKey.enter) {
|
|
||||||
final shiftPressed =
|
|
||||||
HardwareKeyboard.instance.isShiftPressed;
|
|
||||||
|
|
||||||
if (!shiftPressed) {
|
|
||||||
send();
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
},
|
|
||||||
child: TextField(
|
|
||||||
maxLines: 12,
|
|
||||||
minLines: 1,
|
|
||||||
autofocus: true,
|
|
||||||
decoration: .new(
|
|
||||||
hintText: "Your message here...",
|
|
||||||
border: .none,
|
|
||||||
),
|
|
||||||
controller: controller.value,
|
|
||||||
key: key,
|
|
||||||
focusNode: node,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: send,
|
|
||||||
icon: Icon(Icons.send),
|
|
||||||
tooltip: "Send message",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: .symmetric(horizontal: 8, vertical: 12),
|
|
||||||
child: Text(
|
|
||||||
"You don't have permission to send messages in this room...",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import Foundation
|
||||||
|
|
||||||
import app_links
|
import app_links
|
||||||
import dynamic_color
|
import dynamic_color
|
||||||
import file_picker
|
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import media_kit_libs_macos_video
|
import media_kit_libs_macos_video
|
||||||
import media_kit_video
|
import media_kit_video
|
||||||
|
|
@ -21,7 +20,6 @@ import window_manager
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
||||||
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,7 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,7 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-only</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
32
pubspec.lock
32
pubspec.lock
|
|
@ -402,14 +402,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
file_picker:
|
file_selector:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_selector
|
||||||
sha256: fdc6a37f715d19f35b131decf1ce39242eeed5ddae18c0818c3eccb731ab76be
|
sha256: bd15e43e9268db636b53eeaca9f56324d1622af30e5c34d6e267649758c84d9a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.0-beta.7"
|
version: "1.1.0"
|
||||||
|
file_selector_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_android
|
||||||
|
sha256: "6a26687fa65cbc28a5345c7ae6f227e89f0b47740978a4c475b1a625da7a331b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.2+8"
|
||||||
|
file_selector_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_ios
|
||||||
|
sha256: e2ecf2885c121691ce13b60db3508f53c01f869fb6e8dc5c1cfa771e4c46aeca
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.3+5"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -434,6 +450,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.7.0"
|
||||||
|
file_selector_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_web
|
||||||
|
sha256: "73181fbc5257776d8ecaa6a94ab3c8e920ad143b9132a6d984a9271dfc6928d3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.5"
|
||||||
file_selector_windows:
|
file_selector_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,7 @@ dependencies:
|
||||||
path_provider: 2.1.6
|
path_provider: 2.1.6
|
||||||
url_launcher: 6.3.2
|
url_launcher: 6.3.2
|
||||||
freezed_annotation: 3.1.0
|
freezed_annotation: 3.1.0
|
||||||
image_picker: 1.2.3
|
image_picker: ^1.2.3
|
||||||
file_picker: 12.0.0-beta.7
|
|
||||||
path: 1.9.1
|
path: 1.9.1
|
||||||
dynamic_color: 1.8.1
|
dynamic_color: 1.8.1
|
||||||
collection: 1.19.1
|
collection: 1.19.1
|
||||||
|
|
@ -78,6 +77,7 @@ dependencies:
|
||||||
xdg_directories: 1.1.0
|
xdg_directories: 1.1.0
|
||||||
package_info_plus: 10.2.1
|
package_info_plus: 10.2.1
|
||||||
app_links: 7.2.1
|
app_links: 7.2.1
|
||||||
|
file_selector: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.15.1
|
build_runner: ^2.15.1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue