Compare commits

...

2 Commits

Author SHA1 Message Date
285bb42b09 Basic message sending and listing 2024-11-17 01:16:54 +08:00
e9fbd0c65f Chat listing 2024-11-16 21:15:55 +08:00
26 changed files with 2580 additions and 500 deletions

View File

@ -47,6 +47,8 @@ PODS:
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- isar_flutter_libs (1.0.0):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
@ -72,6 +74,7 @@ DEPENDENCIES:
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
@ -101,6 +104,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
isar_flutter_libs:
:path: ".symlinks/plugins/isar_flutter_libs/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
@ -121,6 +126,7 @@ SPEC CHECKSUMS:
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78

View File

@ -0,0 +1,183 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/chat.dart';
import 'package:uuid/uuid.dart';
class ChatMessageController extends ChangeNotifier {
static const kChatMessageBoxPrefix = 'nex_chat_messages_';
static const kSingleBatchLoadLimit = 100;
late final SnNetworkProvider _sn;
late final UserDirectoryProvider _ud;
ChatMessageController(BuildContext context) {
_sn = context.read<SnNetworkProvider>();
_ud = context.read<UserDirectoryProvider>();
}
bool isPending = true;
bool isLoading = false;
int? messageTotal;
bool get isAllLoaded =>
messageTotal != null && messages.length >= messageTotal!;
String? _boxKey;
SnChannel? _channel;
List<SnChatMessage> messages = List.empty(growable: true);
Box<SnChatMessage>? get _box =>
(_boxKey == null || isPending) ? null : Hive.box<SnChatMessage>(_boxKey!);
Future<void> initialize(SnChannel channel) async {
_channel = channel;
_boxKey = '$kChatMessageBoxPrefix${channel.id}';
await Hive.openBox<SnChatMessage>(_boxKey!);
isPending = false;
notifyListeners();
}
Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async {
if (_box == null) return;
await _box!.putAll({
for (final message in messages) message.id: message,
});
}
Future<void> _addMessage(SnChatMessage message) async {
messages.add(message);
notifyListeners();
if (_box == null) return;
await _box!.put(message.id, message);
}
Future<void> sendMessage(
String type,
String content, {
int? quoteId,
int? relatedId,
List<String>? attachments,
}) async {
if (_channel == null) return;
const uuid = Uuid();
final nonce = uuid.v4();
final resp = await _sn.client.post(
'/cgi/im/channels/${_channel!.keyPath}/messages',
data: {
'type': type,
'uuid': nonce,
'body': {
'text': content,
'algorithm': 'plain',
if (quoteId != null) 'quote_id': quoteId,
if (relatedId != null) 'related_id': relatedId,
if (attachments != null) 'attachments': attachments,
},
},
);
final out = SnChatMessage.fromJson(
resp.data['data'] as Map<String, dynamic>,
);
await _addMessage(out);
}
/// Check the local storage is up to date with the server.
/// If the local storage is not up to date, it will be updated.
Future<void> checkUpdate() async {
if (_box == null) return;
if (_box!.isEmpty) return;
isLoading = true;
notifyListeners();
try {
final resp = await _sn.client.get(
'/cgi/im/channels/${_channel!.keyPath}/events/update',
queryParameters: {
'pivot': _box!.values.last.id,
},
);
if (resp.data['up_to_date'] == true) {
await loadMessages();
return;
}
// Only preload the first 100 messages to prevent first time check update cause load to server and waste local storage.
// FIXME If the local is missing more than 100 messages, it won't be fetched, this is a problem, we need to fix it.
final countToFetch = math.min(resp.data['count'] as int, 100);
for (int idx = 0; idx < countToFetch; idx += kSingleBatchLoadLimit) {
await getMessages(kSingleBatchLoadLimit, idx);
}
} catch (err) {
rethrow;
} finally {
isLoading = false;
notifyListeners();
}
}
/// Get message from local storage first, then from the server.
/// Will not check local storage is up to date with the server.
/// If you need to do the sync, do the `checkUpdate` instead.
Future<List<SnChatMessage>> getMessages(
int take,
int offset, {
bool forceLocal = false,
}) async {
if (_box != null) {
// Try retrieve these messages from the local storage
if (_box!.length >= take + offset || forceLocal) {
return _box!.values.skip(offset).take(take).toList();
}
}
final resp = await _sn.client.get(
'/cgi/im/channels/${_channel!.keyPath}/events',
queryParameters: {
'take': take,
'offset': offset,
},
);
messageTotal = resp.data['count'] as int?;
final out = List<SnChatMessage>.from(
resp.data['data']?.map((e) => SnChatMessage.fromJson(e)) ?? [],
);
_saveMessageToLocal(out);
// Preload sender accounts
await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet());
return out;
}
/// The load messages method work as same as the `getMessages` method.
/// But it won't return the messages instead append them to the value that controller has.
/// At the same time, this method provide the `isLoading` state.
/// The `skip` parameter is no longer required since it will skip the messages count that already loaded.
Future<void> loadMessages({int take = 20}) async {
isLoading = true;
notifyListeners();
try {
final out = await getMessages(take, messages.length);
messages.addAll(out);
} catch (err) {
rethrow;
} finally {
isLoading = false;
notifyListeners();
}
}
close() {
_box?.close();
}
}

View File

@ -7,19 +7,27 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:provider/provider.dart';
import 'package:relative_time/relative_time.dart';
import 'package:responsive_framework/responsive_framework.dart';
import 'package:surface/providers/channel.dart';
import 'package:surface/providers/navigation.dart';
import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/theme.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/providers/userinfo.dart';
import 'package:surface/providers/websocket.dart';
import 'package:surface/router.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/types/realm.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
await Hive.initFlutter();
Hive.registerAdapter(SnChannelImplAdapter());
Hive.registerAdapter(SnRealmImplAdapter());
Hive.registerAdapter(SnChannelMemberImplAdapter());
Hive.registerAdapter(SnChatMessageImplAdapter());
if (!kReleaseMode) {
debugInvertOversizedImages = true;
@ -50,8 +58,10 @@ class SolianApp extends StatelessWidget {
// Data layer
Provider(create: (_) => SnNetworkProvider()),
Provider(create: (ctx) => SnAttachmentProvider(ctx)),
Provider(create: (ctx) => UserDirectoryProvider(ctx)),
ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)),
ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)),
ChangeNotifierProvider(create: (ctx) => ChatChannelProvider(ctx)),
],
child: AppMainContent(),
),
@ -72,6 +82,7 @@ class AppMainContent extends StatelessWidget {
Widget build(BuildContext context) {
context.read<NavigationProvider>();
context.read<WebSocketProvider>();
context.read<ChatChannelProvider>();
final th = context.watch<ThemeProvider>();

121
lib/providers/channel.dart Normal file
View File

@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/types/realm.dart';
class ChatChannelProvider extends ChangeNotifier {
static const kChatChannelBoxName = 'nex_chat_channels';
late final SnNetworkProvider _sn;
Box<SnChannel>? get _channelBox => Hive.box<SnChannel>(kChatChannelBoxName);
ChatChannelProvider(BuildContext context) {
_sn = context.read<SnNetworkProvider>();
_initializeLocalData();
}
Future<void> _initializeLocalData() async {
await Hive.openBox<SnChannel>(kChatChannelBoxName);
}
Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async {
if (_channelBox == null) return;
await _channelBox!.putAll({
for (final channel in channels) channel.key: channel,
});
}
Future<List<SnChannel>> _fetchChannelsFromServer({
String scope = 'global',
bool direct = false,
bool doNotSave = false,
}) async {
final resp = await _sn.client.get(
'/cgi/im/channels/$scope/me/available',
queryParameters: {
'direct': direct,
},
);
final out = List<SnChannel>.from(
resp.data?.map((e) => SnChannel.fromJson(e)) ?? [],
);
if (!doNotSave) _saveChannelToLocal(out);
return out;
}
/// The get channel method will return the channel with the given alias.
/// It will use the local storage as much as possible.
/// The alias should include the scope, formatted as `scope:alias`.
Future<SnChannel> getChannel(String key) async {
if (_channelBox != null) {
final local = _channelBox!.get(key);
if (local != null) return local;
}
var resp = await _sn.client.get('/cgi/im/channels/$key');
var out = SnChannel.fromJson(resp.data);
// Preload realm of the channel
if (out.realmId != null) {
resp = await _sn.client.get('/cgi/id/realms/${out.realmId}');
out = out.copyWith(realm: SnRealm.fromJson(resp.data));
}
_saveChannelToLocal([out]);
return out;
}
/// The fetch channel method return a stream, which will emit twice.
/// The first time is when the data was fetched from the local storage.
/// And the second time is when the data was fetched from the server.
/// But there is some exception that will only cause one of them to be emitted.
/// Like the local storage is broken or the server is down.
Stream<List<SnChannel>> fetchChannels() async* {
if (_channelBox != null) yield _channelBox!.values.toList();
var resp = await _sn.client.get('/cgi/id/realms/me/available');
final realms = List<SnRealm>.from(
resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
);
final realmMap = {
for (final realm in realms) realm.alias: realm,
};
final scopeToFetch = {'global', ...realms.map((e) => e.alias)};
final List<SnChannel> result = List.empty(growable: true);
final directMessages = await _fetchChannelsFromServer(
scope: scopeToFetch.first,
direct: true,
);
result.addAll(directMessages);
final nonBelongsChannels = await _fetchChannelsFromServer(
scope: scopeToFetch.first,
direct: false,
);
result.addAll(nonBelongsChannels);
for (final scope in scopeToFetch.skip(1)) {
final channel = await _fetchChannelsFromServer(
scope: scope,
direct: false,
doNotSave: true,
);
final out = channel.map((ele) => ele.copyWith(realm: realmMap[scope]));
_saveChannelToLocal(out);
result.addAll(out);
}
yield result;
}
@override
void dispose() {
_channelBox?.close();
super.dispose();
}
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/account.dart';
class UserDirectoryProvider {
late final SnNetworkProvider _sn;
UserDirectoryProvider(BuildContext context) {
_sn = context.read<SnNetworkProvider>();
}
final Map<String, int> _idCache = {};
final Map<int, SnAccount> _cache = {};
Future<List<SnAccount?>> listAccount(Iterable<dynamic> id) async {
final out = await Future.wait(
id.map((e) => getAccount(e)),
);
return out;
}
Future<SnAccount?> getAccount(dynamic id) async {
if (id is String && _idCache.containsKey(id)) {
id = _idCache[id];
}
if (_cache.containsKey(id)) {
return _cache[id];
}
try {
final resp = await _sn.client.get('/cgi/id/users/$id');
final account = SnAccount.fromJson(
resp.data as Map<String, dynamic>,
);
_cache[account.id] = account;
if (id is String) _idCache[id] = account.id;
return account;
} catch (err) {
return null;
}
}
SnAccount? getAccountFromCache(dynamic id) {
if (id is String && _idCache.containsKey(id)) {
id = _idCache[id];
}
return _cache[id];
}
}

View File

@ -10,6 +10,7 @@ import 'package:surface/screens/auth/login.dart';
import 'package:surface/screens/auth/register.dart';
import 'package:surface/screens/chat.dart';
import 'package:surface/screens/chat/manage.dart';
import 'package:surface/screens/chat/room.dart';
import 'package:surface/screens/explore.dart';
import 'package:surface/screens/home.dart';
import 'package:surface/screens/post/post_detail.dart';
@ -102,6 +103,16 @@ final _appRoutes = [
child: const ChatScreen(),
),
routes: [
GoRoute(
path: '/chat/:scope/:alias',
name: 'chatRoom',
builder: (context, state) => AppBackground(
child: ChatRoomScreen(
scope: state.pathParameters['scope']!,
alias: state.pathParameters['alias']!,
),
),
),
GoRoute(
path: '/chat/manage',
name: 'chatManage',

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/channel.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/dialog.dart';
@ -17,36 +17,26 @@ class ChatScreen extends StatefulWidget {
}
class _ChatScreenState extends State<ChatScreen> {
bool _isBusy = false;
bool _isBusy = true;
List<SnChannel>? _channels;
Future<void> _fetchChannels({scope = 'global', direct = false}) async {
setState(() => _isBusy = true);
try {
final sn = context.read<SnNetworkProvider>();
final resp = await sn.client.get(
'/cgi/im/channels/$scope/me/available',
queryParameters: {
'direct': direct,
},
);
_channels = List<SnChannel>.from(
resp.data?.map((e) => SnChannel.fromJson(e)) ?? [],
);
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);
} finally {
setState(() => _isBusy = false);
}
}
@override
void initState() {
super.initState();
_fetchChannels();
final chan = context.read<ChatChannelProvider>();
chan.fetchChannels().listen((channels) {
if (mounted) setState(() => _channels = channels);
})
..onError((err) {
if (!mounted) return;
context.showErrorDialog(err);
setState(() => _isBusy = false);
})
..onDone(() {
if (!mounted) return;
setState(() => _isBusy = false);
});
}
@override
@ -71,12 +61,25 @@ class _ChatScreenState extends State<ChatScreen> {
final channel = _channels![idx];
return ListTile(
title: Text(channel.name),
subtitle: Text(channel.description),
subtitle: Text(
channel.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
leading: AccountImage(
content: null,
fallbackWidget: const Icon(Symbols.chat, size: 20),
),
onTap: () {
GoRouter.of(context).pushNamed(
'chatRoom',
pathParameters: {
'scope': channel.realm?.alias ?? 'global',
'alias': channel.alias,
},
);
},
);
},
),

102
lib/screens/chat/room.dart Normal file
View File

@ -0,0 +1,102 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/chat_message_controller.dart';
import 'package:surface/providers/channel.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/widgets/chat/chat_message.dart';
import 'package:surface/widgets/chat/chat_message_input.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/loading_indicator.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
class ChatRoomScreen extends StatefulWidget {
final String scope;
final String alias;
const ChatRoomScreen({super.key, required this.scope, required this.alias});
@override
State<ChatRoomScreen> createState() => _ChatRoomScreenState();
}
class _ChatRoomScreenState extends State<ChatRoomScreen> {
bool _isBusy = false;
SnChannel? _channel;
late final ChatMessageController _messageController;
Future<void> _fetchChannel() async {
setState(() => _isBusy = true);
try {
final chan = context.read<ChatChannelProvider>();
_channel = await chan.getChannel('${widget.scope}:${widget.alias}');
} catch (err) {
if (!mounted) return;
context.showErrorDialog(err);
} finally {
setState(() => _isBusy = false);
}
}
@override
void initState() {
super.initState();
_messageController = ChatMessageController(context);
_fetchChannel().then((_) async {
await _messageController.initialize(_channel!);
await _messageController.checkUpdate();
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_channel?.name ?? 'loading'.tr()),
),
body: ListenableBuilder(
listenable: _messageController,
builder: (context, _) {
return Column(
children: [
LoadingIndicator(isActive: _isBusy),
if (_messageController.isPending)
Expanded(
child: const CircularProgressIndicator().center(),
),
if (!_messageController.isPending)
Expanded(
child: InfiniteList(
hasReachedMax: _messageController.isAllLoaded,
itemCount: _messageController.messages.length,
isLoading: _messageController.isLoading,
onFetchData: () {
_messageController.loadMessages();
},
itemBuilder: (context, idx) {
final message = _messageController.messages[idx];
return ChatMessage(data: message);
},
),
),
if (!_messageController.isPending)
Material(
elevation: 2,
child: ChatMessageInput(controller: _messageController)
.padding(bottom: MediaQuery.of(context).padding.bottom),
),
],
);
},
),
);
}
}

View File

@ -1,29 +1,32 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive_flutter/hive_flutter.dart';
part 'account.freezed.dart';
part 'account.g.dart';
@freezed
class SnAccount with _$SnAccount {
const SnAccount._();
const factory SnAccount({
required int id,
required int? affiliatedId,
required int? affiliatedTo,
required int? automatedBy,
required int? automatedId,
@HiveField(0) required int id,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
required DateTime? confirmedAt,
required List<SnAccountContact>? contacts,
required String avatar,
required String banner,
required DateTime? confirmedAt,
required List<SnAccountContact> contacts,
required DateTime createdAt,
required DateTime? deletedAt,
required String description,
required String name,
required String nick,
required Map<String, dynamic> permNodes,
required SnAccountProfile? profile,
required DateTime? suspendedAt,
required DateTime updatedAt,
required int? affiliatedId,
required int? affiliatedTo,
required int? automatedBy,
required int? automatedId,
}) = _SnAccount;
factory SnAccount.fromJson(Map<String, Object?> json) =>

View File

@ -20,24 +20,25 @@ SnAccount _$SnAccountFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$SnAccount {
@HiveField(0)
int get id => throw _privateConstructorUsedError;
int? get affiliatedId => throw _privateConstructorUsedError;
int? get affiliatedTo => throw _privateConstructorUsedError;
int? get automatedBy => throw _privateConstructorUsedError;
int? get automatedId => throw _privateConstructorUsedError;
DateTime get createdAt => throw _privateConstructorUsedError;
DateTime get updatedAt => throw _privateConstructorUsedError;
DateTime? get deletedAt => throw _privateConstructorUsedError;
DateTime? get confirmedAt => throw _privateConstructorUsedError;
List<SnAccountContact>? get contacts => throw _privateConstructorUsedError;
String get avatar => throw _privateConstructorUsedError;
String get banner => throw _privateConstructorUsedError;
DateTime? get confirmedAt => throw _privateConstructorUsedError;
List<SnAccountContact> get contacts => throw _privateConstructorUsedError;
DateTime get createdAt => throw _privateConstructorUsedError;
DateTime? get deletedAt => throw _privateConstructorUsedError;
String get description => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get nick => throw _privateConstructorUsedError;
Map<String, dynamic> get permNodes => throw _privateConstructorUsedError;
SnAccountProfile? get profile => throw _privateConstructorUsedError;
DateTime? get suspendedAt => throw _privateConstructorUsedError;
DateTime get updatedAt => throw _privateConstructorUsedError;
int? get affiliatedId => throw _privateConstructorUsedError;
int? get affiliatedTo => throw _privateConstructorUsedError;
int? get automatedBy => throw _privateConstructorUsedError;
int? get automatedId => throw _privateConstructorUsedError;
/// Serializes this SnAccount to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -55,24 +56,24 @@ abstract class $SnAccountCopyWith<$Res> {
_$SnAccountCopyWithImpl<$Res, SnAccount>;
@useResult
$Res call(
{int id,
int? affiliatedId,
int? affiliatedTo,
int? automatedBy,
int? automatedId,
{@HiveField(0) int id,
DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt,
DateTime? confirmedAt,
List<SnAccountContact>? contacts,
String avatar,
String banner,
DateTime? confirmedAt,
List<SnAccountContact> contacts,
DateTime createdAt,
DateTime? deletedAt,
String description,
String name,
String nick,
Map<String, dynamic> permNodes,
SnAccountProfile? profile,
DateTime? suspendedAt,
DateTime updatedAt});
int? affiliatedId,
int? affiliatedTo,
int? automatedBy,
int? automatedId});
$SnAccountProfileCopyWith<$Res>? get profile;
}
@ -93,45 +94,49 @@ class _$SnAccountCopyWithImpl<$Res, $Val extends SnAccount>
@override
$Res call({
Object? id = null,
Object? affiliatedId = freezed,
Object? affiliatedTo = freezed,
Object? automatedBy = freezed,
Object? automatedId = freezed,
Object? createdAt = null,
Object? updatedAt = null,
Object? deletedAt = freezed,
Object? confirmedAt = freezed,
Object? contacts = freezed,
Object? avatar = null,
Object? banner = null,
Object? confirmedAt = freezed,
Object? contacts = null,
Object? createdAt = null,
Object? deletedAt = freezed,
Object? description = null,
Object? name = null,
Object? nick = null,
Object? permNodes = null,
Object? profile = freezed,
Object? suspendedAt = freezed,
Object? updatedAt = null,
Object? affiliatedId = freezed,
Object? affiliatedTo = freezed,
Object? automatedBy = freezed,
Object? automatedId = freezed,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
affiliatedId: freezed == affiliatedId
? _value.affiliatedId
: affiliatedId // ignore: cast_nullable_to_non_nullable
as int?,
affiliatedTo: freezed == affiliatedTo
? _value.affiliatedTo
: affiliatedTo // ignore: cast_nullable_to_non_nullable
as int?,
automatedBy: freezed == automatedBy
? _value.automatedBy
: automatedBy // ignore: cast_nullable_to_non_nullable
as int?,
automatedId: freezed == automatedId
? _value.automatedId
: automatedId // ignore: cast_nullable_to_non_nullable
as int?,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
updatedAt: null == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
deletedAt: freezed == deletedAt
? _value.deletedAt
: deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
confirmedAt: freezed == confirmedAt
? _value.confirmedAt
: confirmedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
contacts: freezed == contacts
? _value.contacts
: contacts // ignore: cast_nullable_to_non_nullable
as List<SnAccountContact>?,
avatar: null == avatar
? _value.avatar
: avatar // ignore: cast_nullable_to_non_nullable
@ -140,22 +145,6 @@ class _$SnAccountCopyWithImpl<$Res, $Val extends SnAccount>
? _value.banner
: banner // ignore: cast_nullable_to_non_nullable
as String,
confirmedAt: freezed == confirmedAt
? _value.confirmedAt
: confirmedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
contacts: null == contacts
? _value.contacts
: contacts // ignore: cast_nullable_to_non_nullable
as List<SnAccountContact>,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
deletedAt: freezed == deletedAt
? _value.deletedAt
: deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
@ -180,10 +169,22 @@ class _$SnAccountCopyWithImpl<$Res, $Val extends SnAccount>
? _value.suspendedAt
: suspendedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
updatedAt: null == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
affiliatedId: freezed == affiliatedId
? _value.affiliatedId
: affiliatedId // ignore: cast_nullable_to_non_nullable
as int?,
affiliatedTo: freezed == affiliatedTo
? _value.affiliatedTo
: affiliatedTo // ignore: cast_nullable_to_non_nullable
as int?,
automatedBy: freezed == automatedBy
? _value.automatedBy
: automatedBy // ignore: cast_nullable_to_non_nullable
as int?,
automatedId: freezed == automatedId
? _value.automatedId
: automatedId // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
@ -211,24 +212,24 @@ abstract class _$$SnAccountImplCopyWith<$Res>
@override
@useResult
$Res call(
{int id,
int? affiliatedId,
int? affiliatedTo,
int? automatedBy,
int? automatedId,
{@HiveField(0) int id,
DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt,
DateTime? confirmedAt,
List<SnAccountContact>? contacts,
String avatar,
String banner,
DateTime? confirmedAt,
List<SnAccountContact> contacts,
DateTime createdAt,
DateTime? deletedAt,
String description,
String name,
String nick,
Map<String, dynamic> permNodes,
SnAccountProfile? profile,
DateTime? suspendedAt,
DateTime updatedAt});
int? affiliatedId,
int? affiliatedTo,
int? automatedBy,
int? automatedId});
@override
$SnAccountProfileCopyWith<$Res>? get profile;
@ -248,45 +249,49 @@ class __$$SnAccountImplCopyWithImpl<$Res>
@override
$Res call({
Object? id = null,
Object? affiliatedId = freezed,
Object? affiliatedTo = freezed,
Object? automatedBy = freezed,
Object? automatedId = freezed,
Object? createdAt = null,
Object? updatedAt = null,
Object? deletedAt = freezed,
Object? confirmedAt = freezed,
Object? contacts = freezed,
Object? avatar = null,
Object? banner = null,
Object? confirmedAt = freezed,
Object? contacts = null,
Object? createdAt = null,
Object? deletedAt = freezed,
Object? description = null,
Object? name = null,
Object? nick = null,
Object? permNodes = null,
Object? profile = freezed,
Object? suspendedAt = freezed,
Object? updatedAt = null,
Object? affiliatedId = freezed,
Object? affiliatedTo = freezed,
Object? automatedBy = freezed,
Object? automatedId = freezed,
}) {
return _then(_$SnAccountImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
affiliatedId: freezed == affiliatedId
? _value.affiliatedId
: affiliatedId // ignore: cast_nullable_to_non_nullable
as int?,
affiliatedTo: freezed == affiliatedTo
? _value.affiliatedTo
: affiliatedTo // ignore: cast_nullable_to_non_nullable
as int?,
automatedBy: freezed == automatedBy
? _value.automatedBy
: automatedBy // ignore: cast_nullable_to_non_nullable
as int?,
automatedId: freezed == automatedId
? _value.automatedId
: automatedId // ignore: cast_nullable_to_non_nullable
as int?,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
updatedAt: null == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
deletedAt: freezed == deletedAt
? _value.deletedAt
: deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
confirmedAt: freezed == confirmedAt
? _value.confirmedAt
: confirmedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
contacts: freezed == contacts
? _value._contacts
: contacts // ignore: cast_nullable_to_non_nullable
as List<SnAccountContact>?,
avatar: null == avatar
? _value.avatar
: avatar // ignore: cast_nullable_to_non_nullable
@ -295,22 +300,6 @@ class __$$SnAccountImplCopyWithImpl<$Res>
? _value.banner
: banner // ignore: cast_nullable_to_non_nullable
as String,
confirmedAt: freezed == confirmedAt
? _value.confirmedAt
: confirmedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
contacts: null == contacts
? _value._contacts
: contacts // ignore: cast_nullable_to_non_nullable
as List<SnAccountContact>,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
deletedAt: freezed == deletedAt
? _value.deletedAt
: deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
@ -335,71 +324,81 @@ class __$$SnAccountImplCopyWithImpl<$Res>
? _value.suspendedAt
: suspendedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
updatedAt: null == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
affiliatedId: freezed == affiliatedId
? _value.affiliatedId
: affiliatedId // ignore: cast_nullable_to_non_nullable
as int?,
affiliatedTo: freezed == affiliatedTo
? _value.affiliatedTo
: affiliatedTo // ignore: cast_nullable_to_non_nullable
as int?,
automatedBy: freezed == automatedBy
? _value.automatedBy
: automatedBy // ignore: cast_nullable_to_non_nullable
as int?,
automatedId: freezed == automatedId
? _value.automatedId
: automatedId // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SnAccountImpl implements _SnAccount {
class _$SnAccountImpl extends _SnAccount {
const _$SnAccountImpl(
{required this.id,
required this.affiliatedId,
required this.affiliatedTo,
required this.automatedBy,
required this.automatedId,
{@HiveField(0) required this.id,
required this.createdAt,
required this.updatedAt,
required this.deletedAt,
required this.confirmedAt,
required final List<SnAccountContact>? contacts,
required this.avatar,
required this.banner,
required this.confirmedAt,
required final List<SnAccountContact> contacts,
required this.createdAt,
required this.deletedAt,
required this.description,
required this.name,
required this.nick,
required final Map<String, dynamic> permNodes,
required this.profile,
required this.suspendedAt,
required this.updatedAt})
required this.affiliatedId,
required this.affiliatedTo,
required this.automatedBy,
required this.automatedId})
: _contacts = contacts,
_permNodes = permNodes;
_permNodes = permNodes,
super._();
factory _$SnAccountImpl.fromJson(Map<String, dynamic> json) =>
_$$SnAccountImplFromJson(json);
@override
@HiveField(0)
final int id;
@override
final int? affiliatedId;
final DateTime createdAt;
@override
final int? affiliatedTo;
final DateTime updatedAt;
@override
final int? automatedBy;
final DateTime? deletedAt;
@override
final int? automatedId;
final DateTime? confirmedAt;
final List<SnAccountContact>? _contacts;
@override
List<SnAccountContact>? get contacts {
final value = _contacts;
if (value == null) return null;
if (_contacts is EqualUnmodifiableListView) return _contacts;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
final String avatar;
@override
final String banner;
@override
final DateTime? confirmedAt;
final List<SnAccountContact> _contacts;
@override
List<SnAccountContact> get contacts {
if (_contacts is EqualUnmodifiableListView) return _contacts;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_contacts);
}
@override
final DateTime createdAt;
@override
final DateTime? deletedAt;
@override
final String description;
@override
final String name;
@ -418,11 +417,17 @@ class _$SnAccountImpl implements _SnAccount {
@override
final DateTime? suspendedAt;
@override
final DateTime updatedAt;
final int? affiliatedId;
@override
final int? affiliatedTo;
@override
final int? automatedBy;
@override
final int? automatedId;
@override
String toString() {
return 'SnAccount(id: $id, affiliatedId: $affiliatedId, affiliatedTo: $affiliatedTo, automatedBy: $automatedBy, automatedId: $automatedId, avatar: $avatar, banner: $banner, confirmedAt: $confirmedAt, contacts: $contacts, createdAt: $createdAt, deletedAt: $deletedAt, description: $description, name: $name, nick: $nick, permNodes: $permNodes, profile: $profile, suspendedAt: $suspendedAt, updatedAt: $updatedAt)';
return 'SnAccount(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, confirmedAt: $confirmedAt, contacts: $contacts, avatar: $avatar, banner: $banner, description: $description, name: $name, nick: $nick, permNodes: $permNodes, profile: $profile, suspendedAt: $suspendedAt, affiliatedId: $affiliatedId, affiliatedTo: $affiliatedTo, automatedBy: $automatedBy, automatedId: $automatedId)';
}
@override
@ -431,23 +436,17 @@ class _$SnAccountImpl implements _SnAccount {
(other.runtimeType == runtimeType &&
other is _$SnAccountImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.affiliatedId, affiliatedId) ||
other.affiliatedId == affiliatedId) &&
(identical(other.affiliatedTo, affiliatedTo) ||
other.affiliatedTo == affiliatedTo) &&
(identical(other.automatedBy, automatedBy) ||
other.automatedBy == automatedBy) &&
(identical(other.automatedId, automatedId) ||
other.automatedId == automatedId) &&
(identical(other.avatar, avatar) || other.avatar == avatar) &&
(identical(other.banner, banner) || other.banner == banner) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt) &&
(identical(other.deletedAt, deletedAt) ||
other.deletedAt == deletedAt) &&
(identical(other.confirmedAt, confirmedAt) ||
other.confirmedAt == confirmedAt) &&
const DeepCollectionEquality().equals(other._contacts, _contacts) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.deletedAt, deletedAt) ||
other.deletedAt == deletedAt) &&
(identical(other.avatar, avatar) || other.avatar == avatar) &&
(identical(other.banner, banner) || other.banner == banner) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.name, name) || other.name == name) &&
@ -457,8 +456,14 @@ class _$SnAccountImpl implements _SnAccount {
(identical(other.profile, profile) || other.profile == profile) &&
(identical(other.suspendedAt, suspendedAt) ||
other.suspendedAt == suspendedAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt));
(identical(other.affiliatedId, affiliatedId) ||
other.affiliatedId == affiliatedId) &&
(identical(other.affiliatedTo, affiliatedTo) ||
other.affiliatedTo == affiliatedTo) &&
(identical(other.automatedBy, automatedBy) ||
other.automatedBy == automatedBy) &&
(identical(other.automatedId, automatedId) ||
other.automatedId == automatedId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@ -466,23 +471,23 @@ class _$SnAccountImpl implements _SnAccount {
int get hashCode => Object.hash(
runtimeType,
id,
affiliatedId,
affiliatedTo,
automatedBy,
automatedId,
avatar,
banner,
createdAt,
updatedAt,
deletedAt,
confirmedAt,
const DeepCollectionEquality().hash(_contacts),
createdAt,
deletedAt,
avatar,
banner,
description,
name,
nick,
const DeepCollectionEquality().hash(_permNodes),
profile,
suspendedAt,
updatedAt);
affiliatedId,
affiliatedTo,
automatedBy,
automatedId);
/// Create a copy of SnAccount
/// with the given fields replaced by the non-null parameter values.
@ -500,53 +505,49 @@ class _$SnAccountImpl implements _SnAccount {
}
}
abstract class _SnAccount implements SnAccount {
abstract class _SnAccount extends SnAccount {
const factory _SnAccount(
{required final int id,
required final int? affiliatedId,
required final int? affiliatedTo,
required final int? automatedBy,
required final int? automatedId,
{@HiveField(0) required final int id,
required final DateTime createdAt,
required final DateTime updatedAt,
required final DateTime? deletedAt,
required final DateTime? confirmedAt,
required final List<SnAccountContact>? contacts,
required final String avatar,
required final String banner,
required final DateTime? confirmedAt,
required final List<SnAccountContact> contacts,
required final DateTime createdAt,
required final DateTime? deletedAt,
required final String description,
required final String name,
required final String nick,
required final Map<String, dynamic> permNodes,
required final SnAccountProfile? profile,
required final DateTime? suspendedAt,
required final DateTime updatedAt}) = _$SnAccountImpl;
required final int? affiliatedId,
required final int? affiliatedTo,
required final int? automatedBy,
required final int? automatedId}) = _$SnAccountImpl;
const _SnAccount._() : super._();
factory _SnAccount.fromJson(Map<String, dynamic> json) =
_$SnAccountImpl.fromJson;
@override
@HiveField(0)
int get id;
@override
int? get affiliatedId;
DateTime get createdAt;
@override
int? get affiliatedTo;
DateTime get updatedAt;
@override
int? get automatedBy;
DateTime? get deletedAt;
@override
int? get automatedId;
DateTime? get confirmedAt;
@override
List<SnAccountContact>? get contacts;
@override
String get avatar;
@override
String get banner;
@override
DateTime? get confirmedAt;
@override
List<SnAccountContact> get contacts;
@override
DateTime get createdAt;
@override
DateTime? get deletedAt;
@override
String get description;
@override
String get name;
@ -559,7 +560,13 @@ abstract class _SnAccount implements SnAccount {
@override
DateTime? get suspendedAt;
@override
DateTime get updatedAt;
int? get affiliatedId;
@override
int? get affiliatedTo;
@override
int? get automatedBy;
@override
int? get automatedId;
/// Create a copy of SnAccount
/// with the given fields replaced by the non-null parameter values.

View File

@ -9,22 +9,19 @@ part of 'account.dart';
_$SnAccountImpl _$$SnAccountImplFromJson(Map<String, dynamic> json) =>
_$SnAccountImpl(
id: (json['id'] as num).toInt(),
affiliatedId: (json['affiliated_id'] as num?)?.toInt(),
affiliatedTo: (json['affiliated_to'] as num?)?.toInt(),
automatedBy: (json['automated_by'] as num?)?.toInt(),
automatedId: (json['automated_id'] as num?)?.toInt(),
avatar: json['avatar'] as String,
banner: json['banner'] as String,
confirmedAt: json['confirmed_at'] == null
? null
: DateTime.parse(json['confirmed_at'] as String),
contacts: (json['contacts'] as List<dynamic>)
.map((e) => SnAccountContact.fromJson(e as Map<String, dynamic>))
.toList(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
confirmedAt: json['confirmed_at'] == null
? null
: DateTime.parse(json['confirmed_at'] as String),
contacts: (json['contacts'] as List<dynamic>?)
?.map((e) => SnAccountContact.fromJson(e as Map<String, dynamic>))
.toList(),
avatar: json['avatar'] as String,
banner: json['banner'] as String,
description: json['description'] as String,
name: json['name'] as String,
nick: json['nick'] as String,
@ -35,29 +32,32 @@ _$SnAccountImpl _$$SnAccountImplFromJson(Map<String, dynamic> json) =>
suspendedAt: json['suspended_at'] == null
? null
: DateTime.parse(json['suspended_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
affiliatedId: (json['affiliated_id'] as num?)?.toInt(),
affiliatedTo: (json['affiliated_to'] as num?)?.toInt(),
automatedBy: (json['automated_by'] as num?)?.toInt(),
automatedId: (json['automated_id'] as num?)?.toInt(),
);
Map<String, dynamic> _$$SnAccountImplToJson(_$SnAccountImpl instance) =>
<String, dynamic>{
'id': instance.id,
'affiliated_id': instance.affiliatedId,
'affiliated_to': instance.affiliatedTo,
'automated_by': instance.automatedBy,
'automated_id': instance.automatedId,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'confirmed_at': instance.confirmedAt?.toIso8601String(),
'contacts': instance.contacts?.map((e) => e.toJson()).toList(),
'avatar': instance.avatar,
'banner': instance.banner,
'confirmed_at': instance.confirmedAt?.toIso8601String(),
'contacts': instance.contacts.map((e) => e.toJson()).toList(),
'created_at': instance.createdAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'description': instance.description,
'name': instance.name,
'nick': instance.nick,
'perm_nodes': instance.permNodes,
'profile': instance.profile?.toJson(),
'suspended_at': instance.suspendedAt?.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'affiliated_id': instance.affiliatedId,
'affiliated_to': instance.affiliatedTo,
'automated_by': instance.automatedBy,
'automated_id': instance.automatedId,
};
_$SnAccountContactImpl _$$SnAccountContactImplFromJson(

View File

@ -1,4 +1,6 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:surface/types/account.dart';
import 'package:surface/types/realm.dart';
part 'chat.freezed.dart';
@ -6,25 +8,79 @@ part 'chat.g.dart';
@freezed
class SnChannel with _$SnChannel {
const SnChannel._();
@HiveType(typeId: 2)
const factory SnChannel({
required int id,
required DateTime createdAt,
required DateTime updatedAt,
required dynamic deletedAt,
required String alias,
required String name,
required String description,
required List<dynamic> members,
required dynamic messages,
required dynamic calls,
required int type,
required int accountId,
required bool isPublic,
required bool isCommunity,
required SnRealm? realm,
required int? realmId,
@HiveField(0) required int id,
@HiveField(1) required DateTime createdAt,
@HiveField(2) required DateTime updatedAt,
@HiveField(3) required dynamic deletedAt,
@HiveField(4) required String alias,
@HiveField(5) required String name,
@HiveField(6) required String description,
@HiveField(7) required List<dynamic>? members,
List<SnChatMessage>? messages,
dynamic calls,
@HiveField(8) required int type,
@HiveField(9) required int accountId,
@HiveField(10) required SnRealm? realm,
@HiveField(11) required int? realmId,
@HiveField(12) required bool isPublic,
@HiveField(13) required bool isCommunity,
}) = _SnChannel;
factory SnChannel.fromJson(Map<String, dynamic> json) =>
_$SnChannelFromJson(json);
String get key => '${realm?.alias ?? 'global'}:$alias';
String get keyPath => '${realm?.alias ?? 'global'}/$alias';
}
@freezed
class SnChannelMember with _$SnChannelMember {
const SnChannelMember._();
@HiveType(typeId: 3)
const factory SnChannelMember({
@HiveField(0) required int id,
@HiveField(1) required DateTime createdAt,
@HiveField(2) required DateTime updatedAt,
@HiveField(3) required DateTime? deletedAt,
@HiveField(4) required int channelId,
@HiveField(5) required int accountId,
@HiveField(6) required String? nick,
@HiveField(7) required SnChannel? channel,
@HiveField(8) required SnAccount? account,
@Default(0) int notify,
@HiveField(9) required int powerLevel,
dynamic calls,
dynamic events,
}) = _SnChannelMember;
factory SnChannelMember.fromJson(Map<String, dynamic> json) =>
_$SnChannelMemberFromJson(json);
}
@freezed
class SnChatMessage with _$SnChatMessage {
const SnChatMessage._();
@HiveType(typeId: 4)
const factory SnChatMessage({
@HiveField(0) required int id,
@HiveField(1) required DateTime createdAt,
@HiveField(2) required DateTime updatedAt,
@HiveField(3) required DateTime? deletedAt,
@HiveField(4) required String uuid,
@HiveField(5) required Map<String, dynamic> body,
@HiveField(6) required String type,
@HiveField(7) required SnChannel channel,
@HiveField(8) required SnChannelMember sender,
@HiveField(9) required int channelId,
@HiveField(10) required int senderId,
}) = _SnChatMessage;
factory SnChatMessage.fromJson(Map<String, dynamic> json) =>
_$SnChatMessageFromJson(json);
}

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,208 @@
part of 'chat.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class SnChannelImplAdapter extends TypeAdapter<_$SnChannelImpl> {
@override
final int typeId = 2;
@override
_$SnChannelImpl read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return _$SnChannelImpl(
id: fields[0] as int,
createdAt: fields[1] as DateTime,
updatedAt: fields[2] as DateTime,
deletedAt: fields[3] as dynamic,
alias: fields[4] as String,
name: fields[5] as String,
description: fields[6] as String,
members: (fields[7] as List?)?.cast<dynamic>(),
type: fields[8] as int,
accountId: fields[9] as int,
realm: fields[10] as SnRealm?,
realmId: fields[11] as int?,
isPublic: fields[12] as bool,
isCommunity: fields[13] as bool,
);
}
@override
void write(BinaryWriter writer, _$SnChannelImpl obj) {
writer
..writeByte(14)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.createdAt)
..writeByte(2)
..write(obj.updatedAt)
..writeByte(3)
..write(obj.deletedAt)
..writeByte(4)
..write(obj.alias)
..writeByte(5)
..write(obj.name)
..writeByte(6)
..write(obj.description)
..writeByte(8)
..write(obj.type)
..writeByte(9)
..write(obj.accountId)
..writeByte(10)
..write(obj.realm)
..writeByte(11)
..write(obj.realmId)
..writeByte(12)
..write(obj.isPublic)
..writeByte(13)
..write(obj.isCommunity)
..writeByte(7)
..write(obj.members);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SnChannelImplAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class SnChannelMemberImplAdapter extends TypeAdapter<_$SnChannelMemberImpl> {
@override
final int typeId = 3;
@override
_$SnChannelMemberImpl read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return _$SnChannelMemberImpl(
id: fields[0] as int,
createdAt: fields[1] as DateTime,
updatedAt: fields[2] as DateTime,
deletedAt: fields[3] as DateTime?,
channelId: fields[4] as int,
accountId: fields[5] as int,
nick: fields[6] as String?,
channel: fields[7] as SnChannel?,
account: fields[8] as SnAccount?,
powerLevel: fields[9] as int,
);
}
@override
void write(BinaryWriter writer, _$SnChannelMemberImpl obj) {
writer
..writeByte(10)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.createdAt)
..writeByte(2)
..write(obj.updatedAt)
..writeByte(3)
..write(obj.deletedAt)
..writeByte(4)
..write(obj.channelId)
..writeByte(5)
..write(obj.accountId)
..writeByte(6)
..write(obj.nick)
..writeByte(7)
..write(obj.channel)
..writeByte(8)
..write(obj.account)
..writeByte(9)
..write(obj.powerLevel);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SnChannelMemberImplAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class SnChatMessageImplAdapter extends TypeAdapter<_$SnChatMessageImpl> {
@override
final int typeId = 4;
@override
_$SnChatMessageImpl read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return _$SnChatMessageImpl(
id: fields[0] as int,
createdAt: fields[1] as DateTime,
updatedAt: fields[2] as DateTime,
deletedAt: fields[3] as DateTime?,
uuid: fields[4] as String,
body: (fields[5] as Map).cast<String, dynamic>(),
type: fields[6] as String,
channel: fields[7] as SnChannel,
sender: fields[8] as SnChannelMember,
channelId: fields[9] as int,
senderId: fields[10] as int,
);
}
@override
void write(BinaryWriter writer, _$SnChatMessageImpl obj) {
writer
..writeByte(11)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.createdAt)
..writeByte(2)
..write(obj.updatedAt)
..writeByte(3)
..write(obj.deletedAt)
..writeByte(4)
..write(obj.uuid)
..writeByte(6)
..write(obj.type)
..writeByte(7)
..write(obj.channel)
..writeByte(8)
..write(obj.sender)
..writeByte(9)
..write(obj.channelId)
..writeByte(10)
..write(obj.senderId)
..writeByte(5)
..write(obj.body);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SnChatMessageImplAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
@ -15,17 +217,19 @@ _$SnChannelImpl _$$SnChannelImplFromJson(Map<String, dynamic> json) =>
alias: json['alias'] as String,
name: json['name'] as String,
description: json['description'] as String,
members: json['members'] as List<dynamic>,
messages: json['messages'],
members: json['members'] as List<dynamic>?,
messages: (json['messages'] as List<dynamic>?)
?.map((e) => SnChatMessage.fromJson(e as Map<String, dynamic>))
.toList(),
calls: json['calls'],
type: (json['type'] as num).toInt(),
accountId: (json['account_id'] as num).toInt(),
isPublic: json['is_public'] as bool,
isCommunity: json['is_community'] as bool,
realm: json['realm'] == null
? null
: SnRealm.fromJson(json['realm'] as Map<String, dynamic>),
realmId: (json['realm_id'] as num?)?.toInt(),
isPublic: json['is_public'] as bool,
isCommunity: json['is_community'] as bool,
);
Map<String, dynamic> _$$SnChannelImplToJson(_$SnChannelImpl instance) =>
@ -38,12 +242,86 @@ Map<String, dynamic> _$$SnChannelImplToJson(_$SnChannelImpl instance) =>
'name': instance.name,
'description': instance.description,
'members': instance.members,
'messages': instance.messages,
'messages': instance.messages?.map((e) => e.toJson()).toList(),
'calls': instance.calls,
'type': instance.type,
'account_id': instance.accountId,
'is_public': instance.isPublic,
'is_community': instance.isCommunity,
'realm': instance.realm?.toJson(),
'realm_id': instance.realmId,
'is_public': instance.isPublic,
'is_community': instance.isCommunity,
};
_$SnChannelMemberImpl _$$SnChannelMemberImplFromJson(
Map<String, dynamic> json) =>
_$SnChannelMemberImpl(
id: (json['id'] as num).toInt(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
channelId: (json['channel_id'] as num).toInt(),
accountId: (json['account_id'] as num).toInt(),
nick: json['nick'] as String?,
channel: json['channel'] == null
? null
: SnChannel.fromJson(json['channel'] as Map<String, dynamic>),
account: json['account'] == null
? null
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
notify: (json['notify'] as num?)?.toInt() ?? 0,
powerLevel: (json['power_level'] as num).toInt(),
calls: json['calls'],
events: json['events'],
);
Map<String, dynamic> _$$SnChannelMemberImplToJson(
_$SnChannelMemberImpl instance) =>
<String, dynamic>{
'id': instance.id,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'channel_id': instance.channelId,
'account_id': instance.accountId,
'nick': instance.nick,
'channel': instance.channel?.toJson(),
'account': instance.account?.toJson(),
'notify': instance.notify,
'power_level': instance.powerLevel,
'calls': instance.calls,
'events': instance.events,
};
_$SnChatMessageImpl _$$SnChatMessageImplFromJson(Map<String, dynamic> json) =>
_$SnChatMessageImpl(
id: (json['id'] as num).toInt(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
uuid: json['uuid'] as String,
body: json['body'] as Map<String, dynamic>,
type: json['type'] as String,
channel: SnChannel.fromJson(json['channel'] as Map<String, dynamic>),
sender: SnChannelMember.fromJson(json['sender'] as Map<String, dynamic>),
channelId: (json['channel_id'] as num).toInt(),
senderId: (json['sender_id'] as num).toInt(),
);
Map<String, dynamic> _$$SnChatMessageImplToJson(_$SnChatMessageImpl instance) =>
<String, dynamic>{
'id': instance.id,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'uuid': instance.uuid,
'body': instance.body,
'type': instance.type,
'channel': instance.channel.toJson(),
'sender': instance.sender.toJson(),
'channel_id': instance.channelId,
'sender_id': instance.senderId,
};

View File

@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:surface/types/account.dart';
part 'realm.freezed.dart';
@ -24,21 +25,24 @@ class SnRealmMember with _$SnRealmMember {
@freezed
class SnRealm with _$SnRealm {
const SnRealm._();
@HiveType(typeId: 1)
const factory SnRealm({
required int id,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
required String alias,
required String name,
required String description,
required List<SnRealmMember>? members,
required String? avatar,
required String? banner,
required Map<String, dynamic>? accessPolicy,
required bool isPublic,
required bool isCommunity,
required int accountId,
@HiveField(0) required int id,
@HiveField(1) required DateTime createdAt,
@HiveField(2) required DateTime updatedAt,
@HiveField(3) required DateTime? deletedAt,
@HiveField(4) required String alias,
@HiveField(5) required String name,
@HiveField(6) required String description,
List<SnRealmMember>? members,
@HiveField(7) required String? avatar,
@HiveField(8) required String? banner,
@HiveField(9) required Map<String, dynamic>? accessPolicy,
@HiveField(10) required int accountId,
@HiveField(11) required bool isPublic,
@HiveField(12) required bool isCommunity,
}) = _SnRealm;
factory SnRealm.fromJson(Map<String, dynamic> json) =>

View File

@ -367,20 +367,33 @@ SnRealm _$SnRealmFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$SnRealm {
@HiveField(0)
int get id => throw _privateConstructorUsedError;
@HiveField(1)
DateTime get createdAt => throw _privateConstructorUsedError;
@HiveField(2)
DateTime get updatedAt => throw _privateConstructorUsedError;
@HiveField(3)
DateTime? get deletedAt => throw _privateConstructorUsedError;
@HiveField(4)
String get alias => throw _privateConstructorUsedError;
@HiveField(5)
String get name => throw _privateConstructorUsedError;
@HiveField(6)
String get description => throw _privateConstructorUsedError;
List<SnRealmMember>? get members => throw _privateConstructorUsedError;
@HiveField(7)
String? get avatar => throw _privateConstructorUsedError;
@HiveField(8)
String? get banner => throw _privateConstructorUsedError;
@HiveField(9)
Map<String, dynamic>? get accessPolicy => throw _privateConstructorUsedError;
bool get isPublic => throw _privateConstructorUsedError;
bool get isCommunity => throw _privateConstructorUsedError;
@HiveField(10)
int get accountId => throw _privateConstructorUsedError;
@HiveField(11)
bool get isPublic => throw _privateConstructorUsedError;
@HiveField(12)
bool get isCommunity => throw _privateConstructorUsedError;
/// Serializes this SnRealm to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -397,20 +410,20 @@ abstract class $SnRealmCopyWith<$Res> {
_$SnRealmCopyWithImpl<$Res, SnRealm>;
@useResult
$Res call(
{int id,
DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt,
String alias,
String name,
String description,
{@HiveField(0) int id,
@HiveField(1) DateTime createdAt,
@HiveField(2) DateTime updatedAt,
@HiveField(3) DateTime? deletedAt,
@HiveField(4) String alias,
@HiveField(5) String name,
@HiveField(6) String description,
List<SnRealmMember>? members,
String? avatar,
String? banner,
Map<String, dynamic>? accessPolicy,
bool isPublic,
bool isCommunity,
int accountId});
@HiveField(7) String? avatar,
@HiveField(8) String? banner,
@HiveField(9) Map<String, dynamic>? accessPolicy,
@HiveField(10) int accountId,
@HiveField(11) bool isPublic,
@HiveField(12) bool isCommunity});
}
/// @nodoc
@ -439,9 +452,9 @@ class _$SnRealmCopyWithImpl<$Res, $Val extends SnRealm>
Object? avatar = freezed,
Object? banner = freezed,
Object? accessPolicy = freezed,
Object? accountId = null,
Object? isPublic = null,
Object? isCommunity = null,
Object? accountId = null,
}) {
return _then(_value.copyWith(
id: null == id
@ -488,6 +501,10 @@ class _$SnRealmCopyWithImpl<$Res, $Val extends SnRealm>
? _value.accessPolicy
: accessPolicy // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
accountId: null == accountId
? _value.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
isPublic: null == isPublic
? _value.isPublic
: isPublic // ignore: cast_nullable_to_non_nullable
@ -496,10 +513,6 @@ class _$SnRealmCopyWithImpl<$Res, $Val extends SnRealm>
? _value.isCommunity
: isCommunity // ignore: cast_nullable_to_non_nullable
as bool,
accountId: null == accountId
? _value.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
@ -512,20 +525,20 @@ abstract class _$$SnRealmImplCopyWith<$Res> implements $SnRealmCopyWith<$Res> {
@override
@useResult
$Res call(
{int id,
DateTime createdAt,
DateTime updatedAt,
DateTime? deletedAt,
String alias,
String name,
String description,
{@HiveField(0) int id,
@HiveField(1) DateTime createdAt,
@HiveField(2) DateTime updatedAt,
@HiveField(3) DateTime? deletedAt,
@HiveField(4) String alias,
@HiveField(5) String name,
@HiveField(6) String description,
List<SnRealmMember>? members,
String? avatar,
String? banner,
Map<String, dynamic>? accessPolicy,
bool isPublic,
bool isCommunity,
int accountId});
@HiveField(7) String? avatar,
@HiveField(8) String? banner,
@HiveField(9) Map<String, dynamic>? accessPolicy,
@HiveField(10) int accountId,
@HiveField(11) bool isPublic,
@HiveField(12) bool isCommunity});
}
/// @nodoc
@ -552,9 +565,9 @@ class __$$SnRealmImplCopyWithImpl<$Res>
Object? avatar = freezed,
Object? banner = freezed,
Object? accessPolicy = freezed,
Object? accountId = null,
Object? isPublic = null,
Object? isCommunity = null,
Object? accountId = null,
}) {
return _then(_$SnRealmImpl(
id: null == id
@ -601,6 +614,10 @@ class __$$SnRealmImplCopyWithImpl<$Res>
? _value._accessPolicy
: accessPolicy // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
accountId: null == accountId
? _value.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
isPublic: null == isPublic
? _value.isPublic
: isPublic // ignore: cast_nullable_to_non_nullable
@ -609,51 +626,56 @@ class __$$SnRealmImplCopyWithImpl<$Res>
? _value.isCommunity
: isCommunity // ignore: cast_nullable_to_non_nullable
as bool,
accountId: null == accountId
? _value.accountId
: accountId // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SnRealmImpl implements _SnRealm {
@HiveType(typeId: 1)
class _$SnRealmImpl extends _SnRealm {
const _$SnRealmImpl(
{required this.id,
required this.createdAt,
required this.updatedAt,
required this.deletedAt,
required this.alias,
required this.name,
required this.description,
required final List<SnRealmMember>? members,
required this.avatar,
required this.banner,
required final Map<String, dynamic>? accessPolicy,
required this.isPublic,
required this.isCommunity,
required this.accountId})
{@HiveField(0) required this.id,
@HiveField(1) required this.createdAt,
@HiveField(2) required this.updatedAt,
@HiveField(3) required this.deletedAt,
@HiveField(4) required this.alias,
@HiveField(5) required this.name,
@HiveField(6) required this.description,
final List<SnRealmMember>? members,
@HiveField(7) required this.avatar,
@HiveField(8) required this.banner,
@HiveField(9) required final Map<String, dynamic>? accessPolicy,
@HiveField(10) required this.accountId,
@HiveField(11) required this.isPublic,
@HiveField(12) required this.isCommunity})
: _members = members,
_accessPolicy = accessPolicy;
_accessPolicy = accessPolicy,
super._();
factory _$SnRealmImpl.fromJson(Map<String, dynamic> json) =>
_$$SnRealmImplFromJson(json);
@override
@HiveField(0)
final int id;
@override
@HiveField(1)
final DateTime createdAt;
@override
@HiveField(2)
final DateTime updatedAt;
@override
@HiveField(3)
final DateTime? deletedAt;
@override
@HiveField(4)
final String alias;
@override
@HiveField(5)
final String name;
@override
@HiveField(6)
final String description;
final List<SnRealmMember>? _members;
@override
@ -666,11 +688,14 @@ class _$SnRealmImpl implements _SnRealm {
}
@override
@HiveField(7)
final String? avatar;
@override
@HiveField(8)
final String? banner;
final Map<String, dynamic>? _accessPolicy;
@override
@HiveField(9)
Map<String, dynamic>? get accessPolicy {
final value = _accessPolicy;
if (value == null) return null;
@ -680,15 +705,18 @@ class _$SnRealmImpl implements _SnRealm {
}
@override
@HiveField(10)
final int accountId;
@override
@HiveField(11)
final bool isPublic;
@override
@HiveField(12)
final bool isCommunity;
@override
final int accountId;
@override
String toString() {
return 'SnRealm(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, avatar: $avatar, banner: $banner, accessPolicy: $accessPolicy, isPublic: $isPublic, isCommunity: $isCommunity, accountId: $accountId)';
return 'SnRealm(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, avatar: $avatar, banner: $banner, accessPolicy: $accessPolicy, accountId: $accountId, isPublic: $isPublic, isCommunity: $isCommunity)';
}
@override
@ -712,12 +740,12 @@ class _$SnRealmImpl implements _SnRealm {
(identical(other.banner, banner) || other.banner == banner) &&
const DeepCollectionEquality()
.equals(other._accessPolicy, _accessPolicy) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId) &&
(identical(other.isPublic, isPublic) ||
other.isPublic == isPublic) &&
(identical(other.isCommunity, isCommunity) ||
other.isCommunity == isCommunity) &&
(identical(other.accountId, accountId) ||
other.accountId == accountId));
other.isCommunity == isCommunity));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@ -735,9 +763,9 @@ class _$SnRealmImpl implements _SnRealm {
avatar,
banner,
const DeepCollectionEquality().hash(_accessPolicy),
accountId,
isPublic,
isCommunity,
accountId);
isCommunity);
/// Create a copy of SnRealm
/// with the given fields replaced by the non-null parameter values.
@ -755,53 +783,67 @@ class _$SnRealmImpl implements _SnRealm {
}
}
abstract class _SnRealm implements SnRealm {
abstract class _SnRealm extends SnRealm {
const factory _SnRealm(
{required final int id,
required final DateTime createdAt,
required final DateTime updatedAt,
required final DateTime? deletedAt,
required final String alias,
required final String name,
required final String description,
required final List<SnRealmMember>? members,
required final String? avatar,
required final String? banner,
required final Map<String, dynamic>? accessPolicy,
required final bool isPublic,
required final bool isCommunity,
required final int accountId}) = _$SnRealmImpl;
{@HiveField(0) required final int id,
@HiveField(1) required final DateTime createdAt,
@HiveField(2) required final DateTime updatedAt,
@HiveField(3) required final DateTime? deletedAt,
@HiveField(4) required final String alias,
@HiveField(5) required final String name,
@HiveField(6) required final String description,
final List<SnRealmMember>? members,
@HiveField(7) required final String? avatar,
@HiveField(8) required final String? banner,
@HiveField(9) required final Map<String, dynamic>? accessPolicy,
@HiveField(10) required final int accountId,
@HiveField(11) required final bool isPublic,
@HiveField(12) required final bool isCommunity}) = _$SnRealmImpl;
const _SnRealm._() : super._();
factory _SnRealm.fromJson(Map<String, dynamic> json) = _$SnRealmImpl.fromJson;
@override
@HiveField(0)
int get id;
@override
@HiveField(1)
DateTime get createdAt;
@override
@HiveField(2)
DateTime get updatedAt;
@override
@HiveField(3)
DateTime? get deletedAt;
@override
@HiveField(4)
String get alias;
@override
@HiveField(5)
String get name;
@override
@HiveField(6)
String get description;
@override
List<SnRealmMember>? get members;
@override
@HiveField(7)
String? get avatar;
@override
@HiveField(8)
String? get banner;
@override
@HiveField(9)
Map<String, dynamic>? get accessPolicy;
@override
@HiveField(10)
int get accountId;
@override
@HiveField(11)
bool get isPublic;
@override
@HiveField(12)
bool get isCommunity;
@override
int get accountId;
/// Create a copy of SnRealm
/// with the given fields replaced by the non-null parameter values.

View File

@ -2,6 +2,80 @@
part of 'realm.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class SnRealmImplAdapter extends TypeAdapter<_$SnRealmImpl> {
@override
final int typeId = 1;
@override
_$SnRealmImpl read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return _$SnRealmImpl(
id: fields[0] as int,
createdAt: fields[1] as DateTime,
updatedAt: fields[2] as DateTime,
deletedAt: fields[3] as DateTime?,
alias: fields[4] as String,
name: fields[5] as String,
description: fields[6] as String,
avatar: fields[7] as String?,
banner: fields[8] as String?,
accessPolicy: (fields[9] as Map?)?.cast<String, dynamic>(),
accountId: fields[10] as int,
isPublic: fields[11] as bool,
isCommunity: fields[12] as bool,
);
}
@override
void write(BinaryWriter writer, _$SnRealmImpl obj) {
writer
..writeByte(13)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.createdAt)
..writeByte(2)
..write(obj.updatedAt)
..writeByte(3)
..write(obj.deletedAt)
..writeByte(4)
..write(obj.alias)
..writeByte(5)
..write(obj.name)
..writeByte(6)
..write(obj.description)
..writeByte(7)
..write(obj.avatar)
..writeByte(8)
..write(obj.banner)
..writeByte(10)
..write(obj.accountId)
..writeByte(11)
..write(obj.isPublic)
..writeByte(12)
..write(obj.isCommunity)
..writeByte(9)
..write(obj.accessPolicy);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SnRealmImplAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
@ -51,9 +125,9 @@ _$SnRealmImpl _$$SnRealmImplFromJson(Map<String, dynamic> json) =>
avatar: json['avatar'] as String?,
banner: json['banner'] as String?,
accessPolicy: json['access_policy'] as Map<String, dynamic>?,
accountId: (json['account_id'] as num).toInt(),
isPublic: json['is_public'] as bool,
isCommunity: json['is_community'] as bool,
accountId: (json['account_id'] as num).toInt(),
);
Map<String, dynamic> _$$SnRealmImplToJson(_$SnRealmImpl instance) =>
@ -69,7 +143,7 @@ Map<String, dynamic> _$$SnRealmImplToJson(_$SnRealmImpl instance) =>
'avatar': instance.avatar,
'banner': instance.banner,
'access_policy': instance.accessPolicy,
'account_id': instance.accountId,
'is_public': instance.isPublic,
'is_community': instance.isCommunity,
'account_id': instance.accountId,
};

View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/markdown_content.dart';
class ChatMessage extends StatelessWidget {
final SnChatMessage data;
const ChatMessage({super.key, required this.data});
@override
Widget build(BuildContext context) {
final ud = context.read<UserDirectoryProvider>();
final user = ud.getAccountFromCache(data.sender.accountId);
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AccountImage(
content: user?.avatar,
),
const Gap(8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
(data.sender.nick?.isNotEmpty ?? false)
? data.sender.nick!
: user!.nick,
).bold(),
if (data.body['text'] != null)
MarkdownTextContent(content: data.body['text']),
],
),
)
],
);
}
}

View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/chat_message_controller.dart';
class ChatMessageInput extends StatefulWidget {
final ChatMessageController controller;
const ChatMessageInput({super.key, required this.controller});
@override
State<ChatMessageInput> createState() => _ChatMessageInputState();
}
class _ChatMessageInputState extends State<ChatMessageInput> {
final TextEditingController _contentController = TextEditingController();
final FocusNode _focusNode = FocusNode();
void _sendMessage() {
widget.controller.sendMessage(
'messages.new',
_contentController.text,
);
_contentController.clear();
}
@override
void dispose() {
_contentController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 72,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Expanded(
child: TextField(
focusNode: _focusNode,
controller: _contentController,
decoration: InputDecoration(
isCollapsed: true,
hintText: 'Type a message...',
border: InputBorder.none,
),
onSubmitted: (_) {
_sendMessage();
_focusNode.requestFocus();
},
),
),
const Gap(8),
IconButton(
onPressed: _sendMessage,
icon: Icon(
Symbols.send,
color: Theme.of(context).colorScheme.primary,
),
visualDensity: const VisualDensity(
horizontal: -4,
vertical: -4,
),
),
],
).padding(horizontal: 16, vertical: 12),
],
),
);
}
}

View File

@ -8,6 +8,7 @@
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
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);

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
flutter_secure_storage_linux
isar_flutter_libs
url_launcher_linux
)

