merge main
This commit is contained in:
commit
eedb050ce1
14 changed files with 138 additions and 62 deletions
|
|
@ -804,11 +804,18 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
builder: (c) => const RecordingDialog(),
|
||||
);
|
||||
if (result == null) return;
|
||||
final audioFile = File(result.path);
|
||||
// #Pangea
|
||||
// enable web recording
|
||||
// final audioFile = File(result.path);
|
||||
// final file = MatrixAudioFile(
|
||||
// bytes: audioFile.readAsBytesSync(),
|
||||
// name: audioFile.path,
|
||||
// );
|
||||
final file = MatrixAudioFile(
|
||||
bytes: audioFile.readAsBytesSync(),
|
||||
name: audioFile.path,
|
||||
bytes: result.bytes,
|
||||
name: result.path,
|
||||
);
|
||||
// Pangea#
|
||||
await room.sendFileEvent(
|
||||
file,
|
||||
inReplyTo: replyEvent,
|
||||
|
|
|
|||
|
|
@ -286,6 +286,8 @@ class MessageContent extends StatelessWidget {
|
|||
final bigEmotes = event.onlyEmotes &&
|
||||
event.numberEmotes > 0 &&
|
||||
event.numberEmotes <= 10;
|
||||
// #Pangea
|
||||
// return Linkify(
|
||||
final messageTextStyle = TextStyle(
|
||||
color: textColor,
|
||||
fontSize: bigEmotes ? fontSize * 3 : fontSize,
|
||||
|
|
@ -301,11 +303,10 @@ class MessageContent extends StatelessWidget {
|
|||
);
|
||||
} else if (pangeaMessageEvent != null) {
|
||||
toolbarController?.toolbar?.textSelection.setMessageText(
|
||||
pangeaMessageEvent!.body,
|
||||
(event.getDisplayEvent(pangeaMessageEvent!.timeline).body),
|
||||
);
|
||||
}
|
||||
|
||||
// return Linkify(
|
||||
return SelectableLinkify(
|
||||
onSelectionChanged: (selection, cause) {
|
||||
if (cause == SelectionChangedCause.longPress &&
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/utils/update_version_dialog.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:record/record.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
|
@ -39,9 +42,15 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
|
||||
Future<void> startRecording() async {
|
||||
try {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final path = _recordedPath =
|
||||
'${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}';
|
||||
// #Pangea
|
||||
// enable recording on web
|
||||
// final tempDir = await getTemporaryDirectory();
|
||||
// final path = _recordedPath =
|
||||
// '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}';
|
||||
final tempDirPath = kIsWeb ? "." : (await getTemporaryDirectory()).path;
|
||||
_recordedPath =
|
||||
'$tempDirPath/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}';
|
||||
// Pangea#
|
||||
|
||||
final result = await _audioRecorder.hasPermission();
|
||||
if (result != true) {
|
||||
|
|
@ -110,9 +119,25 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
|
||||
void _stopAndSend() async {
|
||||
_recorderSubscription?.cancel();
|
||||
await _audioRecorder.stop();
|
||||
// #Pangea
|
||||
// await _audioRecorder.stop();
|
||||
final outputPath = await _audioRecorder.stop();
|
||||
// Pangea#
|
||||
final path = _recordedPath;
|
||||
if (path == null) throw ('Recording failed!');
|
||||
|
||||
// #Pangea
|
||||
Uint8List bytes;
|
||||
if (kIsWeb) {
|
||||
if (outputPath == null) throw ('Recording failed!');
|
||||
final response = await http.get(Uri.parse(outputPath));
|
||||
bytes = response.bodyBytes;
|
||||
} else {
|
||||
final audioFile = File(path);
|
||||
bytes = audioFile.readAsBytesSync();
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
const waveCount = AudioPlayerWidget.wavesCount;
|
||||
final step = amplitudeTimeline.length < waveCount
|
||||
? 1
|
||||
|
|
@ -126,6 +151,9 @@ class RecordingDialogState extends State<RecordingDialog> {
|
|||
path: path,
|
||||
duration: _duration.inMilliseconds,
|
||||
waveform: waveform,
|
||||
// #Pangea
|
||||
bytes: bytes,
|
||||
// Pangea#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -236,11 +264,17 @@ class RecordingResult {
|
|||
final String path;
|
||||
final int duration;
|
||||
final List<int> waveform;
|
||||
// #Pangea
|
||||
final Uint8List bytes;
|
||||
// Pangea#
|
||||
|
||||
const RecordingResult({
|
||||
required this.path,
|
||||
required this.duration,
|
||||
required this.waveform,
|
||||
// #Pangea
|
||||
required this.bytes,
|
||||
// Pangea#
|
||||
});
|
||||
|
||||
factory RecordingResult.fromJson(Map<String, dynamic> json) =>
|
||||
|
|
@ -248,11 +282,17 @@ class RecordingResult {
|
|||
path: json['path'],
|
||||
duration: json['duration'],
|
||||
waveform: List<int>.from(json['waveform']),
|
||||
// #Pangea
|
||||
bytes: Uint8List.fromList(json['bytes']),
|
||||
// Pangea#
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'path': path,
|
||||
'duration': duration,
|
||||
'waveform': waveform,
|
||||
// #Pangea
|
||||
'bytes': bytes,
|
||||
// Pangea#
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,9 +183,10 @@ class ChatListController extends State<ChatList>
|
|||
bool Function(Room) getRoomFilterByActiveFilter(ActiveFilter activeFilter) {
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
return (room) => !room.isSpace; // #Pangea
|
||||
// &&
|
||||
// !room.isAnalyticsRoom;
|
||||
return (room) =>
|
||||
!room.isSpace // #Pangea
|
||||
&&
|
||||
!room.isAnalyticsRoom;
|
||||
// Pangea#;
|
||||
case ActiveFilter.groups:
|
||||
return (room) =>
|
||||
|
|
@ -799,6 +800,10 @@ class ChatListController extends State<ChatList>
|
|||
// Pangea#
|
||||
|
||||
Future<void> addToSpace() async {
|
||||
// #Pangea
|
||||
final firstSelectedRoom =
|
||||
Matrix.of(context).client.getRoomById(selectedRoomIds.toList().first);
|
||||
// Pangea#
|
||||
final selectedSpace = await showConfirmationDialog<String>(
|
||||
context: context,
|
||||
title: L10n.of(context)!.addToSpace,
|
||||
|
|
@ -817,8 +822,9 @@ class ChatListController extends State<ChatList>
|
|||
&&
|
||||
selectedRoomIds
|
||||
.map((id) => Matrix.of(context).client.getRoomById(id))
|
||||
.where((e) => !(e?.isSpace ?? false))
|
||||
.every((e) => r.canIAddSpaceChild(e)),
|
||||
// Only show non-recursion-causing spaces
|
||||
// Performs a few other checks as well
|
||||
.every((e) => r.canAddAsParentOf(e)),
|
||||
//Pangea#
|
||||
)
|
||||
.map(
|
||||
|
|
@ -828,6 +834,13 @@ class ChatListController extends State<ChatList>
|
|||
// label: space
|
||||
// .getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
label: space.nameIncludingParents(context),
|
||||
// If user is not admin of space, button is grayed out
|
||||
textStyle: TextStyle(
|
||||
color: (firstSelectedRoom == null ||
|
||||
(firstSelectedRoom.isSpace && !space.isRoomAdmin))
|
||||
? Theme.of(context).colorScheme.outline
|
||||
: Theme.of(context).colorScheme.surfaceTint,
|
||||
),
|
||||
// Pangea#
|
||||
),
|
||||
)
|
||||
|
|
@ -839,12 +852,20 @@ class ChatListController extends State<ChatList>
|
|||
future: () async {
|
||||
final space = Matrix.of(context).client.getRoomById(selectedSpace)!;
|
||||
// #Pangea
|
||||
if (firstSelectedRoom == null) {
|
||||
throw L10n.of(context)!.nonexistentSelection;
|
||||
}
|
||||
// If user is not admin of the would-be parent space, does not allow
|
||||
if (firstSelectedRoom.isSpace && !space.isRoomAdmin) {
|
||||
throw L10n.of(context)!.cantAddSpaceChild;
|
||||
}
|
||||
await pangeaAddToSpace(
|
||||
space,
|
||||
selectedRoomIds.toList(),
|
||||
context,
|
||||
pangeaController,
|
||||
);
|
||||
|
||||
// if (space.canSendDefaultStates) {
|
||||
// for (final roomId in selectedRoomIds) {
|
||||
// await space.setSpaceChild(roomId);
|
||||
|
|
@ -857,7 +878,10 @@ class ChatListController extends State<ChatList>
|
|||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context)!.chatHasBeenAddedToThisSpace),
|
||||
// #Pangea
|
||||
// content: Text(L10n.of(context)!.chatHasBeenAddedToThisSpace),
|
||||
content: Text(L10n.of(context)!.roomAddedToSpace),
|
||||
// Pangea#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -338,6 +338,16 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
widget.controller.cancelAction();
|
||||
// #Pangea
|
||||
if (room == null || room.membership == Membership.leave) return;
|
||||
if (room.isSpace) {
|
||||
await room.archiveSpace(
|
||||
context,
|
||||
Matrix.of(context).client,
|
||||
onlyAdmin: false,
|
||||
);
|
||||
} else {
|
||||
widget.controller.toggleSelection(room.id);
|
||||
await widget.controller.archiveAction();
|
||||
}
|
||||
// Pangea#
|
||||
_refresh();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ extension ChildrenAndParentsRoomExtension on Room {
|
|||
|
||||
// Checks if has permissions to add child chat
|
||||
// Or whether potential child space is ancestor of this
|
||||
bool _canAddAsParentOf(Room? child, {bool spaceMode = false}) {
|
||||
bool _canAddAsParentOf(Room? child) {
|
||||
if (child == null || !child.isSpace) {
|
||||
return _canIAddSpaceChild(child, spaceMode: spaceMode);
|
||||
return _canIAddSpaceChild(child);
|
||||
}
|
||||
if (id == child.id) return false;
|
||||
return !child._allSpaceChildRoomIds.contains(id);
|
||||
|
|
|
|||
|
|
@ -60,9 +60,14 @@ extension EventsRoomExtension on Room {
|
|||
future: () async {
|
||||
final List<Room> children = await getChildRooms();
|
||||
for (final Room child in children) {
|
||||
await child.archive();
|
||||
if (!child.isAnalyticsRoom) {
|
||||
if (child.membership != Membership.join) {
|
||||
child.join;
|
||||
}
|
||||
await child.archive();
|
||||
}
|
||||
}
|
||||
await archive();
|
||||
await _archive();
|
||||
},
|
||||
);
|
||||
MatrixState.pangeaController.classController
|
||||
|
|
|
|||
|
|
@ -109,10 +109,9 @@ extension PangeaRoom on Room {
|
|||
|
||||
List<String> get allSpaceChildRoomIds => _allSpaceChildRoomIds;
|
||||
|
||||
bool canAddAsParentOf(Room? child, {spaceMode = false}) =>
|
||||
_canAddAsParentOf(child, spaceMode: spaceMode);
|
||||
bool canAddAsParentOf(Room? child) => _canAddAsParentOf(child);
|
||||
|
||||
// space settings
|
||||
// class_and_exchange_settings
|
||||
|
||||
DateTime? get rulesUpdatedAt => _rulesUpdatedAt;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,18 +66,16 @@ extension RoomInformationRoomExtension on Room {
|
|||
return (eventsDefaultPowerLevel ?? 0) >=
|
||||
ClassDefaultValues.powerLevelOfAdmin;
|
||||
}
|
||||
int joinedRooms = 0;
|
||||
for (final child in spaceChildren) {
|
||||
if (child.roomId == null) continue;
|
||||
final Room? room = client.getRoomById(child.roomId!);
|
||||
if (room?.isLocked == false) {
|
||||
if (room == null || room.isAnalyticsRoom || room.isArchived) continue;
|
||||
if (!room._isLocked) {
|
||||
return false;
|
||||
}
|
||||
if (room != null) {
|
||||
joinedRooms += 1;
|
||||
}
|
||||
}
|
||||
return joinedRooms > 0 ? true : false;
|
||||
return (eventsDefaultPowerLevel ?? 0) >=
|
||||
ClassDefaultValues.powerLevelOfAdmin;
|
||||
}
|
||||
|
||||
bool _isAnalyticsRoomOfUser(String userId) =>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
Future<void> lockRoom(Room room, Client client) async {
|
||||
|
|
@ -65,7 +66,7 @@ Future<void> lockSpace(Room space, Client client) async {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (child == null) continue;
|
||||
if (child == null || child.isArchived || child.isAnalyticsRoom) continue;
|
||||
child.isSpace
|
||||
? await lockSpace(child, client)
|
||||
: await lockChat(child, client);
|
||||
|
|
|
|||
|
|
@ -15,14 +15,12 @@ class OverlayMessage extends StatelessWidget {
|
|||
final Event? previousEvent;
|
||||
final bool selected;
|
||||
final Timeline timeline;
|
||||
// #Pangea
|
||||
// final LanguageModel? selectedDisplayLang;
|
||||
final bool immersionMode;
|
||||
// final bool definitions;
|
||||
final bool ownMessage;
|
||||
final ToolbarDisplayController toolbarController;
|
||||
final double? width;
|
||||
// Pangea#
|
||||
|
||||
const OverlayMessage(
|
||||
this.event, {
|
||||
|
|
@ -30,12 +28,10 @@ class OverlayMessage extends StatelessWidget {
|
|||
this.previousEvent,
|
||||
this.selected = false,
|
||||
required this.timeline,
|
||||
// #Pangea
|
||||
required this.immersionMode,
|
||||
required this.ownMessage,
|
||||
required this.toolbarController,
|
||||
this.width,
|
||||
// Pangea#
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -46,14 +42,12 @@ class OverlayMessage extends StatelessWidget {
|
|||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
var color = Theme.of(context).colorScheme.surfaceVariant;
|
||||
// #Pangea
|
||||
var color = Theme.of(context).colorScheme.surfaceContainerHighest;
|
||||
final isLight = Theme.of(context).brightness == Brightness.light;
|
||||
var lightness = isLight ? .05 : .85;
|
||||
// Pangea#
|
||||
final textColor = ownMessage
|
||||
? Theme.of(context).colorScheme.onPrimary
|
||||
: Theme.of(context).colorScheme.onBackground;
|
||||
: Theme.of(context).colorScheme.onSurface;
|
||||
|
||||
const hardCorner = Radius.circular(4);
|
||||
|
||||
|
|
@ -118,13 +112,11 @@ class OverlayMessage extends StatelessWidget {
|
|||
: (color.blue * lightness).round(),
|
||||
);
|
||||
|
||||
// #Pangea
|
||||
final pangeaMessageEvent = PangeaMessageEvent(
|
||||
event: event,
|
||||
timeline: timeline,
|
||||
ownMessage: ownMessage,
|
||||
);
|
||||
// Pangea#
|
||||
|
||||
return Material(
|
||||
color: noBubble ? Colors.transparent : color,
|
||||
|
|
@ -152,7 +144,7 @@ class OverlayMessage extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
MessageContent(
|
||||
event,
|
||||
event.getDisplayEvent(timeline),
|
||||
textColor: textColor,
|
||||
borderRadius: borderRadius,
|
||||
selected: selected,
|
||||
|
|
@ -162,13 +154,10 @@ class OverlayMessage extends StatelessWidget {
|
|||
isOverlay: true,
|
||||
),
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
) // #Pangea
|
||||
||
|
||||
(pangeaMessageEvent.showUseType)
|
||||
// Pangea#
|
||||
)
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
) ||
|
||||
(pangeaMessageEvent.showUseType))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
|
|
@ -176,7 +165,6 @@ class OverlayMessage extends StatelessWidget {
|
|||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (pangeaMessageEvent.showUseType) ...[
|
||||
pangeaMessageEvent.useType.iconView(
|
||||
context,
|
||||
|
|
@ -188,14 +176,13 @@ class OverlayMessage extends StatelessWidget {
|
|||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
)) ...[
|
||||
// Pangea#
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
' - ${event.originServerTs.localizedTimeShort(context)}',
|
||||
' - ${event.getDisplayEvent(timeline).originServerTs.localizedTimeShort(context)}',
|
||||
style: TextStyle(
|
||||
color: textColor.withAlpha(164),
|
||||
fontSize: 12,
|
||||
|
|
|
|||
|
|
@ -140,10 +140,13 @@ class AddToSpaceState extends State<AddToSpaceToggles> {
|
|||
|
||||
Widget getAddToSpaceToggleItem(int index) {
|
||||
final Room possibleParent = possibleParents[index];
|
||||
final bool canAdd = possibleParent.canAddAsParentOf(
|
||||
room,
|
||||
spaceMode: widget.spaceMode,
|
||||
);
|
||||
final bool canAdd = (room?.isSpace ?? false)
|
||||
// Room is space
|
||||
? possibleParent.isRoomAdmin &&
|
||||
room!.isRoomAdmin &&
|
||||
possibleParent.canAddAsParentOf(room)
|
||||
// Room is null or chat
|
||||
: possibleParent.canAddAsParentOf(room);
|
||||
|
||||
return Opacity(
|
||||
opacity: canAdd ? 1 : 0.5,
|
||||
|
|
|
|||
|
|
@ -70,12 +70,11 @@ class PangeaRichTextState extends State<PangeaRichText> {
|
|||
|
||||
void setTextSpan() {
|
||||
if (_fetchingRepresentation == true) {
|
||||
_setTextSpan(textSpan = widget.pangeaMessageEvent.body);
|
||||
return;
|
||||
}
|
||||
|
||||
if (repEvent != null) {
|
||||
_setTextSpan(repEvent!.text);
|
||||
_setTextSpan(
|
||||
textSpan = widget.pangeaMessageEvent.event
|
||||
.getDisplayEvent(widget.pangeaMessageEvent.timeline)
|
||||
.body,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
|
@ -29,7 +28,10 @@ abstract class PlatformInfos {
|
|||
|
||||
static bool get usesTouchscreen => !isMobile;
|
||||
|
||||
static bool get platformCanRecord => (isMobile || isMacOS);
|
||||
// #Pangea
|
||||
// static bool get platformCanRecord => (isMobile || isMacOS);
|
||||
static bool get platformCanRecord => (isMobile || isMacOS || kIsWeb);
|
||||
// Pangea#
|
||||
|
||||
static String get clientName =>
|
||||
'${AppConfig.applicationName} ${isWeb ? 'web' : Platform.operatingSystem}${kReleaseMode ? '' : 'Debug'}';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue