✨ DM groups
This commit is contained in:
parent
7ccaf737a9
commit
81d5083908
@ -10,8 +10,8 @@ part 'chat.g.dart';
|
|||||||
abstract class SnChatRoom with _$SnChatRoom {
|
abstract class SnChatRoom with _$SnChatRoom {
|
||||||
const factory SnChatRoom({
|
const factory SnChatRoom({
|
||||||
required String id,
|
required String id,
|
||||||
required String name,
|
required String? name,
|
||||||
required String description,
|
required String? description,
|
||||||
required int type,
|
required int type,
|
||||||
required bool isPublic,
|
required bool isPublic,
|
||||||
required String? pictureId,
|
required String? pictureId,
|
||||||
|
@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnChatRoom {
|
mixin _$SnChatRoom {
|
||||||
|
|
||||||
String get id; String get name; String get description; int get type; bool get isPublic; String? get pictureId; SnCloudFile? get picture; String? get backgroundId; SnCloudFile? get background; String? get realmId; SnRealm? get realm; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; List<SnChatMember>? get members;
|
String get id; String? get name; String? get description; int get type; bool get isPublic; String? get pictureId; SnCloudFile? get picture; String? get backgroundId; SnCloudFile? get background; String? get realmId; SnRealm? get realm; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; List<SnChatMember>? get members;
|
||||||
/// Create a copy of SnChatRoom
|
/// Create a copy of SnChatRoom
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@ -49,7 +49,7 @@ abstract mixin class $SnChatRoomCopyWith<$Res> {
|
|||||||
factory $SnChatRoomCopyWith(SnChatRoom value, $Res Function(SnChatRoom) _then) = _$SnChatRoomCopyWithImpl;
|
factory $SnChatRoomCopyWith(SnChatRoom value, $Res Function(SnChatRoom) _then) = _$SnChatRoomCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String name, String description, int type, bool isPublic, String? pictureId, SnCloudFile? picture, String? backgroundId, SnCloudFile? background, String? realmId, SnRealm? realm, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnChatMember>? members
|
String id, String? name, String? description, int type, bool isPublic, String? pictureId, SnCloudFile? picture, String? backgroundId, SnCloudFile? background, String? realmId, SnRealm? realm, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnChatMember>? members
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -66,12 +66,12 @@ class _$SnChatRoomCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnChatRoom
|
/// Create a copy of SnChatRoom
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? description = null,Object? type = null,Object? isPublic = null,Object? pictureId = freezed,Object? picture = freezed,Object? backgroundId = freezed,Object? background = freezed,Object? realmId = freezed,Object? realm = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? members = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = freezed,Object? description = freezed,Object? type = null,Object? isPublic = null,Object? pictureId = freezed,Object? picture = freezed,Object? backgroundId = freezed,Object? background = freezed,Object? realmId = freezed,Object? realm = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? members = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,isPublic: null == isPublic ? _self.isPublic : isPublic // ignore: cast_nullable_to_non_nullable
|
as int,isPublic: null == isPublic ? _self.isPublic : isPublic // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,pictureId: freezed == pictureId ? _self.pictureId : pictureId // ignore: cast_nullable_to_non_nullable
|
as bool,pictureId: freezed == pictureId ? _self.pictureId : pictureId // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
as String?,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
||||||
@ -134,8 +134,8 @@ class _SnChatRoom implements SnChatRoom {
|
|||||||
factory _SnChatRoom.fromJson(Map<String, dynamic> json) => _$SnChatRoomFromJson(json);
|
factory _SnChatRoom.fromJson(Map<String, dynamic> json) => _$SnChatRoomFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@override final String name;
|
@override final String? name;
|
||||||
@override final String description;
|
@override final String? description;
|
||||||
@override final int type;
|
@override final int type;
|
||||||
@override final bool isPublic;
|
@override final bool isPublic;
|
||||||
@override final String? pictureId;
|
@override final String? pictureId;
|
||||||
@ -190,7 +190,7 @@ abstract mixin class _$SnChatRoomCopyWith<$Res> implements $SnChatRoomCopyWith<$
|
|||||||
factory _$SnChatRoomCopyWith(_SnChatRoom value, $Res Function(_SnChatRoom) _then) = __$SnChatRoomCopyWithImpl;
|
factory _$SnChatRoomCopyWith(_SnChatRoom value, $Res Function(_SnChatRoom) _then) = __$SnChatRoomCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String name, String description, int type, bool isPublic, String? pictureId, SnCloudFile? picture, String? backgroundId, SnCloudFile? background, String? realmId, SnRealm? realm, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnChatMember>? members
|
String id, String? name, String? description, int type, bool isPublic, String? pictureId, SnCloudFile? picture, String? backgroundId, SnCloudFile? background, String? realmId, SnRealm? realm, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnChatMember>? members
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -207,12 +207,12 @@ class __$SnChatRoomCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnChatRoom
|
/// Create a copy of SnChatRoom
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? description = null,Object? type = null,Object? isPublic = null,Object? pictureId = freezed,Object? picture = freezed,Object? backgroundId = freezed,Object? background = freezed,Object? realmId = freezed,Object? realm = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? members = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = freezed,Object? description = freezed,Object? type = null,Object? isPublic = null,Object? pictureId = freezed,Object? picture = freezed,Object? backgroundId = freezed,Object? background = freezed,Object? realmId = freezed,Object? realm = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? members = freezed,}) {
|
||||||
return _then(_SnChatRoom(
|
return _then(_SnChatRoom(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||||
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
as String?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
|
||||||
as int,isPublic: null == isPublic ? _self.isPublic : isPublic // ignore: cast_nullable_to_non_nullable
|
as int,isPublic: null == isPublic ? _self.isPublic : isPublic // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,pictureId: freezed == pictureId ? _self.pictureId : pictureId // ignore: cast_nullable_to_non_nullable
|
as bool,pictureId: freezed == pictureId ? _self.pictureId : pictureId // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
as String?,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
||||||
|
@ -8,8 +8,8 @@ part of 'chat.dart';
|
|||||||
|
|
||||||
_SnChatRoom _$SnChatRoomFromJson(Map<String, dynamic> json) => _SnChatRoom(
|
_SnChatRoom _$SnChatRoomFromJson(Map<String, dynamic> json) => _SnChatRoom(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
name: json['name'] as String,
|
name: json['name'] as String?,
|
||||||
description: json['description'] as String,
|
description: json['description'] as String?,
|
||||||
type: (json['type'] as num).toInt(),
|
type: (json['type'] as num).toInt(),
|
||||||
isPublic: json['is_public'] as bool,
|
isPublic: json['is_public'] as bool,
|
||||||
pictureId: json['picture_id'] as String?,
|
pictureId: json['picture_id'] as String?,
|
||||||
|
@ -23,6 +23,7 @@ class _CaptchaScreenState extends ConsumerState<CaptchaScreen> {
|
|||||||
final message = event.data as String;
|
final message = event.data as String;
|
||||||
if (message.startsWith("captcha_tk=")) {
|
if (message.startsWith("captcha_tk=")) {
|
||||||
String token = message.replaceFirst("captcha_tk=", "");
|
String token = message.replaceFirst("captcha_tk=", "");
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
if (context.mounted) Navigator.pop(context, token);
|
if (context.mounted) Navigator.pop(context, token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import 'package:croppy/croppy.dart' hide cropImage;
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -48,20 +47,27 @@ class ChatRoomListTile extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading:
|
leading:
|
||||||
isDirect
|
(isDirect && room.pictureId == null)
|
||||||
? ProfilePictureWidget(
|
? SplitAvatarWidget(
|
||||||
fileId: room.members!.first.account.profile.pictureId,
|
filesId:
|
||||||
|
room.members!
|
||||||
|
.map((e) => e.account.profile.pictureId)
|
||||||
|
.toList(),
|
||||||
)
|
)
|
||||||
: room.pictureId == null
|
: room.pictureId == null
|
||||||
? CircleAvatar(child: Text(room.name[0].toUpperCase()))
|
? CircleAvatar(child: Text(room.name![0].toUpperCase()))
|
||||||
: ProfilePictureWidget(fileId: room.pictureId),
|
: ProfilePictureWidget(fileId: room.pictureId),
|
||||||
title: Text(isDirect ? room.members!.first.account.nick : room.name),
|
title: Text(
|
||||||
|
(isDirect && room.name == null)
|
||||||
|
? room.members!.map((e) => e.account.nick).join(', ')
|
||||||
|
: room.name!,
|
||||||
|
),
|
||||||
subtitle:
|
subtitle:
|
||||||
subtitle != null
|
subtitle != null
|
||||||
? subtitle!
|
? subtitle!
|
||||||
: isDirect
|
: (isDirect && room.description == null)
|
||||||
? Text('@${room.members!.first.account.name}')
|
? Text(room.members!.map((e) => '@${e.account.name}').join(', '))
|
||||||
: Text(room.description),
|
: Text(room.description ?? 'descriptionNone'.tr()),
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
onTap:
|
onTap:
|
||||||
onTap ??
|
onTap ??
|
||||||
@ -82,8 +88,6 @@ Future<List<SnChatRoom>> chatroomsJoined(Ref ref) async {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
final chatFabKey = GlobalKey<ExpandableFabState>();
|
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class ChatListScreen extends HookConsumerWidget {
|
class ChatListScreen extends HookConsumerWidget {
|
||||||
const ChatListScreen({super.key});
|
const ChatListScreen({super.key});
|
||||||
@ -139,69 +143,41 @@ class ChatListScreen extends HookConsumerWidget {
|
|||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: ExpandableFab.location,
|
floatingActionButton: FloatingActionButton(
|
||||||
floatingActionButton: ExpandableFab(
|
onPressed: () {
|
||||||
key: chatFabKey,
|
showModalBottomSheet(
|
||||||
distance: 75,
|
context: context,
|
||||||
type: ExpandableFabType.up,
|
builder:
|
||||||
childrenAnimation: ExpandableFabAnimation.none,
|
(context) => Column(
|
||||||
overlayStyle: ExpandableFabOverlayStyle(
|
mainAxisSize: MainAxisSize.min,
|
||||||
color: Theme.of(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
context,
|
children: [
|
||||||
).colorScheme.surface.withAlpha((255 * 0.5).round()),
|
ListTile(
|
||||||
),
|
title: Text('createChatRoom').tr(),
|
||||||
openButtonBuilder: RotateFloatingActionButtonBuilder(
|
leading: const Icon(Symbols.add),
|
||||||
child: const Icon(Icons.add),
|
onTap: () {
|
||||||
fabSize: ExpandableFabSize.regular,
|
Navigator.pop(context);
|
||||||
foregroundColor:
|
context.pushRoute(NewChatRoute()).then((value) {
|
||||||
Theme.of(context).floatingActionButtonTheme.foregroundColor,
|
if (value != null) {
|
||||||
backgroundColor:
|
ref.invalidate(chatroomsJoinedProvider);
|
||||||
Theme.of(context).floatingActionButtonTheme.backgroundColor,
|
}
|
||||||
),
|
});
|
||||||
closeButtonBuilder: DefaultFloatingActionButtonBuilder(
|
},
|
||||||
child: const Icon(Icons.close),
|
),
|
||||||
fabSize: ExpandableFabSize.regular,
|
ListTile(
|
||||||
foregroundColor:
|
title: Text('createDirectMessage').tr(),
|
||||||
Theme.of(context).floatingActionButtonTheme.foregroundColor,
|
leading: const Icon(Symbols.person),
|
||||||
backgroundColor:
|
onTap: () {
|
||||||
Theme.of(context).floatingActionButtonTheme.backgroundColor,
|
Navigator.pop(context);
|
||||||
),
|
createDirectMessage();
|
||||||
children: [
|
},
|
||||||
Row(
|
),
|
||||||
children: [
|
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||||
Text('createChatRoom').tr(),
|
],
|
||||||
const Gap(20),
|
),
|
||||||
FloatingActionButton(
|
);
|
||||||
heroTag: null,
|
},
|
||||||
tooltip: 'createChatRoom'.tr(),
|
child: const Icon(Symbols.add),
|
||||||
onPressed: () {
|
|
||||||
chatFabKey.currentState?.toggle();
|
|
||||||
context.pushRoute(NewChatRoute()).then((value) {
|
|
||||||
if (value != null) {
|
|
||||||
ref.invalidate(chatroomsJoinedProvider);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: const Icon(Symbols.chat_add_on),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text('createDirectMessage').tr(),
|
|
||||||
const Gap(20),
|
|
||||||
FloatingActionButton(
|
|
||||||
heroTag: null,
|
|
||||||
tooltip: 'createDirectMessage'.tr(),
|
|
||||||
onPressed: () {
|
|
||||||
chatFabKey.currentState?.toggle();
|
|
||||||
createDirectMessage();
|
|
||||||
},
|
|
||||||
child: const Icon(Symbols.communication),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: chats.when(
|
body: chats.when(
|
||||||
data:
|
data:
|
||||||
@ -281,8 +257,8 @@ class EditChatScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (chat.value != null) {
|
if (chat.value != null) {
|
||||||
nameController.text = chat.value!.name;
|
nameController.text = chat.value!.name ?? '';
|
||||||
descriptionController.text = chat.value!.description;
|
descriptionController.text = chat.value!.description ?? '';
|
||||||
picture.value = chat.value!.picture;
|
picture.value = chat.value!.picture;
|
||||||
background.value = chat.value!.background;
|
background.value = chat.value!.background;
|
||||||
currentRealm.value = joinedRealms.value?.firstWhereOrNull(
|
currentRealm.value = joinedRealms.value?.firstWhereOrNull(
|
||||||
|
@ -442,10 +442,12 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
height: 26,
|
height: 26,
|
||||||
width: 26,
|
width: 26,
|
||||||
child:
|
child:
|
||||||
room!.type == 1
|
(room!.type == 1 && room.pictureId == null)
|
||||||
? ProfilePictureWidget(
|
? SplitAvatarWidget(
|
||||||
fileId:
|
filesId:
|
||||||
room.members!.first.account.profile.pictureId,
|
room.members!
|
||||||
|
.map((e) => e.account.profile.pictureId)
|
||||||
|
.toList(),
|
||||||
)
|
)
|
||||||
: room.pictureId != null
|
: room.pictureId != null
|
||||||
? ProfilePictureWidget(
|
? ProfilePictureWidget(
|
||||||
@ -454,15 +456,15 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
: CircleAvatar(
|
: CircleAvatar(
|
||||||
child: Text(
|
child: Text(
|
||||||
room.name[0].toUpperCase(),
|
room.name![0].toUpperCase(),
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
room.type == 1
|
(room.type == 1 && room.name == null)
|
||||||
? room.members!.first.account.nick
|
? room.members!.map((e) => e.account.nick).join(', ')
|
||||||
: room.name,
|
: room.name!,
|
||||||
).fontSize(19),
|
).fontSize(19),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -763,7 +765,7 @@ class _ChatInput extends StatelessWidget {
|
|||||||
? 'chatDirectMessageHint'.tr(
|
? 'chatDirectMessageHint'.tr(
|
||||||
args: [chatRoom.members!.first.account.nick],
|
args: [chatRoom.members!.first.account.nick],
|
||||||
)
|
)
|
||||||
: 'chatMessageHint'.tr(args: [chatRoom.name]),
|
: 'chatMessageHint'.tr(args: [chatRoom.name!]),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
@ -54,14 +54,20 @@ class ChatDetailScreen extends HookConsumerWidget {
|
|||||||
leading: PageBackButton(shadows: [iconShadow]),
|
leading: PageBackButton(shadows: [iconShadow]),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background:
|
background:
|
||||||
currentRoom!.type == 1 &&
|
(currentRoom!.type == 1 &&
|
||||||
|
currentRoom.backgroundId != null)
|
||||||
|
? CloudImageWidget(
|
||||||
|
fileId: currentRoom.backgroundId!,
|
||||||
|
)
|
||||||
|
: (currentRoom.type == 1 &&
|
||||||
|
currentRoom.members!.length == 1 &&
|
||||||
currentRoom
|
currentRoom
|
||||||
.members!
|
.members!
|
||||||
.first
|
.first
|
||||||
.account
|
.account
|
||||||
.profile
|
.profile
|
||||||
.backgroundId !=
|
.backgroundId !=
|
||||||
null
|
null)
|
||||||
? CloudImageWidget(
|
? CloudImageWidget(
|
||||||
fileId:
|
fileId:
|
||||||
currentRoom
|
currentRoom
|
||||||
@ -81,9 +87,11 @@ class ChatDetailScreen extends HookConsumerWidget {
|
|||||||
Theme.of(context).appBarTheme.backgroundColor,
|
Theme.of(context).appBarTheme.backgroundColor,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
currentRoom.type == 1
|
(currentRoom.type == 1 && currentRoom.name == null)
|
||||||
? currentRoom.members!.first.account.nick
|
? currentRoom.members!
|
||||||
: currentRoom.name,
|
.map((e) => e.account.name)
|
||||||
|
.join(', ')
|
||||||
|
: currentRoom.name!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).appBarTheme.foregroundColor,
|
color: Theme.of(context).appBarTheme.foregroundColor,
|
||||||
shadows: [iconShadow],
|
shadows: [iconShadow],
|
||||||
@ -114,7 +122,7 @@ class ChatDetailScreen extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
currentRoom.description,
|
currentRoom.description ?? 'descriptionNone'.tr(),
|
||||||
style: const TextStyle(fontSize: 16),
|
style: const TextStyle(fontSize: 16),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -73,7 +73,11 @@ class RealmListScreen extends HookConsumerWidget {
|
|||||||
heroTag: Key("realms-page-fab"),
|
heroTag: Key("realms-page-fab"),
|
||||||
child: const Icon(Symbols.add),
|
child: const Icon(Symbols.add),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.router.push(NewRealmRoute());
|
context.router.push(NewRealmRoute()).then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
ref.invalidate(realmsJoinedProvider);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
|
@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:island/models/wallet.dart';
|
import 'package:island/models/wallet.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
@ -149,7 +150,11 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
error: (error, stackTrace) => Center(child: Text('Error: $error')),
|
error:
|
||||||
|
(error, stackTrace) => ResponseErrorWidget(
|
||||||
|
error: error,
|
||||||
|
onRetry: () => ref.invalidate(walletCurrentProvider),
|
||||||
|
),
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -64,6 +64,14 @@ class CloudImageWidget extends ConsumerWidget {
|
|||||||
child: UniversalImage(uri: uri, blurHash: blurHash),
|
child: UniversalImage(uri: uri, blurHash: blurHash),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ImageProvider provider({
|
||||||
|
required String fileId,
|
||||||
|
required String serverUrl,
|
||||||
|
}) {
|
||||||
|
final uri = '$serverUrl/files/$fileId';
|
||||||
|
return CachedNetworkImageProvider(uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProfilePictureWidget extends ConsumerWidget {
|
class ProfilePictureWidget extends ConsumerWidget {
|
||||||
@ -104,3 +112,153 @@ class ProfilePictureWidget extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SplitAvatarWidget extends ConsumerWidget {
|
||||||
|
final List<String?> filesId;
|
||||||
|
final double radius;
|
||||||
|
final IconData fallbackIcon;
|
||||||
|
final Color? fallbackColor;
|
||||||
|
|
||||||
|
const SplitAvatarWidget({
|
||||||
|
super.key,
|
||||||
|
required this.filesId,
|
||||||
|
this.radius = 20,
|
||||||
|
this.fallbackIcon = Symbols.account_circle,
|
||||||
|
this.fallbackColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
if (filesId.isEmpty) {
|
||||||
|
return ProfilePictureWidget(
|
||||||
|
fileId: null,
|
||||||
|
radius: radius,
|
||||||
|
fallbackIcon: fallbackIcon,
|
||||||
|
fallbackColor: fallbackColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (filesId.length == 1) {
|
||||||
|
return ProfilePictureWidget(
|
||||||
|
fileId: filesId[0],
|
||||||
|
radius: radius,
|
||||||
|
fallbackIcon: fallbackIcon,
|
||||||
|
fallbackColor: fallbackColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(radius)),
|
||||||
|
child: Container(
|
||||||
|
width: radius * 2,
|
||||||
|
height: radius * 2,
|
||||||
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
if (filesId.length == 2)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildQuadrant(context, filesId[0], ref, radius),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildQuadrant(context, filesId[1], ref, radius),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else if (filesId.length == 3)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildQuadrant(context, filesId[0], ref, radius),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildQuadrant(context, filesId[1], ref, radius),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildQuadrant(context, filesId[2], ref, radius),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else ...[
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
child: _buildQuadrant(context, filesId[0], ref, radius),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: _buildQuadrant(context, filesId[1], ref, radius),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
child: _buildQuadrant(context, filesId[2], ref, radius),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
child:
|
||||||
|
filesId.length > 4
|
||||||
|
? Container(
|
||||||
|
width: radius,
|
||||||
|
height: radius,
|
||||||
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'+${filesId.length - 3}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: radius * 0.4,
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: _buildQuadrant(context, filesId[3], ref, radius),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildQuadrant(
|
||||||
|
BuildContext context,
|
||||||
|
String? fileId,
|
||||||
|
WidgetRef ref,
|
||||||
|
double radius,
|
||||||
|
) {
|
||||||
|
if (fileId == null) {
|
||||||
|
return Container(
|
||||||
|
width: radius,
|
||||||
|
height: radius,
|
||||||
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
child:
|
||||||
|
Icon(
|
||||||
|
fallbackIcon,
|
||||||
|
size: radius * 0.6,
|
||||||
|
color:
|
||||||
|
fallbackColor ??
|
||||||
|
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
|
).center(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final serverUrl = ref.watch(serverUrlProvider);
|
||||||
|
final uri = '$serverUrl/files/$fileId';
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: radius,
|
||||||
|
height: radius,
|
||||||
|
child: CachedNetworkImage(imageUrl: uri, fit: BoxFit.cover),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
class ResponseErrorWidget extends StatelessWidget {
|
class ResponseErrorWidget extends StatelessWidget {
|
||||||
final Error error;
|
final dynamic error;
|
||||||
final VoidCallback onRetry;
|
final VoidCallback onRetry;
|
||||||
const ResponseErrorWidget({
|
const ResponseErrorWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@ -18,13 +18,13 @@ class ResponseErrorWidget extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Symbols.error_outline, size: 48),
|
const Icon(Symbols.error_outline, size: 48),
|
||||||
const Gap(16),
|
const Gap(4),
|
||||||
Text(
|
Text(
|
||||||
error.toString(),
|
error.toString(),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(color: Color(0xFF757575)),
|
style: const TextStyle(color: Color(0xFF757575)),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(8),
|
||||||
TextButton(onPressed: onRetry, child: const Text('retry').tr()),
|
TextButton(onPressed: onRetry, child: const Text('retry').tr()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
16
pubspec.lock
16
pubspec.lock
@ -81,6 +81,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.1"
|
version: "10.0.1"
|
||||||
|
avatar_stack:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: avatar_stack
|
||||||
|
sha256: "354527ba139956fd6439e2c49199d8298d72afdaa6c4cd6f37f26b97faf21f7e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
bitsdojo_window:
|
bitsdojo_window:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -630,14 +638,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
flutter_expandable_fab:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_expandable_fab
|
|
||||||
sha256: c2936d398169166064d025df91a3bb417109a859e725d9b80c6ef7f04e01b6ab
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.1"
|
|
||||||
flutter_highlight:
|
flutter_highlight:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -88,7 +88,6 @@ dependencies:
|
|||||||
drift_flutter: ^0.2.4
|
drift_flutter: ^0.2.4
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
collection: ^1.19.1
|
collection: ^1.19.1
|
||||||
flutter_expandable_fab: ^2.5.0
|
|
||||||
markdown_editor_plus: ^0.2.15
|
markdown_editor_plus: ^0.2.15
|
||||||
croppy: ^1.3.6
|
croppy: ^1.3.6
|
||||||
table_calendar: ^3.1.3
|
table_calendar: ^3.1.3
|
||||||
@ -96,6 +95,7 @@ dependencies:
|
|||||||
dropdown_button2: ^2.3.9
|
dropdown_button2: ^2.3.9
|
||||||
riverpod_paging_utils: ^0.8.0
|
riverpod_paging_utils: ^0.8.0
|
||||||
crypto: ^3.0.6
|
crypto: ^3.0.6
|
||||||
|
avatar_stack: ^3.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user