View File

@ -8,6 +8,7 @@ import Foundation
import connectivity_plus
import file_selector_macos
import flutter_secure_storage_macos
import isar_flutter_libs
import path_provider_foundation
import shared_preferences_foundation
import sqflite_darwin
@ -17,6 +18,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))

View File

@ -818,6 +818,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
isar:
dependency: "direct main"
description:
name: isar
sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea"
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
isar_flutter_libs:
dependency: "direct main"
description:
name: isar_flutter_libs
sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
jni:
dependency: transitive
description:
@ -934,10 +950,10 @@ packages:
dependency: "direct main"
description:
name: material_symbols_icons
sha256: "7b723abea4ad37e16fe921f1f1971cbb9b0f66d223a8c99981168a2306416b98"
sha256: "1dea2aef1c83434f832f14341a5ffa1254e76b68e4d90333f95f8a2643bf1024"
url: "https://pub.dev"
source: hosted
version: "4.2791.1"
version: "4.2799.0"
meta:
dependency: transitive
description:

View File

@ -74,6 +74,8 @@ dependencies:
collection: ^1.18.0
mime: ^2.0.0
web_socket_channel: ^3.0.1
isar: ^3.1.0+1
isar_flutter_libs: ^3.1.0+1
hive: ^2.2.3
hive_flutter: ^1.1.0

View File

@ -9,6 +9,7 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
IsarFlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
file_selector_windows
flutter_secure_storage_windows
isar_flutter_libs
url_launcher_windows
)