Compare commits
13 Commits
2.0.0+2
...
835203706d
| Author | SHA1 | Date | |
|---|---|---|---|
| 835203706d | |||
| 0e208cc320 | |||
| ee2cb0c989 | |||
| 37c61a0406 | |||
| fa73a28324 | |||
| d945b103ca | |||
| 8bc0da5188 | |||
| 2e68d227a0 | |||
| b8245b00b6 | |||
| 462e818078 | |||
| e4582b7d25 | |||
| 00eef6e45a | |||
| 9498d428cd |
@@ -9,6 +9,13 @@
|
|||||||
# packages, and plugins designed to encourage good coding practices.
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- "**/*.g.dart"
|
||||||
|
- "**/*.freezed.dart"
|
||||||
|
errors:
|
||||||
|
invalid_annotation_target: ignore # Due to freezed + json_serializable issue, ref https://github.com/rrousselGit/freezed/issues/488#issuecomment-894358980
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
# The lint rules applied to this project can be customized in the
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
"screenSettings": "Settings",
|
"screenSettings": "Settings",
|
||||||
"screenAlbum": "Album",
|
"screenAlbum": "Album",
|
||||||
"screenChat": "Chat",
|
"screenChat": "Chat",
|
||||||
|
"screenChatManage": "Edit Channel",
|
||||||
|
"screenChatNew": "New Channel",
|
||||||
|
"screenRealm": "Realm",
|
||||||
|
"screenRealmManage": "Edit Realm",
|
||||||
|
"screenRealmNew": "New Realm",
|
||||||
"dialogOkay": "Okay",
|
"dialogOkay": "Okay",
|
||||||
"dialogCancel": "Cancel",
|
"dialogCancel": "Cancel",
|
||||||
"dialogConfirm": "Confirm",
|
"dialogConfirm": "Confirm",
|
||||||
@@ -32,6 +37,7 @@
|
|||||||
"next": "Next",
|
"next": "Next",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"apply": "Apply",
|
"apply": "Apply",
|
||||||
|
"cancel": "Cancel",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"preview": "Preview",
|
"preview": "Preview",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
@@ -131,5 +137,25 @@
|
|||||||
"sensitiveContent": "Sensitive Content",
|
"sensitiveContent": "Sensitive Content",
|
||||||
"sensitiveContentCollapsed": "Sensitive content has been collapsed.",
|
"sensitiveContentCollapsed": "Sensitive content has been collapsed.",
|
||||||
"sensitiveContentDescription": "This content has been marked as sensitive, and may not be suitable for all viewers.",
|
"sensitiveContentDescription": "This content has been marked as sensitive, and may not be suitable for all viewers.",
|
||||||
"sensitiveContentReveal": "Reveal"
|
"sensitiveContentReveal": "Reveal",
|
||||||
|
"serverConnecting": "Connecting to server...",
|
||||||
|
"serverDisconnected": "Lost connection from server",
|
||||||
|
"fieldChatAlias": "Channel Alias",
|
||||||
|
"fieldChatAliasHint": "The unique channel alias within the site, used to represent the channel in URL, leave blank to auto generate. Should be URL-Safe.",
|
||||||
|
"fieldChatName": "Name",
|
||||||
|
"fieldChatDescription": "Description",
|
||||||
|
"fieldChatBelongToRealm": "Belongs to",
|
||||||
|
"fieldChatBelongToRealmUnset": "Unset Channel Belongs to Realm",
|
||||||
|
"channelEditingNotice": "You are editing channel {}",
|
||||||
|
"channelDeleted": "Chat channel {} has been deleted." ,
|
||||||
|
"channelDelete": "Delete channel {}",
|
||||||
|
"channelDeleteDescription": "Are you sure you want to delete this channel? This operation is irreversible, all messages in this channel will be permanently deleted.",
|
||||||
|
"fieldRealmAlias": "Realm Alias",
|
||||||
|
"fieldRealmAliasHint": "The unique realm alias within the site, used to represent the realm in URL, leave blank to auto generate. Should be URL-Safe.",
|
||||||
|
"fieldRealmName": "Name",
|
||||||
|
"fieldRealmDescription": "Description",
|
||||||
|
"realmEditingNotice": "You are editing realm {}",
|
||||||
|
"realmDeleted": "Realm {} has been deleted.",
|
||||||
|
"realmDelete": "Delete realm {}",
|
||||||
|
"realmDeleteDescription": "Are you sure you want to delete this realm? This operation is irreversible, all resources (posts, chat channels, publishers, etc) belonging to this realm will be permanently deleted. Be careful and think twice!"
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,11 @@
|
|||||||
"screenSettings": "设置",
|
"screenSettings": "设置",
|
||||||
"screenAlbum": "相册",
|
"screenAlbum": "相册",
|
||||||
"screenChat": "聊天",
|
"screenChat": "聊天",
|
||||||
|
"screenChatManage": "编辑聊天频道",
|
||||||
|
"screenChatNew": "新建聊天频道",
|
||||||
|
"screenRealm": "领域",
|
||||||
|
"screenRealmManage": "编辑领域",
|
||||||
|
"screenRealmNew": "新建领域",
|
||||||
"dialogOkay": "好的",
|
"dialogOkay": "好的",
|
||||||
"dialogCancel": "取消",
|
"dialogCancel": "取消",
|
||||||
"dialogConfirm": "确认",
|
"dialogConfirm": "确认",
|
||||||
@@ -33,6 +38,7 @@
|
|||||||
"next": "下一步",
|
"next": "下一步",
|
||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
"apply": "应用",
|
"apply": "应用",
|
||||||
|
"cancel": "取消",
|
||||||
"create": "创建",
|
"create": "创建",
|
||||||
"preview": "预览",
|
"preview": "预览",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
@@ -131,5 +137,25 @@
|
|||||||
"sensitiveContent": "敏感内容",
|
"sensitiveContent": "敏感内容",
|
||||||
"sensitiveContentCollapsed": "敏感内容已折叠。",
|
"sensitiveContentCollapsed": "敏感内容已折叠。",
|
||||||
"sensitiveContentDescription": "此内容已被标记,可能不适合所有人查看。",
|
"sensitiveContentDescription": "此内容已被标记,可能不适合所有人查看。",
|
||||||
"sensitiveContentReveal": "显示内容"
|
"sensitiveContentReveal": "显示内容",
|
||||||
|
"serverConnecting": "正在连接服务器…",
|
||||||
|
"serverDisconnected": "已与服务器断开连接",
|
||||||
|
"fieldChatAlias": "频道别名",
|
||||||
|
"fieldChatAliasHint": "全站范围内唯一的频道别名,用于在 URL 中表示该频道,留空则自动生成。应遵循 URL-Safe 的原则。",
|
||||||
|
"fieldChatName": "名称",
|
||||||
|
"fieldChatDescription": "描述",
|
||||||
|
"fieldChatBelongToRealm": "所属领域",
|
||||||
|
"fieldChatBelongToRealmUnset": "未设置频道所属领域",
|
||||||
|
"channelEditingNotice": "您正在编辑频道 {}",
|
||||||
|
"channelDeleted": "聊天频道 {} 已被删除" ,
|
||||||
|
"channelDelete": "删除聊天频道 {}",
|
||||||
|
"channelDeleteDescription": "你确定要删除这个聊天频道吗?该操作不可撤销,其频道内的所有消息将被永久删除。",
|
||||||
|
"fieldRealmAlias": "领域别名",
|
||||||
|
"fieldRealmAliasHint": "全站范围内唯一的领域别名,用于在 URL 中表示该领域,留空则自动生成。应遵循 URL-Safe 的原则。",
|
||||||
|
"fieldRealmName": "名称",
|
||||||
|
"fieldRealmDescription": "描述",
|
||||||
|
"realmEditingNotice": "您正在编辑领域 {}",
|
||||||
|
"realmDeleted": "领域 {} 已被删除" ,
|
||||||
|
"realmDelete": "删除领域 {}",
|
||||||
|
"realmDeleteDescription": "你确定要删除这个领域吗?该操作不可撤销,其隶属于该领域的所有资源(帖子、聊天频道、发布者、制品等)都将被永久删除。三思而后行!"
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ PODS:
|
|||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_native_splash (0.0.1):
|
- flutter_native_splash (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_secure_storage (3.3.1):
|
- flutter_secure_storage (6.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
@@ -119,7 +119,7 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||||
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
|
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:easy_localization_loader/easy_localization_loader.dart';
|
import 'package:easy_localization_loader/easy_localization_loader.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:relative_time/relative_time.dart';
|
import 'package:relative_time/relative_time.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
@@ -11,12 +12,15 @@ import 'package:surface/providers/sn_attachment.dart';
|
|||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/theme.dart';
|
import 'package:surface/providers/theme.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/providers/websocket.dart';
|
||||||
import 'package:surface/router.dart';
|
import 'package:surface/router.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
|
await Hive.initFlutter();
|
||||||
|
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
debugInvertOversizedImages = true;
|
debugInvertOversizedImages = true;
|
||||||
}
|
}
|
||||||
@@ -35,14 +39,19 @@ class SolianApp extends StatelessWidget {
|
|||||||
supportedLocales: [Locale('en', 'US'), Locale('zh', 'CN')],
|
supportedLocales: [Locale('en', 'US'), Locale('zh', 'CN')],
|
||||||
fallbackLocale: Locale('en', 'US'),
|
fallbackLocale: Locale('en', 'US'),
|
||||||
useFallbackTranslations: true,
|
useFallbackTranslations: true,
|
||||||
|
useOnlyLangCode: true,
|
||||||
assetLoader: JsonAssetLoader(),
|
assetLoader: JsonAssetLoader(),
|
||||||
child: MultiProvider(
|
child: MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
// Display layer
|
||||||
|
ChangeNotifierProvider(create: (_) => ThemeProvider()),
|
||||||
|
ChangeNotifierProvider(create: (ctx) => NavigationProvider()),
|
||||||
|
|
||||||
|
// Data layer
|
||||||
Provider(create: (_) => SnNetworkProvider()),
|
Provider(create: (_) => SnNetworkProvider()),
|
||||||
Provider(create: (ctx) => SnAttachmentProvider(ctx)),
|
Provider(create: (ctx) => SnAttachmentProvider(ctx)),
|
||||||
ChangeNotifierProvider(create: (ctx) => NavigationProvider()),
|
|
||||||
ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)),
|
ChangeNotifierProvider(create: (ctx) => UserProvider(ctx)),
|
||||||
ChangeNotifierProvider(create: (_) => ThemeProvider()),
|
ChangeNotifierProvider(create: (ctx) => WebSocketProvider(ctx)),
|
||||||
],
|
],
|
||||||
child: AppMainContent(),
|
child: AppMainContent(),
|
||||||
),
|
),
|
||||||
@@ -62,7 +71,7 @@ class AppMainContent extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
context.read<NavigationProvider>();
|
context.read<NavigationProvider>();
|
||||||
context.read<UserProvider>();
|
context.read<WebSocketProvider>();
|
||||||
|
|
||||||
final th = context.watch<ThemeProvider>();
|
final th = context.watch<ThemeProvider>();
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
int? get currentIndex => _currentIndex;
|
int? get currentIndex => _currentIndex;
|
||||||
|
|
||||||
|
static const List<String> kShowBottomNavScreen = [
|
||||||
|
'home',
|
||||||
|
'explore',
|
||||||
|
'account',
|
||||||
|
'album',
|
||||||
|
'chat',
|
||||||
|
];
|
||||||
|
|
||||||
static const List<AppNavDestination> kAllDestination = [
|
static const List<AppNavDestination> kAllDestination = [
|
||||||
AppNavDestination(
|
AppNavDestination(
|
||||||
icon: Icon(Symbols.home, weight: 400, opticalSize: 20),
|
icon: Icon(Symbols.home, weight: 400, opticalSize: 20),
|
||||||
@@ -35,26 +43,32 @@ class NavigationProvider extends ChangeNotifier {
|
|||||||
screen: 'explore',
|
screen: 'explore',
|
||||||
label: 'screenExplore',
|
label: 'screenExplore',
|
||||||
),
|
),
|
||||||
|
AppNavDestination(
|
||||||
|
icon: Icon(Symbols.chat, weight: 400, opticalSize: 20),
|
||||||
|
screen: 'chat',
|
||||||
|
label: 'screenChat',
|
||||||
|
),
|
||||||
AppNavDestination(
|
AppNavDestination(
|
||||||
icon: Icon(Symbols.account_circle, weight: 400, opticalSize: 20),
|
icon: Icon(Symbols.account_circle, weight: 400, opticalSize: 20),
|
||||||
screen: 'account',
|
screen: 'account',
|
||||||
label: 'screenAccount',
|
label: 'screenAccount',
|
||||||
),
|
),
|
||||||
|
AppNavDestination(
|
||||||
|
icon: Icon(Symbols.group, weight: 400, opticalSize: 20),
|
||||||
|
screen: 'realm',
|
||||||
|
label: 'screenRealm',
|
||||||
|
),
|
||||||
AppNavDestination(
|
AppNavDestination(
|
||||||
icon: Icon(Symbols.album, weight: 400, opticalSize: 20),
|
icon: Icon(Symbols.album, weight: 400, opticalSize: 20),
|
||||||
screen: 'album',
|
screen: 'album',
|
||||||
label: 'screenAlbum',
|
label: 'screenAlbum',
|
||||||
),
|
),
|
||||||
AppNavDestination(
|
|
||||||
icon: Icon(Symbols.chat, weight: 400, opticalSize: 20),
|
|
||||||
screen: 'chat',
|
|
||||||
label: 'screenChat',
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
static const List<String> kDefaultPinnedDestination = [
|
static const List<String> kDefaultPinnedDestination = [
|
||||||
'home',
|
'home',
|
||||||
'explore',
|
'explore',
|
||||||
'account'
|
'chat',
|
||||||
|
'account',
|
||||||
];
|
];
|
||||||
|
|
||||||
List<AppNavDestination> destinations = [];
|
List<AppNavDestination> destinations = [];
|
||||||
|
|||||||
@@ -44,6 +44,25 @@ class SnNetworkProvider {
|
|||||||
RequestOptions options,
|
RequestOptions options,
|
||||||
RequestInterceptorHandler handler,
|
RequestInterceptorHandler handler,
|
||||||
) async {
|
) async {
|
||||||
|
final atk = await getFreshAtk();
|
||||||
|
if (atk != null) {
|
||||||
|
options.headers['Authorization'] = 'Bearer $atk';
|
||||||
|
}
|
||||||
|
return handler.next(options);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
client = addClientAdapter(client);
|
||||||
|
|
||||||
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
_prefs = prefs;
|
||||||
|
client.options.baseUrl =
|
||||||
|
_prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getFreshAtk() async {
|
||||||
try {
|
try {
|
||||||
var atk = await _storage.read(key: kAtkStoreKey);
|
var atk = await _storage.read(key: kAtkStoreKey);
|
||||||
if (atk != null) {
|
if (atk != null) {
|
||||||
@@ -52,8 +71,7 @@ class SnNetworkProvider {
|
|||||||
throw Exception('invalid format of access token');
|
throw Exception('invalid format of access token');
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawPayload =
|
var rawPayload = atkParts[1].replaceAll('-', '+').replaceAll('_', '/');
|
||||||
atkParts[1].replaceAll('-', '+').replaceAll('_', '/');
|
|
||||||
switch (rawPayload.length % 4) {
|
switch (rawPayload.length % 4) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
@@ -76,27 +94,16 @@ class SnNetworkProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (atk != null) {
|
if (atk != null) {
|
||||||
options.headers['Authorization'] = 'Bearer $atk';
|
return atk;
|
||||||
} else {
|
} else {
|
||||||
log('Access token refresh failed...');
|
log('Access token refresh failed...');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log('Failed to authenticate user: $err');
|
log('Failed to authenticate user: $err');
|
||||||
} finally {
|
|
||||||
handler.next(options);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
client = addClientAdapter(client);
|
return null;
|
||||||
|
|
||||||
SharedPreferences.getInstance().then((prefs) {
|
|
||||||
_prefs = prefs;
|
|
||||||
client.options.baseUrl =
|
|
||||||
_prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getAttachmentUrl(String ky) {
|
String getAttachmentUrl(String ky) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class UserProvider extends ChangeNotifier {
|
|||||||
late final SnNetworkProvider _sn;
|
late final SnNetworkProvider _sn;
|
||||||
late final FlutterSecureStorage _storage = FlutterSecureStorage();
|
late final FlutterSecureStorage _storage = FlutterSecureStorage();
|
||||||
|
|
||||||
|
Future<String?> get atk => _storage.read(key: kAtkStoreKey);
|
||||||
|
|
||||||
UserProvider(BuildContext context) {
|
UserProvider(BuildContext context) {
|
||||||
_sn = context.read<SnNetworkProvider>();
|
_sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
|||||||
110
lib/providers/websocket.dart
Normal file
110
lib/providers/websocket.dart
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
|
import 'package:surface/types/websocket.dart';
|
||||||
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
|
||||||
|
class WebSocketProvider extends ChangeNotifier {
|
||||||
|
bool isBusy = false;
|
||||||
|
bool isConnected = false;
|
||||||
|
|
||||||
|
WebSocketChannel? conn;
|
||||||
|
|
||||||
|
late final SnNetworkProvider _sn;
|
||||||
|
late final UserProvider _ua;
|
||||||
|
|
||||||
|
StreamController<WebSocketPackage> stream = StreamController.broadcast();
|
||||||
|
|
||||||
|
WebSocketProvider(BuildContext context) {
|
||||||
|
_sn = context.read<SnNetworkProvider>();
|
||||||
|
_ua = context.read<UserProvider>();
|
||||||
|
|
||||||
|
// Wait for the userinfo provide initialize authorization status
|
||||||
|
Future.delayed(const Duration(milliseconds: 250), () async {
|
||||||
|
if (_ua.isAuthorized) {
|
||||||
|
log('[WebSocket] Connecting to the server...');
|
||||||
|
await connect();
|
||||||
|
} else {
|
||||||
|
log('[WebSocket] Unable connect to the server, unauthorized.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> connect({noRetry = false}) async {
|
||||||
|
if (!_ua.isAuthorized) return;
|
||||||
|
if (isConnected) {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
final atk = await _sn.getFreshAtk();
|
||||||
|
final uri = Uri.parse(
|
||||||
|
'${_sn.client.options.baseUrl.replaceFirst('http', 'ws')}/ws?tk=$atk',
|
||||||
|
);
|
||||||
|
|
||||||
|
isBusy = true;
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
try {
|
||||||
|
conn = WebSocketChannel.connect(uri);
|
||||||
|
await conn!.ready;
|
||||||
|
log('[WebSocket] Connected to server!');
|
||||||
|
isConnected = true;
|
||||||
|
} catch (err) {
|
||||||
|
if (err is WebSocketChannelException) {
|
||||||
|
log('Failed to connect to websocket: ${(err.inner as dynamic).message}');
|
||||||
|
} else {
|
||||||
|
log('Failed to connect to websocket: $err');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noRetry) {
|
||||||
|
log('Retry connecting to websocket in 3 seconds...');
|
||||||
|
return Future.delayed(
|
||||||
|
const Duration(seconds: 3),
|
||||||
|
() => connect(noRetry: true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isBusy = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect() {
|
||||||
|
if (conn != null) {
|
||||||
|
conn!.sink.close();
|
||||||
|
}
|
||||||
|
isConnected = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void listen() {
|
||||||
|
conn?.stream.listen(
|
||||||
|
(event) {
|
||||||
|
final packet = WebSocketPackage.fromJson(jsonDecode(event));
|
||||||
|
log('Websocket incoming message: ${packet.method} ${packet.message}');
|
||||||
|
stream.sink.add(packet);
|
||||||
|
// TODO handle notification
|
||||||
|
// if (packet.method == 'notifications.new') {
|
||||||
|
// final NotificationProvider nty = Get.find();
|
||||||
|
// nty.notifications.add(Notification.fromJson(packet.payload!));
|
||||||
|
// nty.notificationUnread.value++;
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
isConnected = false;
|
||||||
|
notifyListeners();
|
||||||
|
Future.delayed(const Duration(seconds: 1), () => connect());
|
||||||
|
},
|
||||||
|
onError: (err) {
|
||||||
|
isConnected = false;
|
||||||
|
notifyListeners();
|
||||||
|
Future.delayed(const Duration(seconds: 11), () => connect());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
158
lib/router.dart
158
lib/router.dart
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:animations/animations.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:surface/screens/account.dart';
|
import 'package:surface/screens/account.dart';
|
||||||
import 'package:surface/screens/account/profile_edit.dart';
|
import 'package:surface/screens/account/profile_edit.dart';
|
||||||
@@ -8,59 +9,44 @@ import 'package:surface/screens/album.dart';
|
|||||||
import 'package:surface/screens/auth/login.dart';
|
import 'package:surface/screens/auth/login.dart';
|
||||||
import 'package:surface/screens/auth/register.dart';
|
import 'package:surface/screens/auth/register.dart';
|
||||||
import 'package:surface/screens/chat.dart';
|
import 'package:surface/screens/chat.dart';
|
||||||
|
import 'package:surface/screens/chat/manage.dart';
|
||||||
import 'package:surface/screens/explore.dart';
|
import 'package:surface/screens/explore.dart';
|
||||||
import 'package:surface/screens/home.dart';
|
import 'package:surface/screens/home.dart';
|
||||||
import 'package:surface/screens/post/post_detail.dart';
|
import 'package:surface/screens/post/post_detail.dart';
|
||||||
import 'package:surface/screens/post/post_editor.dart';
|
import 'package:surface/screens/post/post_editor.dart';
|
||||||
|
import 'package:surface/screens/realm.dart';
|
||||||
|
import 'package:surface/screens/realm/manage.dart';
|
||||||
import 'package:surface/screens/settings.dart';
|
import 'package:surface/screens/settings.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
|
import 'package:surface/widgets/navigation/app_background.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
|
|
||||||
final appRouter = GoRouter(
|
final _appRoutes = [
|
||||||
routes: [
|
|
||||||
ShellRoute(
|
ShellRoute(
|
||||||
builder: (context, state, child) => AppScaffold(
|
builder: (context, state, child) => AppPageScaffold(
|
||||||
body: child,
|
body: child,
|
||||||
showBottomNavigation: true,
|
showAppBar: false,
|
||||||
showDrawer: true,
|
|
||||||
),
|
),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
builder: (context, state) => const HomeScreen(),
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
|
child: const HomeScreen(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/posts',
|
path: '/posts',
|
||||||
name: 'explore',
|
name: 'explore',
|
||||||
builder: (context, state) => const ExploreScreen(),
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
),
|
child: const ExploreScreen(),
|
||||||
GoRoute(
|
|
||||||
path: '/account',
|
|
||||||
name: 'account',
|
|
||||||
builder: (context, state) => const AccountScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/chat',
|
|
||||||
name: 'chat',
|
|
||||||
builder: (context, state) => const ChatScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/album',
|
|
||||||
name: 'album',
|
|
||||||
builder: (context, state) => const AlbumScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
ShellRoute(
|
|
||||||
builder: (context, state, child) => AppScaffold(
|
|
||||||
body: child,
|
|
||||||
),
|
),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/post/write/:mode',
|
path: '/post/write/:mode',
|
||||||
name: 'postEditor',
|
name: 'postEditor',
|
||||||
builder: (context, state) => PostEditorScreen(
|
pageBuilder: (context, state) => CustomTransitionPage(
|
||||||
|
child: PostEditorScreen(
|
||||||
mode: state.pathParameters['mode']!,
|
mode: state.pathParameters['mode']!,
|
||||||
postEditId: int.tryParse(
|
postEditId: int.tryParse(
|
||||||
state.uri.queryParameters['editing'] ?? '',
|
state.uri.queryParameters['editing'] ?? '',
|
||||||
@@ -72,23 +58,110 @@ final appRouter = GoRouter(
|
|||||||
state.uri.queryParameters['reposting'] ?? '',
|
state.uri.queryParameters['reposting'] ?? '',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
transitionsBuilder:
|
||||||
|
(context, animation, secondaryAnimation, child) {
|
||||||
|
return FadeThroughTransition(
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
child: AppBackground(isLessOptimization: true, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/post/:slug',
|
path: '/post/:slug',
|
||||||
name: 'postDetail',
|
name: 'postDetail',
|
||||||
builder: (context, state) => PostDetailScreen(
|
pageBuilder: (context, state) => CustomTransitionPage(
|
||||||
|
child: PostDetailScreen(
|
||||||
slug: state.pathParameters['slug']!,
|
slug: state.pathParameters['slug']!,
|
||||||
preload: state.extra as SnPost?,
|
preload: state.extra as SnPost?,
|
||||||
),
|
),
|
||||||
)
|
transitionsBuilder:
|
||||||
|
(context, animation, secondaryAnimation, child) {
|
||||||
|
return FadeThroughTransition(
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
child: AppBackground(isLessOptimization: true, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/account',
|
||||||
|
name: 'account',
|
||||||
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
|
child: const AccountScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/chat',
|
||||||
|
name: 'chat',
|
||||||
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
|
child: const ChatScreen(),
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/chat/manage',
|
||||||
|
name: 'chatManage',
|
||||||
|
pageBuilder: (context, state) => CustomTransitionPage(
|
||||||
|
child: ChatManageScreen(),
|
||||||
|
transitionsBuilder:
|
||||||
|
(context, animation, secondaryAnimation, child) {
|
||||||
|
return FadeThroughTransition(
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
child: AppBackground(
|
||||||
|
isLessOptimization: true,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/realm',
|
||||||
|
name: 'realm',
|
||||||
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
|
child: const RealmScreen(),
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/realm/manage',
|
||||||
|
name: 'realmManage',
|
||||||
|
pageBuilder: (context, state) => CustomTransitionPage(
|
||||||
|
child: RealmManageScreen(
|
||||||
|
editingRealmAlias: state.uri.queryParameters['editing'],
|
||||||
|
),
|
||||||
|
transitionsBuilder:
|
||||||
|
(context, animation, secondaryAnimation, child) {
|
||||||
|
return FadeThroughTransition(
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
child: AppBackground(
|
||||||
|
isLessOptimization: true,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/album',
|
||||||
|
name: 'album',
|
||||||
|
pageBuilder: (context, state) => NoTransitionPage(
|
||||||
|
child: const AlbumScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ShellRoute(
|
ShellRoute(
|
||||||
builder: (context, state, child) => AppScaffold(
|
builder: (context, state, child) => AppPageScaffold(body: child),
|
||||||
body: child,
|
|
||||||
autoImplyAppBar: true,
|
|
||||||
showDrawer: true,
|
|
||||||
),
|
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/auth/login',
|
path: '/auth/login',
|
||||||
@@ -125,10 +198,7 @@ final appRouter = GoRouter(
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
ShellRoute(
|
ShellRoute(
|
||||||
builder: (context, state, child) => AppScaffold(
|
builder: (context, state, child) => AppPageScaffold(body: child),
|
||||||
body: child,
|
|
||||||
autoImplyAppBar: true,
|
|
||||||
),
|
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
@@ -137,5 +207,13 @@ final appRouter = GoRouter(
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
final appRouter = GoRouter(
|
||||||
|
routes: [
|
||||||
|
ShellRoute(
|
||||||
|
routes: _appRoutes,
|
||||||
|
builder: (context, state, child) => AppRootScaffold(body: child),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
|
|
||||||
class AccountScreen extends StatelessWidget {
|
class AccountScreen extends StatelessWidget {
|
||||||
const AccountScreen({super.key});
|
const AccountScreen({super.key});
|
||||||
@@ -17,7 +16,7 @@ class AccountScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ua = context.watch<UserProvider>();
|
final ua = context.watch<UserProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("screenAccount").tr(),
|
title: Text("screenAccount").tr(),
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import 'package:surface/types/post.dart';
|
|||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
import 'package:surface/widgets/universal_image.dart';
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
|
||||||
class AccountPublisherEditScreen extends StatefulWidget {
|
class AccountPublisherEditScreen extends StatefulWidget {
|
||||||
@@ -149,20 +148,14 @@ class _AccountPublisherEditScreenState
|
|||||||
mimetype: 'image/png',
|
mimetype: 'image/png',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!mounted) return;
|
switch (place) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
case 'avatar':
|
||||||
await sn.client.put(
|
_avatar = attachment.rid;
|
||||||
'/cgi/id/users/me/$place',
|
break;
|
||||||
data: {'attachment': attachment.rid},
|
case 'banner':
|
||||||
);
|
_banner = attachment.rid;
|
||||||
|
break;
|
||||||
if (!mounted) return;
|
}
|
||||||
final ua = context.read<UserProvider>();
|
|
||||||
await ua.refreshUser();
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
context.showSnackbar('accountProfileEditApplied'.tr());
|
|
||||||
_syncWidget();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@@ -189,7 +182,7 @@ class _AccountPublisherEditScreenState
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -287,7 +280,7 @@ class _AccountPublisherEditScreenState
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 24, vertical: 12),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
|
|
||||||
class AccountPublisherNewScreen extends StatefulWidget {
|
class AccountPublisherNewScreen extends StatefulWidget {
|
||||||
const AccountPublisherNewScreen({super.key});
|
const AccountPublisherNewScreen({super.key});
|
||||||
@@ -23,7 +22,7 @@ class _AccountPublisherNewScreenState extends State<AccountPublisherNewScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@@ -11,7 +10,6 @@ import 'package:surface/types/post.dart';
|
|||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
|
|
||||||
class PublisherScreen extends StatefulWidget {
|
class PublisherScreen extends StatefulWidget {
|
||||||
const PublisherScreen({super.key});
|
const PublisherScreen({super.key});
|
||||||
@@ -55,7 +53,7 @@ class _PublisherScreenState extends State<PublisherScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|||||||
@@ -1,10 +1,88 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
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/types/chat.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
|
||||||
class ChatScreen extends StatelessWidget {
|
class ChatScreen extends StatefulWidget {
|
||||||
const ChatScreen({super.key});
|
const ChatScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatScreen> createState() => _ChatScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatScreenState extends State<ChatScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Placeholder();
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('screenChat').tr(),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
child: const Icon(Symbols.chat_add_on),
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).pushNamed('chatManage');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: _channels?.length ?? 0,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final channel = _channels![idx];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(channel.name),
|
||||||
|
subtitle: Text(channel.description),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: null,
|
||||||
|
fallbackWidget: const Icon(Symbols.chat, size: 20),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
292
lib/screens/chat/manage.dart
Normal file
292
lib/screens/chat/manage.dart
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/types/chat.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class ChatManageScreen extends StatefulWidget {
|
||||||
|
final String? editingChannelAlias;
|
||||||
|
const ChatManageScreen({super.key, this.editingChannelAlias});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatManageScreen> createState() => _ChatManageScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatManageScreenState extends State<ChatManageScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
final _aliasController = TextEditingController();
|
||||||
|
final _nameController = TextEditingController();
|
||||||
|
final _descriptionController = TextEditingController();
|
||||||
|
|
||||||
|
List<SnRealm>? _realms;
|
||||||
|
SnRealm? _belongToRealm;
|
||||||
|
|
||||||
|
Future<void> _fetchRealms() async {
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
try {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.get('/cgi/id/realms/me/available');
|
||||||
|
_realms = List<SnRealm>.from(
|
||||||
|
resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (mounted) context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SnChannel? _editingChannel;
|
||||||
|
|
||||||
|
Future<void> _fetchChannel() async {
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.get(
|
||||||
|
'/cgi/im/channels/${widget.editingChannelAlias}',
|
||||||
|
);
|
||||||
|
_editingChannel = SnChannel.fromJson(resp.data);
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _performAction() async {
|
||||||
|
final uuid = const Uuid();
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final scope = _belongToRealm != null ? _belongToRealm!.alias : 'global';
|
||||||
|
final payload = {
|
||||||
|
'alias': _aliasController.text.isNotEmpty
|
||||||
|
? _aliasController.text.toLowerCase()
|
||||||
|
: uuid.v4().replaceAll('-', '').substring(0, 12),
|
||||||
|
'name': _nameController.text,
|
||||||
|
'description': _descriptionController.text,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final resp = await sn.client.request(
|
||||||
|
widget.editingChannelAlias != null
|
||||||
|
? '/cgi/im/channels/$scope/${widget.editingChannelAlias}'
|
||||||
|
: '/cgi/im/channels/$scope',
|
||||||
|
data: payload,
|
||||||
|
options: Options(
|
||||||
|
method: widget.editingChannelAlias != null ? 'PUT' : 'POST',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
if (context.mounted) Navigator.pop(context, resp.data);
|
||||||
|
} catch (err) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
if (context.mounted) context.showErrorDialog(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.editingChannelAlias != null) _fetchChannel();
|
||||||
|
_fetchRealms();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_aliasController.dispose();
|
||||||
|
_nameController.dispose();
|
||||||
|
_descriptionController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: widget.editingChannelAlias != null
|
||||||
|
? Text('screenChatManage').tr()
|
||||||
|
: Text('screenChatNew').tr(),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
if (_editingChannel != null)
|
||||||
|
MaterialBanner(
|
||||||
|
leading: const Icon(Icons.edit),
|
||||||
|
leadingPadding: const EdgeInsets.only(left: 10, right: 20),
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
content: Text(
|
||||||
|
'channelEditingNotice'
|
||||||
|
.tr(args: ['#${_editingChannel!.alias}']),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('cancel').tr(),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton2<SnRealm>(
|
||||||
|
isExpanded: true,
|
||||||
|
hint: Text(
|
||||||
|
'fieldChatBelongToRealm'.tr(),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
...(_realms?.map(
|
||||||
|
(SnRealm item) => DropdownMenuItem<SnRealm>(
|
||||||
|
value: item,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
AccountImage(
|
||||||
|
content: item.avatar,
|
||||||
|
radius: 16,
|
||||||
|
fallbackWidget: const Icon(
|
||||||
|
Symbols.group,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(item.name).textStyle(Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!),
|
||||||
|
Text(
|
||||||
|
item.description,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).textStyle(
|
||||||
|
Theme.of(context).textTheme.bodySmall!),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) ??
|
||||||
|
[]),
|
||||||
|
DropdownMenuItem<SnRealm>(
|
||||||
|
value: null,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 16,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.onSurface,
|
||||||
|
child: const Icon(Symbols.clear),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('fieldChatBelongToRealmUnset')
|
||||||
|
.tr()
|
||||||
|
.textStyle(
|
||||||
|
Theme.of(context).textTheme.bodyMedium!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
value: _belongToRealm,
|
||||||
|
onChanged: (SnRealm? value) {
|
||||||
|
setState(() => _belongToRealm = value);
|
||||||
|
},
|
||||||
|
buttonStyleData: const ButtonStyleData(
|
||||||
|
padding: EdgeInsets.only(right: 16),
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
const Gap(12),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _aliasController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldChatAlias'.tr(),
|
||||||
|
helperText: 'fieldChatAliasHint'.tr(),
|
||||||
|
helperMaxLines: 2,
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _nameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldChatName'.tr(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _descriptionController,
|
||||||
|
maxLines: null,
|
||||||
|
minLines: 3,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldChatDescription'.tr(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: _isBusy ? null : _performAction,
|
||||||
|
icon: const Icon(Symbols.save),
|
||||||
|
label: Text('apply').tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,10 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_attachment.dart';
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
import 'package:surface/widgets/post/post_item.dart';
|
||||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
floatingActionButtonLocation: ExpandableFab.location,
|
floatingActionButtonLocation: ExpandableFab.location,
|
||||||
floatingActionButton: ExpandableFab(
|
floatingActionButton: ExpandableFab(
|
||||||
key: _fabKey,
|
key: _fabKey,
|
||||||
@@ -173,7 +173,10 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
onFetchData: _fetchPosts,
|
onFetchData: _fetchPosts,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 640),
|
||||||
child: PostItem(data: _posts[idx]),
|
child: PostItem(data: _posts[idx]),
|
||||||
|
).center(),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postDetail',
|
'postDetail',
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
@@ -14,7 +13,7 @@ class HomeScreen extends StatefulWidget {
|
|||||||
class _HomeScreenState extends State<HomeScreen> {
|
class _HomeScreenState extends State<HomeScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("screenHome").tr(),
|
title: Text("screenHome").tr(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_attachment.dart';
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
import 'package:surface/widgets/post/post_comment_list.dart';
|
import 'package:surface/widgets/post/post_comment_list.dart';
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
import 'package:surface/widgets/post/post_item.dart';
|
||||||
import 'package:surface/widgets/post/post_mini_editor.dart';
|
import 'package:surface/widgets/post/post_mini_editor.dart';
|
||||||
@@ -72,9 +72,10 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final ua = context.watch<UserProvider>();
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: BackButton(
|
leading: BackButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -108,10 +109,13 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
),
|
),
|
||||||
if (_data != null)
|
if (_data != null)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 640),
|
||||||
child: PostItem(
|
child: PostItem(
|
||||||
data: _data!,
|
data: _data!,
|
||||||
showComments: false,
|
showComments: false,
|
||||||
),
|
),
|
||||||
|
).center(),
|
||||||
),
|
),
|
||||||
const SliverToBoxAdapter(child: Divider(height: 1)),
|
const SliverToBoxAdapter(child: Divider(height: 1)),
|
||||||
if (_data != null)
|
if (_data != null)
|
||||||
@@ -127,7 +131,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
],
|
],
|
||||||
).padding(horizontal: 20, vertical: 12),
|
).padding(horizontal: 20, vertical: 12),
|
||||||
),
|
),
|
||||||
if (_data != null)
|
if (_data != null && ua.isAuthorized)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 240,
|
height: 240,
|
||||||
@@ -158,6 +162,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
PostCommentSliverList(
|
PostCommentSliverList(
|
||||||
key: _childListKey,
|
key: _childListKey,
|
||||||
parentPostId: _data!.id,
|
parentPostId: _data!.id,
|
||||||
|
maxWidth: 640,
|
||||||
),
|
),
|
||||||
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
import 'package:surface/widgets/post/post_item.dart';
|
||||||
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
import 'package:surface/widgets/post/post_media_pending_list.dart';
|
||||||
import 'package:surface/widgets/post/post_meta_editor.dart';
|
import 'package:surface/widgets/post/post_meta_editor.dart';
|
||||||
@@ -111,7 +110,7 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
return ListenableBuilder(
|
return ListenableBuilder(
|
||||||
listenable: _writeController,
|
listenable: _writeController,
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: BackButton(
|
leading: BackButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|||||||
220
lib/screens/realm.dart
Normal file
220
lib/screens/realm.dart
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
|
||||||
|
class RealmScreen extends StatefulWidget {
|
||||||
|
const RealmScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RealmScreen> createState() => _RealmScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RealmScreenState extends State<RealmScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
|
bool _isCompactView = false;
|
||||||
|
|
||||||
|
List<SnRealm>? _realms;
|
||||||
|
|
||||||
|
Future<void> _fetchRealms() async {
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
try {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.get('/cgi/id/realms/me/available');
|
||||||
|
_realms = List<SnRealm>.from(
|
||||||
|
resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (mounted) context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _deleteRealm(SnRealm realm) async {
|
||||||
|
final confirm = await context.showConfirmDialog(
|
||||||
|
'realmDelete'.tr(args: ['#${realm.alias}']),
|
||||||
|
'realmDeleteDescription'.tr(),
|
||||||
|
);
|
||||||
|
if (!confirm) return;
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sn.client.delete('/cgi/id/realms/${realm.alias}');
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showSnackbar('realmDeleted'.tr(args: ['#${realm.alias}']));
|
||||||
|
_fetchRealms();
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchRealms();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('screenRealm').tr(),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: !_isCompactView
|
||||||
|
? const Icon(Icons.view_list)
|
||||||
|
: const Icon(Icons.view_module),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => _isCompactView = !_isCompactView);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
child: const Icon(Symbols.group_add),
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).pushNamed('realmManage');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
onRefresh: _fetchRealms,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: _realms?.length ?? 0,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
final realm = _realms![idx];
|
||||||
|
if (_isCompactView) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
leading: AccountImage(
|
||||||
|
content: realm.avatar,
|
||||||
|
fallbackWidget: const Icon(Symbols.group, size: 20),
|
||||||
|
),
|
||||||
|
title: Text(realm.name),
|
||||||
|
subtitle: Text(
|
||||||
|
realm.description,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
trailing: PopupMenuButton(
|
||||||
|
itemBuilder: (BuildContext context) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.edit),
|
||||||
|
const Gap(16),
|
||||||
|
Text('edit').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'realmManage',
|
||||||
|
queryParameters: {'editing': realm.alias},
|
||||||
|
).then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
_fetchRealms();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.delete),
|
||||||
|
const Gap(16),
|
||||||
|
Text('delete').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_deleteRealm(realm);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.all(12),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 16 / 7,
|
||||||
|
child: Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainer,
|
||||||
|
child: (realm.banner?.isEmpty ?? true)
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(realm.banner!),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: -30,
|
||||||
|
left: 18,
|
||||||
|
child: AccountImage(
|
||||||
|
content: realm.avatar,
|
||||||
|
radius: 24,
|
||||||
|
fallbackWidget:
|
||||||
|
const Icon(Symbols.group, size: 24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(20 + 12),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(realm.name).textStyle(
|
||||||
|
Theme.of(context).textTheme.titleMedium!),
|
||||||
|
Text(realm.description).textStyle(
|
||||||
|
Theme.of(context).textTheme.bodySmall!),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, bottom: 14),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
312
lib/screens/realm/manage.dart
Normal file
312
lib/screens/realm/manage.dart
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:croppy/croppy.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:path/path.dart' show basename;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
|
import 'package:surface/widgets/dialog.dart';
|
||||||
|
import 'package:surface/widgets/loading_indicator.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class RealmManageScreen extends StatefulWidget {
|
||||||
|
final String? editingRealmAlias;
|
||||||
|
const RealmManageScreen({super.key, this.editingRealmAlias});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RealmManageScreen> createState() => _RealmManageScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RealmManageScreenState extends State<RealmManageScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
SnRealm? _editingRealm;
|
||||||
|
|
||||||
|
Future<void> _fetchRealm() async {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final resp =
|
||||||
|
await sn.client.get('/cgi/id/realms/${widget.editingRealmAlias}');
|
||||||
|
final out = SnRealm.fromJson(resp.data);
|
||||||
|
_editingRealm = out;
|
||||||
|
_avatar = out.avatar;
|
||||||
|
_banner = out.banner;
|
||||||
|
_aliasController.text = out.alias;
|
||||||
|
_nameController.text = out.name;
|
||||||
|
_descriptionController.text = out.description;
|
||||||
|
} catch (err) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
if (context.mounted) context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _avatar;
|
||||||
|
String? _banner;
|
||||||
|
|
||||||
|
final _aliasController = TextEditingController();
|
||||||
|
final _nameController = TextEditingController();
|
||||||
|
final _descriptionController = TextEditingController();
|
||||||
|
|
||||||
|
final _imagePicker = ImagePicker();
|
||||||
|
|
||||||
|
Future<void> _updateImage(String place) async {
|
||||||
|
final image = await _imagePicker.pickImage(source: ImageSource.gallery);
|
||||||
|
if (image == null) return;
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
final ImageProvider imageProvider =
|
||||||
|
kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path));
|
||||||
|
final aspectRatios = place == 'banner'
|
||||||
|
? [CropAspectRatio(width: 16, height: 7)]
|
||||||
|
: [CropAspectRatio(width: 1, height: 1)];
|
||||||
|
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
|
? await showCupertinoImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
)
|
||||||
|
: await showMaterialImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
context,
|
||||||
|
allowedAspectRatios: aspectRatios,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
final attach = context.read<SnAttachmentProvider>();
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final rawBytes =
|
||||||
|
(await result.uiImage.toByteData(format: ImageByteFormat.png))!
|
||||||
|
.buffer
|
||||||
|
.asUint8List();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final attachment = await attach.directUploadOne(
|
||||||
|
rawBytes,
|
||||||
|
basename(image.path),
|
||||||
|
'avatar',
|
||||||
|
null,
|
||||||
|
mimetype: 'image/png',
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (place) {
|
||||||
|
case 'avatar':
|
||||||
|
_avatar = attachment.rid;
|
||||||
|
break;
|
||||||
|
case 'banner':
|
||||||
|
_banner = attachment.rid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (!mounted) return;
|
||||||
|
context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _performAction() async {
|
||||||
|
final uuid = const Uuid();
|
||||||
|
final payload = {
|
||||||
|
'alias': _aliasController.text.isNotEmpty
|
||||||
|
? _aliasController.text.toLowerCase()
|
||||||
|
: uuid.v4().replaceAll('-', '').substring(0, 12),
|
||||||
|
'name': _nameController.text,
|
||||||
|
'description': _descriptionController.text,
|
||||||
|
'avatar': _avatar,
|
||||||
|
'banner': _banner,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final resp = await sn.client.request(
|
||||||
|
widget.editingRealmAlias != null
|
||||||
|
? '/cgi/id/realms/${widget.editingRealmAlias}'
|
||||||
|
: '/cgi/id/realms',
|
||||||
|
data: payload,
|
||||||
|
options: Options(
|
||||||
|
method: widget.editingRealmAlias != null ? 'PUT' : 'POST',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final out = SnRealm.fromJson(resp.data);
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
if (context.mounted) Navigator.pop(context, out);
|
||||||
|
} catch (err) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
if (context.mounted) context.showErrorDialog(err);
|
||||||
|
} finally {
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.editingRealmAlias != null) _fetchRealm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_aliasController.dispose();
|
||||||
|
_nameController.dispose();
|
||||||
|
_descriptionController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: widget.editingRealmAlias != null
|
||||||
|
? Text('screenRealmManage').tr()
|
||||||
|
: Text('screenRealmNew').tr(),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
LoadingIndicator(isActive: _isBusy),
|
||||||
|
if (_editingRealm != null)
|
||||||
|
MaterialBanner(
|
||||||
|
leading: const Icon(Icons.edit),
|
||||||
|
leadingPadding: const EdgeInsets.only(left: 10, right: 20),
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
content: Text(
|
||||||
|
'realmEditingNotice'.tr(args: ['#${_editingRealm!.alias}']),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('cancel').tr(),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
Material(
|
||||||
|
elevation: 0,
|
||||||
|
child: InkWell(
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainerHigh,
|
||||||
|
child: _banner != null
|
||||||
|
? AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(_banner!),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_updateImage('banner');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: -28,
|
||||||
|
left: 16,
|
||||||
|
child: Material(
|
||||||
|
elevation: 2,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(40)),
|
||||||
|
child: InkWell(
|
||||||
|
child: AccountImage(
|
||||||
|
content: _avatar,
|
||||||
|
radius: 40,
|
||||||
|
fallbackWidget: const Icon(Symbols.group, size: 40),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_updateImage('avatar');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24),
|
||||||
|
const Gap(8 + 28),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _aliasController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldRealmAlias'.tr(),
|
||||||
|
helperText: 'fieldRealmAliasHint'.tr(),
|
||||||
|
helperMaxLines: 2,
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _nameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldRealmName'.tr(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
TextField(
|
||||||
|
controller: _descriptionController,
|
||||||
|
maxLines: null,
|
||||||
|
minLines: 3,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
labelText: 'fieldRealmDescription'.tr(),
|
||||||
|
),
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: _isBusy ? null : _performAction,
|
||||||
|
icon: const Icon(Symbols.save),
|
||||||
|
label: Text('apply').tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24 + 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@ import 'package:surface/providers/sn_network.dart';
|
|||||||
import 'package:surface/providers/theme.dart';
|
import 'package:surface/providers/theme.dart';
|
||||||
import 'package:surface/theme.dart';
|
import 'package:surface/theme.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
const SettingsScreen({super.key});
|
const SettingsScreen({super.key});
|
||||||
@@ -58,7 +57,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
|
||||||
return AppScaffold(
|
return Scaffold(
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
30
lib/types/chat.dart
Normal file
30
lib/types/chat.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:surface/types/realm.dart';
|
||||||
|
|
||||||
|
part 'chat.freezed.dart';
|
||||||
|
part 'chat.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SnChannel with _$SnChannel {
|
||||||
|
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,
|
||||||
|
}) = _SnChannel;
|
||||||
|
|
||||||
|
factory SnChannel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnChannelFromJson(json);
|
||||||
|
}
|
||||||
516
lib/types/chat.freezed.dart
Normal file
516
lib/types/chat.freezed.dart
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'chat.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
SnChannel _$SnChannelFromJson(Map<String, dynamic> json) {
|
||||||
|
return _SnChannel.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnChannel {
|
||||||
|
int get id => throw _privateConstructorUsedError;
|
||||||
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
dynamic get deletedAt => throw _privateConstructorUsedError;
|
||||||
|
String get alias => throw _privateConstructorUsedError;
|
||||||
|
String get name => throw _privateConstructorUsedError;
|
||||||
|
String get description => throw _privateConstructorUsedError;
|
||||||
|
List<dynamic> get members => throw _privateConstructorUsedError;
|
||||||
|
dynamic get messages => throw _privateConstructorUsedError;
|
||||||
|
dynamic get calls => throw _privateConstructorUsedError;
|
||||||
|
int get type => throw _privateConstructorUsedError;
|
||||||
|
int get accountId => throw _privateConstructorUsedError;
|
||||||
|
bool get isPublic => throw _privateConstructorUsedError;
|
||||||
|
bool get isCommunity => throw _privateConstructorUsedError;
|
||||||
|
SnRealm? get realm => throw _privateConstructorUsedError;
|
||||||
|
int? get realmId => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this SnChannel to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of SnChannel
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$SnChannelCopyWith<SnChannel> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SnChannelCopyWith<$Res> {
|
||||||
|
factory $SnChannelCopyWith(SnChannel value, $Res Function(SnChannel) then) =
|
||||||
|
_$SnChannelCopyWithImpl<$Res, SnChannel>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
dynamic deletedAt,
|
||||||
|
String alias,
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
List<dynamic> members,
|
||||||
|
dynamic messages,
|
||||||
|
dynamic calls,
|
||||||
|
int type,
|
||||||
|
int accountId,
|
||||||
|
bool isPublic,
|
||||||
|
bool isCommunity,
|
||||||
|
SnRealm? realm,
|
||||||
|
int? realmId});
|
||||||
|
|
||||||
|
$SnRealmCopyWith<$Res>? get realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnChannelCopyWithImpl<$Res, $Val extends SnChannel>
|
||||||
|
implements $SnChannelCopyWith<$Res> {
|
||||||
|
_$SnChannelCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnChannel
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? alias = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? description = null,
|
||||||
|
Object? members = null,
|
||||||
|
Object? messages = freezed,
|
||||||
|
Object? calls = freezed,
|
||||||
|
Object? type = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
Object? isPublic = null,
|
||||||
|
Object? isCommunity = null,
|
||||||
|
Object? realm = freezed,
|
||||||
|
Object? realmId = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // 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 dynamic,
|
||||||
|
alias: null == alias
|
||||||
|
? _value.alias
|
||||||
|
: alias // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
description: null == description
|
||||||
|
? _value.description
|
||||||
|
: description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
members: null == members
|
||||||
|
? _value.members
|
||||||
|
: members // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<dynamic>,
|
||||||
|
messages: freezed == messages
|
||||||
|
? _value.messages
|
||||||
|
: messages // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
|
calls: freezed == calls
|
||||||
|
? _value.calls
|
||||||
|
: calls // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
|
type: null == type
|
||||||
|
? _value.type
|
||||||
|
: type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
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
|
||||||
|
as bool,
|
||||||
|
isCommunity: null == isCommunity
|
||||||
|
? _value.isCommunity
|
||||||
|
: isCommunity // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
realm: freezed == realm
|
||||||
|
? _value.realm
|
||||||
|
: realm // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnRealm?,
|
||||||
|
realmId: freezed == realmId
|
||||||
|
? _value.realmId
|
||||||
|
: realmId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnChannel
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnRealmCopyWith<$Res>? get realm {
|
||||||
|
if (_value.realm == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnRealmCopyWith<$Res>(_value.realm!, (value) {
|
||||||
|
return _then(_value.copyWith(realm: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SnChannelImplCopyWith<$Res>
|
||||||
|
implements $SnChannelCopyWith<$Res> {
|
||||||
|
factory _$$SnChannelImplCopyWith(
|
||||||
|
_$SnChannelImpl value, $Res Function(_$SnChannelImpl) then) =
|
||||||
|
__$$SnChannelImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
dynamic deletedAt,
|
||||||
|
String alias,
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
List<dynamic> members,
|
||||||
|
dynamic messages,
|
||||||
|
dynamic calls,
|
||||||
|
int type,
|
||||||
|
int accountId,
|
||||||
|
bool isPublic,
|
||||||
|
bool isCommunity,
|
||||||
|
SnRealm? realm,
|
||||||
|
int? realmId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$SnRealmCopyWith<$Res>? get realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SnChannelImplCopyWithImpl<$Res>
|
||||||
|
extends _$SnChannelCopyWithImpl<$Res, _$SnChannelImpl>
|
||||||
|
implements _$$SnChannelImplCopyWith<$Res> {
|
||||||
|
__$$SnChannelImplCopyWithImpl(
|
||||||
|
_$SnChannelImpl _value, $Res Function(_$SnChannelImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of SnChannel
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? alias = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? description = null,
|
||||||
|
Object? members = null,
|
||||||
|
Object? messages = freezed,
|
||||||
|
Object? calls = freezed,
|
||||||
|
Object? type = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
Object? isPublic = null,
|
||||||
|
Object? isCommunity = null,
|
||||||
|
Object? realm = freezed,
|
||||||
|
Object? realmId = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$SnChannelImpl(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // 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 dynamic,
|
||||||
|
alias: null == alias
|
||||||
|
? _value.alias
|
||||||
|
: alias // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
description: null == description
|
||||||
|
? _value.description
|
||||||
|
: description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
members: null == members
|
||||||
|
? _value._members
|
||||||
|
: members // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<dynamic>,
|
||||||
|
messages: freezed == messages
|
||||||
|
? _value.messages
|
||||||
|
: messages // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
|
calls: freezed == calls
|
||||||
|
? _value.calls
|
||||||
|
: calls // ignore: cast_nullable_to_non_nullable
|
||||||
|
as dynamic,
|
||||||
|
type: null == type
|
||||||
|
? _value.type
|
||||||
|
: type // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
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
|
||||||
|
as bool,
|
||||||
|
isCommunity: null == isCommunity
|
||||||
|
? _value.isCommunity
|
||||||
|
: isCommunity // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
realm: freezed == realm
|
||||||
|
? _value.realm
|
||||||
|
: realm // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnRealm?,
|
||||||
|
realmId: freezed == realmId
|
||||||
|
? _value.realmId
|
||||||
|
: realmId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$SnChannelImpl implements _SnChannel {
|
||||||
|
const _$SnChannelImpl(
|
||||||
|
{required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.alias,
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required final List<dynamic> members,
|
||||||
|
required this.messages,
|
||||||
|
required this.calls,
|
||||||
|
required this.type,
|
||||||
|
required this.accountId,
|
||||||
|
required this.isPublic,
|
||||||
|
required this.isCommunity,
|
||||||
|
required this.realm,
|
||||||
|
required this.realmId})
|
||||||
|
: _members = members;
|
||||||
|
|
||||||
|
factory _$SnChannelImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$SnChannelImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int id;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime updatedAt;
|
||||||
|
@override
|
||||||
|
final dynamic deletedAt;
|
||||||
|
@override
|
||||||
|
final String alias;
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final String description;
|
||||||
|
final List<dynamic> _members;
|
||||||
|
@override
|
||||||
|
List<dynamic> get members {
|
||||||
|
if (_members is EqualUnmodifiableListView) return _members;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_members);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final dynamic messages;
|
||||||
|
@override
|
||||||
|
final dynamic calls;
|
||||||
|
@override
|
||||||
|
final int type;
|
||||||
|
@override
|
||||||
|
final int accountId;
|
||||||
|
@override
|
||||||
|
final bool isPublic;
|
||||||
|
@override
|
||||||
|
final bool isCommunity;
|
||||||
|
@override
|
||||||
|
final SnRealm? realm;
|
||||||
|
@override
|
||||||
|
final int? realmId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnChannel(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, members: $members, messages: $messages, calls: $calls, type: $type, accountId: $accountId, isPublic: $isPublic, isCommunity: $isCommunity, realm: $realm, realmId: $realmId)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SnChannelImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
const DeepCollectionEquality().equals(other.deletedAt, deletedAt) &&
|
||||||
|
(identical(other.alias, alias) || other.alias == alias) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.description, description) ||
|
||||||
|
other.description == description) &&
|
||||||
|
const DeepCollectionEquality().equals(other._members, _members) &&
|
||||||
|
const DeepCollectionEquality().equals(other.messages, messages) &&
|
||||||
|
const DeepCollectionEquality().equals(other.calls, calls) &&
|
||||||
|
(identical(other.type, type) || other.type == type) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId) &&
|
||||||
|
(identical(other.isPublic, isPublic) ||
|
||||||
|
other.isPublic == isPublic) &&
|
||||||
|
(identical(other.isCommunity, isCommunity) ||
|
||||||
|
other.isCommunity == isCommunity) &&
|
||||||
|
(identical(other.realm, realm) || other.realm == realm) &&
|
||||||
|
(identical(other.realmId, realmId) || other.realmId == realmId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
const DeepCollectionEquality().hash(deletedAt),
|
||||||
|
alias,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
const DeepCollectionEquality().hash(_members),
|
||||||
|
const DeepCollectionEquality().hash(messages),
|
||||||
|
const DeepCollectionEquality().hash(calls),
|
||||||
|
type,
|
||||||
|
accountId,
|
||||||
|
isPublic,
|
||||||
|
isCommunity,
|
||||||
|
realm,
|
||||||
|
realmId);
|
||||||
|
|
||||||
|
/// Create a copy of SnChannel
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SnChannelImplCopyWith<_$SnChannelImpl> get copyWith =>
|
||||||
|
__$$SnChannelImplCopyWithImpl<_$SnChannelImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$SnChannelImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SnChannel implements SnChannel {
|
||||||
|
const factory _SnChannel(
|
||||||
|
{required final int id,
|
||||||
|
required final DateTime createdAt,
|
||||||
|
required final DateTime updatedAt,
|
||||||
|
required final dynamic deletedAt,
|
||||||
|
required final String alias,
|
||||||
|
required final String name,
|
||||||
|
required final String description,
|
||||||
|
required final List<dynamic> members,
|
||||||
|
required final dynamic messages,
|
||||||
|
required final dynamic calls,
|
||||||
|
required final int type,
|
||||||
|
required final int accountId,
|
||||||
|
required final bool isPublic,
|
||||||
|
required final bool isCommunity,
|
||||||
|
required final SnRealm? realm,
|
||||||
|
required final int? realmId}) = _$SnChannelImpl;
|
||||||
|
|
||||||
|
factory _SnChannel.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$SnChannelImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get id;
|
||||||
|
@override
|
||||||
|
DateTime get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime get updatedAt;
|
||||||
|
@override
|
||||||
|
dynamic get deletedAt;
|
||||||
|
@override
|
||||||
|
String get alias;
|
||||||
|
@override
|
||||||
|
String get name;
|
||||||
|
@override
|
||||||
|
String get description;
|
||||||
|
@override
|
||||||
|
List<dynamic> get members;
|
||||||
|
@override
|
||||||
|
dynamic get messages;
|
||||||
|
@override
|
||||||
|
dynamic get calls;
|
||||||
|
@override
|
||||||
|
int get type;
|
||||||
|
@override
|
||||||
|
int get accountId;
|
||||||
|
@override
|
||||||
|
bool get isPublic;
|
||||||
|
@override
|
||||||
|
bool get isCommunity;
|
||||||
|
@override
|
||||||
|
SnRealm? get realm;
|
||||||
|
@override
|
||||||
|
int? get realmId;
|
||||||
|
|
||||||
|
/// Create a copy of SnChannel
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$SnChannelImplCopyWith<_$SnChannelImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
49
lib/types/chat.g.dart
Normal file
49
lib/types/chat.g.dart
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'chat.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$SnChannelImpl _$$SnChannelImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnChannelImpl(
|
||||||
|
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'],
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
members: json['members'] as List<dynamic>,
|
||||||
|
messages: json['messages'],
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$SnChannelImplToJson(_$SnChannelImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt,
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'members': instance.members,
|
||||||
|
'messages': instance.messages,
|
||||||
|
'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,
|
||||||
|
};
|
||||||
46
lib/types/realm.dart
Normal file
46
lib/types/realm.dart
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:surface/types/account.dart';
|
||||||
|
|
||||||
|
part 'realm.freezed.dart';
|
||||||
|
part 'realm.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SnRealmMember with _$SnRealmMember {
|
||||||
|
const factory SnRealmMember({
|
||||||
|
required int id,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
required int realmId,
|
||||||
|
required int accountId,
|
||||||
|
required SnRealm realm,
|
||||||
|
required SnAccount account,
|
||||||
|
required int powerLevel,
|
||||||
|
}) = _SnRealmMember;
|
||||||
|
|
||||||
|
factory SnRealmMember.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnRealmMemberFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SnRealm with _$SnRealm {
|
||||||
|
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,
|
||||||
|
}) = _SnRealm;
|
||||||
|
|
||||||
|
factory SnRealm.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnRealmFromJson(json);
|
||||||
|
}
|
||||||
812
lib/types/realm.freezed.dart
Normal file
812
lib/types/realm.freezed.dart
Normal file
@@ -0,0 +1,812 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'realm.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
SnRealmMember _$SnRealmMemberFromJson(Map<String, dynamic> json) {
|
||||||
|
return _SnRealmMember.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnRealmMember {
|
||||||
|
int get id => throw _privateConstructorUsedError;
|
||||||
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get deletedAt => throw _privateConstructorUsedError;
|
||||||
|
int get realmId => throw _privateConstructorUsedError;
|
||||||
|
int get accountId => throw _privateConstructorUsedError;
|
||||||
|
SnRealm get realm => throw _privateConstructorUsedError;
|
||||||
|
SnAccount get account => throw _privateConstructorUsedError;
|
||||||
|
int get powerLevel => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this SnRealmMember to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$SnRealmMemberCopyWith<SnRealmMember> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SnRealmMemberCopyWith<$Res> {
|
||||||
|
factory $SnRealmMemberCopyWith(
|
||||||
|
SnRealmMember value, $Res Function(SnRealmMember) then) =
|
||||||
|
_$SnRealmMemberCopyWithImpl<$Res, SnRealmMember>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
int realmId,
|
||||||
|
int accountId,
|
||||||
|
SnRealm realm,
|
||||||
|
SnAccount account,
|
||||||
|
int powerLevel});
|
||||||
|
|
||||||
|
$SnRealmCopyWith<$Res> get realm;
|
||||||
|
$SnAccountCopyWith<$Res> get account;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnRealmMemberCopyWithImpl<$Res, $Val extends SnRealmMember>
|
||||||
|
implements $SnRealmMemberCopyWith<$Res> {
|
||||||
|
_$SnRealmMemberCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? realmId = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
Object? realm = null,
|
||||||
|
Object? account = null,
|
||||||
|
Object? powerLevel = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // 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?,
|
||||||
|
realmId: null == realmId
|
||||||
|
? _value.realmId
|
||||||
|
: realmId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
accountId: null == accountId
|
||||||
|
? _value.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
realm: null == realm
|
||||||
|
? _value.realm
|
||||||
|
: realm // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnRealm,
|
||||||
|
account: null == account
|
||||||
|
? _value.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount,
|
||||||
|
powerLevel: null == powerLevel
|
||||||
|
? _value.powerLevel
|
||||||
|
: powerLevel // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnRealmCopyWith<$Res> get realm {
|
||||||
|
return $SnRealmCopyWith<$Res>(_value.realm, (value) {
|
||||||
|
return _then(_value.copyWith(realm: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnAccountCopyWith<$Res> get account {
|
||||||
|
return $SnAccountCopyWith<$Res>(_value.account, (value) {
|
||||||
|
return _then(_value.copyWith(account: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SnRealmMemberImplCopyWith<$Res>
|
||||||
|
implements $SnRealmMemberCopyWith<$Res> {
|
||||||
|
factory _$$SnRealmMemberImplCopyWith(
|
||||||
|
_$SnRealmMemberImpl value, $Res Function(_$SnRealmMemberImpl) then) =
|
||||||
|
__$$SnRealmMemberImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
int realmId,
|
||||||
|
int accountId,
|
||||||
|
SnRealm realm,
|
||||||
|
SnAccount account,
|
||||||
|
int powerLevel});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$SnRealmCopyWith<$Res> get realm;
|
||||||
|
@override
|
||||||
|
$SnAccountCopyWith<$Res> get account;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SnRealmMemberImplCopyWithImpl<$Res>
|
||||||
|
extends _$SnRealmMemberCopyWithImpl<$Res, _$SnRealmMemberImpl>
|
||||||
|
implements _$$SnRealmMemberImplCopyWith<$Res> {
|
||||||
|
__$$SnRealmMemberImplCopyWithImpl(
|
||||||
|
_$SnRealmMemberImpl _value, $Res Function(_$SnRealmMemberImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? realmId = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
Object? realm = null,
|
||||||
|
Object? account = null,
|
||||||
|
Object? powerLevel = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$SnRealmMemberImpl(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // 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?,
|
||||||
|
realmId: null == realmId
|
||||||
|
? _value.realmId
|
||||||
|
: realmId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
accountId: null == accountId
|
||||||
|
? _value.accountId
|
||||||
|
: accountId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
realm: null == realm
|
||||||
|
? _value.realm
|
||||||
|
: realm // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnRealm,
|
||||||
|
account: null == account
|
||||||
|
? _value.account
|
||||||
|
: account // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnAccount,
|
||||||
|
powerLevel: null == powerLevel
|
||||||
|
? _value.powerLevel
|
||||||
|
: powerLevel // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$SnRealmMemberImpl implements _SnRealmMember {
|
||||||
|
const _$SnRealmMemberImpl(
|
||||||
|
{required this.id,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.realmId,
|
||||||
|
required this.accountId,
|
||||||
|
required this.realm,
|
||||||
|
required this.account,
|
||||||
|
required this.powerLevel});
|
||||||
|
|
||||||
|
factory _$SnRealmMemberImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$SnRealmMemberImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int id;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime updatedAt;
|
||||||
|
@override
|
||||||
|
final DateTime? deletedAt;
|
||||||
|
@override
|
||||||
|
final int realmId;
|
||||||
|
@override
|
||||||
|
final int accountId;
|
||||||
|
@override
|
||||||
|
final SnRealm realm;
|
||||||
|
@override
|
||||||
|
final SnAccount account;
|
||||||
|
@override
|
||||||
|
final int powerLevel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnRealmMember(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, accountId: $accountId, realm: $realm, account: $account, powerLevel: $powerLevel)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SnRealmMemberImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
(identical(other.deletedAt, deletedAt) ||
|
||||||
|
other.deletedAt == deletedAt) &&
|
||||||
|
(identical(other.realmId, realmId) || other.realmId == realmId) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId) &&
|
||||||
|
(identical(other.realm, realm) || other.realm == realm) &&
|
||||||
|
(identical(other.account, account) || other.account == account) &&
|
||||||
|
(identical(other.powerLevel, powerLevel) ||
|
||||||
|
other.powerLevel == powerLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, id, createdAt, updatedAt,
|
||||||
|
deletedAt, realmId, accountId, realm, account, powerLevel);
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SnRealmMemberImplCopyWith<_$SnRealmMemberImpl> get copyWith =>
|
||||||
|
__$$SnRealmMemberImplCopyWithImpl<_$SnRealmMemberImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$SnRealmMemberImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SnRealmMember implements SnRealmMember {
|
||||||
|
const factory _SnRealmMember(
|
||||||
|
{required final int id,
|
||||||
|
required final DateTime createdAt,
|
||||||
|
required final DateTime updatedAt,
|
||||||
|
required final DateTime? deletedAt,
|
||||||
|
required final int realmId,
|
||||||
|
required final int accountId,
|
||||||
|
required final SnRealm realm,
|
||||||
|
required final SnAccount account,
|
||||||
|
required final int powerLevel}) = _$SnRealmMemberImpl;
|
||||||
|
|
||||||
|
factory _SnRealmMember.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$SnRealmMemberImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get id;
|
||||||
|
@override
|
||||||
|
DateTime get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime get updatedAt;
|
||||||
|
@override
|
||||||
|
DateTime? get deletedAt;
|
||||||
|
@override
|
||||||
|
int get realmId;
|
||||||
|
@override
|
||||||
|
int get accountId;
|
||||||
|
@override
|
||||||
|
SnRealm get realm;
|
||||||
|
@override
|
||||||
|
SnAccount get account;
|
||||||
|
@override
|
||||||
|
int get powerLevel;
|
||||||
|
|
||||||
|
/// Create a copy of SnRealmMember
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$SnRealmMemberImplCopyWith<_$SnRealmMemberImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
SnRealm _$SnRealmFromJson(Map<String, dynamic> json) {
|
||||||
|
return _SnRealm.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnRealm {
|
||||||
|
int get id => throw _privateConstructorUsedError;
|
||||||
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get deletedAt => throw _privateConstructorUsedError;
|
||||||
|
String get alias => throw _privateConstructorUsedError;
|
||||||
|
String get name => throw _privateConstructorUsedError;
|
||||||
|
String get description => throw _privateConstructorUsedError;
|
||||||
|
List<SnRealmMember>? get members => throw _privateConstructorUsedError;
|
||||||
|
String? get avatar => throw _privateConstructorUsedError;
|
||||||
|
String? get banner => throw _privateConstructorUsedError;
|
||||||
|
Map<String, dynamic>? get accessPolicy => throw _privateConstructorUsedError;
|
||||||
|
bool get isPublic => throw _privateConstructorUsedError;
|
||||||
|
bool get isCommunity => throw _privateConstructorUsedError;
|
||||||
|
int get accountId => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this SnRealm to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of SnRealm
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$SnRealmCopyWith<SnRealm> get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $SnRealmCopyWith<$Res> {
|
||||||
|
factory $SnRealmCopyWith(SnRealm value, $Res Function(SnRealm) then) =
|
||||||
|
_$SnRealmCopyWithImpl<$Res, SnRealm>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String alias,
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
List<SnRealmMember>? members,
|
||||||
|
String? avatar,
|
||||||
|
String? banner,
|
||||||
|
Map<String, dynamic>? accessPolicy,
|
||||||
|
bool isPublic,
|
||||||
|
bool isCommunity,
|
||||||
|
int accountId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnRealmCopyWithImpl<$Res, $Val extends SnRealm>
|
||||||
|
implements $SnRealmCopyWith<$Res> {
|
||||||
|
_$SnRealmCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnRealm
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? alias = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? description = null,
|
||||||
|
Object? members = freezed,
|
||||||
|
Object? avatar = freezed,
|
||||||
|
Object? banner = freezed,
|
||||||
|
Object? accessPolicy = freezed,
|
||||||
|
Object? isPublic = null,
|
||||||
|
Object? isCommunity = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // 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?,
|
||||||
|
alias: null == alias
|
||||||
|
? _value.alias
|
||||||
|
: alias // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
description: null == description
|
||||||
|
? _value.description
|
||||||
|
: description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
members: freezed == members
|
||||||
|
? _value.members
|
||||||
|
: members // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnRealmMember>?,
|
||||||
|
avatar: freezed == avatar
|
||||||
|
? _value.avatar
|
||||||
|
: avatar // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
banner: freezed == banner
|
||||||
|
? _value.banner
|
||||||
|
: banner // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
accessPolicy: freezed == accessPolicy
|
||||||
|
? _value.accessPolicy
|
||||||
|
: accessPolicy // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>?,
|
||||||
|
isPublic: null == isPublic
|
||||||
|
? _value.isPublic
|
||||||
|
: isPublic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
isCommunity: null == isCommunity
|
||||||
|
? _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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$SnRealmImplCopyWith<$Res> implements $SnRealmCopyWith<$Res> {
|
||||||
|
factory _$$SnRealmImplCopyWith(
|
||||||
|
_$SnRealmImpl value, $Res Function(_$SnRealmImpl) then) =
|
||||||
|
__$$SnRealmImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{int id,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime? deletedAt,
|
||||||
|
String alias,
|
||||||
|
String name,
|
||||||
|
String description,
|
||||||
|
List<SnRealmMember>? members,
|
||||||
|
String? avatar,
|
||||||
|
String? banner,
|
||||||
|
Map<String, dynamic>? accessPolicy,
|
||||||
|
bool isPublic,
|
||||||
|
bool isCommunity,
|
||||||
|
int accountId});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$SnRealmImplCopyWithImpl<$Res>
|
||||||
|
extends _$SnRealmCopyWithImpl<$Res, _$SnRealmImpl>
|
||||||
|
implements _$$SnRealmImplCopyWith<$Res> {
|
||||||
|
__$$SnRealmImplCopyWithImpl(
|
||||||
|
_$SnRealmImpl _value, $Res Function(_$SnRealmImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of SnRealm
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = null,
|
||||||
|
Object? createdAt = null,
|
||||||
|
Object? updatedAt = null,
|
||||||
|
Object? deletedAt = freezed,
|
||||||
|
Object? alias = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? description = null,
|
||||||
|
Object? members = freezed,
|
||||||
|
Object? avatar = freezed,
|
||||||
|
Object? banner = freezed,
|
||||||
|
Object? accessPolicy = freezed,
|
||||||
|
Object? isPublic = null,
|
||||||
|
Object? isCommunity = null,
|
||||||
|
Object? accountId = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$SnRealmImpl(
|
||||||
|
id: null == id
|
||||||
|
? _value.id
|
||||||
|
: id // 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?,
|
||||||
|
alias: null == alias
|
||||||
|
? _value.alias
|
||||||
|
: alias // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
description: null == description
|
||||||
|
? _value.description
|
||||||
|
: description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
members: freezed == members
|
||||||
|
? _value._members
|
||||||
|
: members // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<SnRealmMember>?,
|
||||||
|
avatar: freezed == avatar
|
||||||
|
? _value.avatar
|
||||||
|
: avatar // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
banner: freezed == banner
|
||||||
|
? _value.banner
|
||||||
|
: banner // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
accessPolicy: freezed == accessPolicy
|
||||||
|
? _value._accessPolicy
|
||||||
|
: accessPolicy // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>?,
|
||||||
|
isPublic: null == isPublic
|
||||||
|
? _value.isPublic
|
||||||
|
: isPublic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
isCommunity: null == isCommunity
|
||||||
|
? _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 {
|
||||||
|
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})
|
||||||
|
: _members = members,
|
||||||
|
_accessPolicy = accessPolicy;
|
||||||
|
|
||||||
|
factory _$SnRealmImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$SnRealmImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int id;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime updatedAt;
|
||||||
|
@override
|
||||||
|
final DateTime? deletedAt;
|
||||||
|
@override
|
||||||
|
final String alias;
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
@override
|
||||||
|
final String description;
|
||||||
|
final List<SnRealmMember>? _members;
|
||||||
|
@override
|
||||||
|
List<SnRealmMember>? get members {
|
||||||
|
final value = _members;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_members is EqualUnmodifiableListView) return _members;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String? avatar;
|
||||||
|
@override
|
||||||
|
final String? banner;
|
||||||
|
final Map<String, dynamic>? _accessPolicy;
|
||||||
|
@override
|
||||||
|
Map<String, dynamic>? get accessPolicy {
|
||||||
|
final value = _accessPolicy;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_accessPolicy is EqualUnmodifiableMapView) return _accessPolicy;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isPublic;
|
||||||
|
@override
|
||||||
|
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)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$SnRealmImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt) &&
|
||||||
|
(identical(other.deletedAt, deletedAt) ||
|
||||||
|
other.deletedAt == deletedAt) &&
|
||||||
|
(identical(other.alias, alias) || other.alias == alias) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.description, description) ||
|
||||||
|
other.description == description) &&
|
||||||
|
const DeepCollectionEquality().equals(other._members, _members) &&
|
||||||
|
(identical(other.avatar, avatar) || other.avatar == avatar) &&
|
||||||
|
(identical(other.banner, banner) || other.banner == banner) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._accessPolicy, _accessPolicy) &&
|
||||||
|
(identical(other.isPublic, isPublic) ||
|
||||||
|
other.isPublic == isPublic) &&
|
||||||
|
(identical(other.isCommunity, isCommunity) ||
|
||||||
|
other.isCommunity == isCommunity) &&
|
||||||
|
(identical(other.accountId, accountId) ||
|
||||||
|
other.accountId == accountId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
deletedAt,
|
||||||
|
alias,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
const DeepCollectionEquality().hash(_members),
|
||||||
|
avatar,
|
||||||
|
banner,
|
||||||
|
const DeepCollectionEquality().hash(_accessPolicy),
|
||||||
|
isPublic,
|
||||||
|
isCommunity,
|
||||||
|
accountId);
|
||||||
|
|
||||||
|
/// Create a copy of SnRealm
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$SnRealmImplCopyWith<_$SnRealmImpl> get copyWith =>
|
||||||
|
__$$SnRealmImplCopyWithImpl<_$SnRealmImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$SnRealmImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _SnRealm implements 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;
|
||||||
|
|
||||||
|
factory _SnRealm.fromJson(Map<String, dynamic> json) = _$SnRealmImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get id;
|
||||||
|
@override
|
||||||
|
DateTime get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime get updatedAt;
|
||||||
|
@override
|
||||||
|
DateTime? get deletedAt;
|
||||||
|
@override
|
||||||
|
String get alias;
|
||||||
|
@override
|
||||||
|
String get name;
|
||||||
|
@override
|
||||||
|
String get description;
|
||||||
|
@override
|
||||||
|
List<SnRealmMember>? get members;
|
||||||
|
@override
|
||||||
|
String? get avatar;
|
||||||
|
@override
|
||||||
|
String? get banner;
|
||||||
|
@override
|
||||||
|
Map<String, dynamic>? get accessPolicy;
|
||||||
|
@override
|
||||||
|
bool get isPublic;
|
||||||
|
@override
|
||||||
|
bool get isCommunity;
|
||||||
|
@override
|
||||||
|
int get accountId;
|
||||||
|
|
||||||
|
/// Create a copy of SnRealm
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$SnRealmImplCopyWith<_$SnRealmImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
75
lib/types/realm.g.dart
Normal file
75
lib/types/realm.g.dart
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'realm.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$SnRealmMemberImpl _$$SnRealmMemberImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnRealmMemberImpl(
|
||||||
|
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),
|
||||||
|
realmId: (json['realm_id'] as num).toInt(),
|
||||||
|
accountId: (json['account_id'] as num).toInt(),
|
||||||
|
realm: SnRealm.fromJson(json['realm'] as Map<String, dynamic>),
|
||||||
|
account: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
|
powerLevel: (json['power_level'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$SnRealmMemberImplToJson(_$SnRealmMemberImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'realm_id': instance.realmId,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
'realm': instance.realm.toJson(),
|
||||||
|
'account': instance.account.toJson(),
|
||||||
|
'power_level': instance.powerLevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$SnRealmImpl _$$SnRealmImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnRealmImpl(
|
||||||
|
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),
|
||||||
|
alias: json['alias'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
members: (json['members'] as List<dynamic>?)
|
||||||
|
?.map((e) => SnRealmMember.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
avatar: json['avatar'] as String?,
|
||||||
|
banner: json['banner'] as String?,
|
||||||
|
accessPolicy: json['access_policy'] as Map<String, dynamic>?,
|
||||||
|
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) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
'alias': instance.alias,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'members': instance.members?.map((e) => e.toJson()).toList(),
|
||||||
|
'avatar': instance.avatar,
|
||||||
|
'banner': instance.banner,
|
||||||
|
'access_policy': instance.accessPolicy,
|
||||||
|
'is_public': instance.isPublic,
|
||||||
|
'is_community': instance.isCommunity,
|
||||||
|
'account_id': instance.accountId,
|
||||||
|
};
|
||||||
17
lib/types/websocket.dart
Normal file
17
lib/types/websocket.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'websocket.freezed.dart';
|
||||||
|
part 'websocket.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class WebSocketPackage with _$WebSocketPackage {
|
||||||
|
const factory WebSocketPackage({
|
||||||
|
@JsonKey(name: 'w') @Default('unknown') String method,
|
||||||
|
@JsonKey(name: 'e') String? endpoint,
|
||||||
|
@JsonKey(name: 'm') String? message,
|
||||||
|
@JsonKey(name: 'p') @Default({}) Map<String, dynamic>? payload,
|
||||||
|
}) = _WebSocketPackage;
|
||||||
|
|
||||||
|
factory WebSocketPackage.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$WebSocketPackageFromJson(json);
|
||||||
|
}
|
||||||
252
lib/types/websocket.freezed.dart
Normal file
252
lib/types/websocket.freezed.dart
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'websocket.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
WebSocketPackage _$WebSocketPackageFromJson(Map<String, dynamic> json) {
|
||||||
|
return _WebSocketPackage.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$WebSocketPackage {
|
||||||
|
@JsonKey(name: 'w')
|
||||||
|
String get method => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(name: 'e')
|
||||||
|
String? get endpoint => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(name: 'm')
|
||||||
|
String? get message => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(name: 'p')
|
||||||
|
Map<String, dynamic>? get payload => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this WebSocketPackage to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of WebSocketPackage
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$WebSocketPackageCopyWith<WebSocketPackage> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $WebSocketPackageCopyWith<$Res> {
|
||||||
|
factory $WebSocketPackageCopyWith(
|
||||||
|
WebSocketPackage value, $Res Function(WebSocketPackage) then) =
|
||||||
|
_$WebSocketPackageCopyWithImpl<$Res, WebSocketPackage>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{@JsonKey(name: 'w') String method,
|
||||||
|
@JsonKey(name: 'e') String? endpoint,
|
||||||
|
@JsonKey(name: 'm') String? message,
|
||||||
|
@JsonKey(name: 'p') Map<String, dynamic>? payload});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$WebSocketPackageCopyWithImpl<$Res, $Val extends WebSocketPackage>
|
||||||
|
implements $WebSocketPackageCopyWith<$Res> {
|
||||||
|
_$WebSocketPackageCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of WebSocketPackage
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? method = null,
|
||||||
|
Object? endpoint = freezed,
|
||||||
|
Object? message = freezed,
|
||||||
|
Object? payload = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
method: null == method
|
||||||
|
? _value.method
|
||||||
|
: method // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
endpoint: freezed == endpoint
|
||||||
|
? _value.endpoint
|
||||||
|
: endpoint // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
message: freezed == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
payload: freezed == payload
|
||||||
|
? _value.payload
|
||||||
|
: payload // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$WebSocketPackageImplCopyWith<$Res>
|
||||||
|
implements $WebSocketPackageCopyWith<$Res> {
|
||||||
|
factory _$$WebSocketPackageImplCopyWith(_$WebSocketPackageImpl value,
|
||||||
|
$Res Function(_$WebSocketPackageImpl) then) =
|
||||||
|
__$$WebSocketPackageImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{@JsonKey(name: 'w') String method,
|
||||||
|
@JsonKey(name: 'e') String? endpoint,
|
||||||
|
@JsonKey(name: 'm') String? message,
|
||||||
|
@JsonKey(name: 'p') Map<String, dynamic>? payload});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$WebSocketPackageImplCopyWithImpl<$Res>
|
||||||
|
extends _$WebSocketPackageCopyWithImpl<$Res, _$WebSocketPackageImpl>
|
||||||
|
implements _$$WebSocketPackageImplCopyWith<$Res> {
|
||||||
|
__$$WebSocketPackageImplCopyWithImpl(_$WebSocketPackageImpl _value,
|
||||||
|
$Res Function(_$WebSocketPackageImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of WebSocketPackage
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? method = null,
|
||||||
|
Object? endpoint = freezed,
|
||||||
|
Object? message = freezed,
|
||||||
|
Object? payload = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$WebSocketPackageImpl(
|
||||||
|
method: null == method
|
||||||
|
? _value.method
|
||||||
|
: method // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
endpoint: freezed == endpoint
|
||||||
|
? _value.endpoint
|
||||||
|
: endpoint // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
message: freezed == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
payload: freezed == payload
|
||||||
|
? _value._payload
|
||||||
|
: payload // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, dynamic>?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$WebSocketPackageImpl implements _WebSocketPackage {
|
||||||
|
const _$WebSocketPackageImpl(
|
||||||
|
{@JsonKey(name: 'w') this.method = 'unknown',
|
||||||
|
@JsonKey(name: 'e') this.endpoint,
|
||||||
|
@JsonKey(name: 'm') this.message,
|
||||||
|
@JsonKey(name: 'p') final Map<String, dynamic>? payload = const {}})
|
||||||
|
: _payload = payload;
|
||||||
|
|
||||||
|
factory _$WebSocketPackageImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$WebSocketPackageImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'w')
|
||||||
|
final String method;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'e')
|
||||||
|
final String? endpoint;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'm')
|
||||||
|
final String? message;
|
||||||
|
final Map<String, dynamic>? _payload;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'p')
|
||||||
|
Map<String, dynamic>? get payload {
|
||||||
|
final value = _payload;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_payload is EqualUnmodifiableMapView) return _payload;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'WebSocketPackage(method: $method, endpoint: $endpoint, message: $message, payload: $payload)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$WebSocketPackageImpl &&
|
||||||
|
(identical(other.method, method) || other.method == method) &&
|
||||||
|
(identical(other.endpoint, endpoint) ||
|
||||||
|
other.endpoint == endpoint) &&
|
||||||
|
(identical(other.message, message) || other.message == message) &&
|
||||||
|
const DeepCollectionEquality().equals(other._payload, _payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, method, endpoint, message,
|
||||||
|
const DeepCollectionEquality().hash(_payload));
|
||||||
|
|
||||||
|
/// Create a copy of WebSocketPackage
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$WebSocketPackageImplCopyWith<_$WebSocketPackageImpl> get copyWith =>
|
||||||
|
__$$WebSocketPackageImplCopyWithImpl<_$WebSocketPackageImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$WebSocketPackageImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _WebSocketPackage implements WebSocketPackage {
|
||||||
|
const factory _WebSocketPackage(
|
||||||
|
{@JsonKey(name: 'w') final String method,
|
||||||
|
@JsonKey(name: 'e') final String? endpoint,
|
||||||
|
@JsonKey(name: 'm') final String? message,
|
||||||
|
@JsonKey(name: 'p') final Map<String, dynamic>? payload}) =
|
||||||
|
_$WebSocketPackageImpl;
|
||||||
|
|
||||||
|
factory _WebSocketPackage.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$WebSocketPackageImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'w')
|
||||||
|
String get method;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'e')
|
||||||
|
String? get endpoint;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'm')
|
||||||
|
String? get message;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: 'p')
|
||||||
|
Map<String, dynamic>? get payload;
|
||||||
|
|
||||||
|
/// Create a copy of WebSocketPackage
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$WebSocketPackageImplCopyWith<_$WebSocketPackageImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
25
lib/types/websocket.g.dart
Normal file
25
lib/types/websocket.g.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'websocket.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$WebSocketPackageImpl _$$WebSocketPackageImplFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$WebSocketPackageImpl(
|
||||||
|
method: json['w'] as String? ?? 'unknown',
|
||||||
|
endpoint: json['e'] as String?,
|
||||||
|
message: json['m'] as String?,
|
||||||
|
payload: json['p'] as Map<String, dynamic>? ?? const {},
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$WebSocketPackageImplToJson(
|
||||||
|
_$WebSocketPackageImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'w': instance.method,
|
||||||
|
'e': instance.endpoint,
|
||||||
|
'm': instance.message,
|
||||||
|
'p': instance.payload,
|
||||||
|
};
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
@@ -11,14 +9,15 @@ class AttachmentList extends StatelessWidget {
|
|||||||
final List<SnAttachment> data;
|
final List<SnAttachment> data;
|
||||||
final bool? bordered;
|
final bool? bordered;
|
||||||
final double? maxHeight;
|
final double? maxHeight;
|
||||||
|
final EdgeInsets? listPadding;
|
||||||
const AttachmentList({
|
const AttachmentList({
|
||||||
super.key,
|
super.key,
|
||||||
required this.data,
|
required this.data,
|
||||||
this.bordered,
|
this.bordered,
|
||||||
this.maxHeight,
|
this.maxHeight,
|
||||||
|
this.listPadding,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const double kMaxItemWidth = 520;
|
|
||||||
static const BorderRadius kDefaultRadius =
|
static const BorderRadius kDefaultRadius =
|
||||||
BorderRadius.all(Radius.circular(8));
|
BorderRadius.all(Radius.circular(8));
|
||||||
|
|
||||||
@@ -27,19 +26,22 @@ class AttachmentList extends StatelessWidget {
|
|||||||
final borderSide = (bordered ?? false)
|
final borderSide = (bordered ?? false)
|
||||||
? BorderSide(width: 1, color: Theme.of(context).dividerColor)
|
? BorderSide(width: 1, color: Theme.of(context).dividerColor)
|
||||||
: BorderSide.none;
|
: BorderSide.none;
|
||||||
|
final backgroundColor = Theme.of(context).colorScheme.surfaceContainer;
|
||||||
|
final constraints = BoxConstraints(
|
||||||
|
minWidth: 80,
|
||||||
|
maxHeight: maxHeight ?? double.infinity,
|
||||||
|
);
|
||||||
|
|
||||||
if (data.isEmpty) return const SizedBox.shrink();
|
if (data.isEmpty) return const SizedBox.shrink();
|
||||||
if (data.length == 1) {
|
if (data.length == 1) {
|
||||||
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
|
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
|
||||||
return Container(
|
return Padding(
|
||||||
constraints: BoxConstraints(
|
// Single child list-like displaying
|
||||||
maxHeight: maxHeight ?? double.infinity,
|
padding: listPadding ?? EdgeInsets.zero,
|
||||||
maxWidth: math.min(
|
child: Container(
|
||||||
MediaQuery.of(context).size.width - 20,
|
constraints: constraints,
|
||||||
kMaxItemWidth,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
border: Border(top: borderSide, bottom: borderSide),
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
borderRadius: kDefaultRadius,
|
borderRadius: kDefaultRadius,
|
||||||
),
|
),
|
||||||
@@ -50,11 +52,13 @@ class AttachmentList extends StatelessWidget {
|
|||||||
child: AttachmentItem(data: data[0], isExpandable: true),
|
child: AttachmentItem(data: data[0], isExpandable: true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
border: Border(top: borderSide, bottom: borderSide),
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
),
|
),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
@@ -72,15 +76,12 @@ class AttachmentList extends StatelessWidget {
|
|||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: data.length,
|
itemCount: data.length,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
return Container(
|
return Stack(
|
||||||
constraints: BoxConstraints(
|
children: [
|
||||||
maxHeight: maxHeight ?? double.infinity,
|
Container(
|
||||||
maxWidth: math.min(
|
constraints: constraints,
|
||||||
MediaQuery.of(context).size.width - 20,
|
|
||||||
kMaxItemWidth,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
border: Border(top: borderSide, bottom: borderSide),
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
borderRadius: kDefaultRadius,
|
borderRadius: kDefaultRadius,
|
||||||
),
|
),
|
||||||
@@ -88,13 +89,23 @@ class AttachmentList extends StatelessWidget {
|
|||||||
aspectRatio: data[idx].metadata['ratio']?.toDouble() ?? 1,
|
aspectRatio: data[idx].metadata['ratio']?.toDouble() ?? 1,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: kDefaultRadius,
|
borderRadius: kDefaultRadius,
|
||||||
child: AttachmentItem(data: data[idx], isExpandable: true),
|
child:
|
||||||
|
AttachmentItem(data: data[idx], isExpandable: true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 12,
|
||||||
|
bottom: 12,
|
||||||
|
child: Chip(
|
||||||
|
label: Text('${idx + 1}/${data.length}'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (context, index) => const Gap(8),
|
separatorBuilder: (context, index) => const Gap(8),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: listPadding,
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
),
|
),
|
||||||
|
|||||||
55
lib/widgets/connection_indicator.dart
Normal file
55
lib/widgets/connection_indicator.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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/providers/userinfo.dart';
|
||||||
|
import 'package:surface/providers/websocket.dart';
|
||||||
|
|
||||||
|
class ConnectionIndicator extends StatelessWidget {
|
||||||
|
const ConnectionIndicator({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ws = context.watch<WebSocketProvider>();
|
||||||
|
|
||||||
|
return ListenableBuilder(
|
||||||
|
listenable: ws,
|
||||||
|
builder: (context, _) {
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: 8,
|
||||||
|
top: MediaQuery.of(context).padding.top + 8,
|
||||||
|
left: 24,
|
||||||
|
right: 24,
|
||||||
|
),
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
child: ua.isAuthorized
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (ws.isBusy)
|
||||||
|
Text('serverConnecting').tr().textColor(
|
||||||
|
Theme.of(context).colorScheme.onSecondaryContainer)
|
||||||
|
else if (!ws.isConnected)
|
||||||
|
Text('serverDisconnected').tr().textColor(
|
||||||
|
Theme.of(context).colorScheme.onSecondaryContainer),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
)
|
||||||
|
.height(
|
||||||
|
(ws.isBusy || !ws.isConnected) && ua.isAuthorized
|
||||||
|
? MediaQuery.of(context).padding.top + 36
|
||||||
|
: 0,
|
||||||
|
animate: true)
|
||||||
|
.animate(
|
||||||
|
const Duration(milliseconds: 300),
|
||||||
|
Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,21 +6,44 @@ import 'package:path_provider/path_provider.dart';
|
|||||||
|
|
||||||
class AppBackground extends StatelessWidget {
|
class AppBackground extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
const AppBackground({super.key, required this.child});
|
final bool isLessOptimization;
|
||||||
|
const AppBackground({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.isLessOptimization = false,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
Widget _buildWithBackgroundImage(
|
||||||
Widget build(BuildContext context) {
|
BuildContext context,
|
||||||
|
File imageFile,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
return ScaffoldMessenger(
|
if (isLessOptimization) {
|
||||||
child: FutureBuilder(
|
final size = MediaQuery.of(context).size;
|
||||||
future:
|
return Container(
|
||||||
kIsWeb ? Future.value(null) : getApplicationDocumentsDirectory(),
|
color: Theme.of(context).colorScheme.surface,
|
||||||
builder: (context, snapshot) {
|
child: Container(
|
||||||
if (snapshot.hasData) {
|
decoration: BoxDecoration(
|
||||||
final path = '${snapshot.data!.path}/app_background_image';
|
backgroundBlendMode: BlendMode.darken,
|
||||||
final file = File(path);
|
color: Theme.of(context).colorScheme.surface,
|
||||||
if (file.existsSync()) {
|
image: DecorationImage(
|
||||||
|
opacity: 0.2,
|
||||||
|
image: ResizeImage(
|
||||||
|
FileImage(imageFile),
|
||||||
|
width: (size.width * devicePixelRatio).round(),
|
||||||
|
height: (size.height * devicePixelRatio).round(),
|
||||||
|
policy: ResizeImagePolicy.fit,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
@@ -32,11 +55,9 @@ class AppBackground extends StatelessWidget {
|
|||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
opacity: 0.2,
|
opacity: 0.2,
|
||||||
image: ResizeImage(
|
image: ResizeImage(
|
||||||
FileImage(file),
|
FileImage(imageFile),
|
||||||
width: (constraints.maxWidth * devicePixelRatio)
|
width: (constraints.maxWidth * devicePixelRatio).round(),
|
||||||
.round(),
|
height: (constraints.maxHeight * devicePixelRatio).round(),
|
||||||
height: (constraints.maxHeight * devicePixelRatio)
|
|
||||||
.round(),
|
|
||||||
policy: ResizeImagePolicy.fit,
|
policy: ResizeImagePolicy.fit,
|
||||||
),
|
),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
@@ -48,6 +69,20 @@ class AppBackground extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ScaffoldMessenger(
|
||||||
|
child: FutureBuilder(
|
||||||
|
future:
|
||||||
|
kIsWeb ? Future.value(null) : getApplicationDocumentsDirectory(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
final path = '${snapshot.data!.path}/app_background_image';
|
||||||
|
final file = File(path);
|
||||||
|
if (file.existsSync()) {
|
||||||
|
return _buildWithBackgroundImage(context, file, child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
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:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@@ -9,7 +7,8 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
import 'package:surface/providers/navigation.dart';
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
|
||||||
class AppNavigationDrawer extends StatefulWidget {
|
class AppNavigationDrawer extends StatefulWidget {
|
||||||
const AppNavigationDrawer({super.key});
|
final double? elevation;
|
||||||
|
const AppNavigationDrawer({super.key, this.elevation});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
|
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
|
||||||
@@ -43,6 +42,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return NavigationDrawer(
|
return NavigationDrawer(
|
||||||
|
elevation: widget.elevation,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
selectedIndex: nav.currentIndex,
|
selectedIndex: nav.currentIndex,
|
||||||
children: [
|
children: [
|
||||||
@@ -51,12 +51,12 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('Solar Network').bold(),
|
Text('Solar Network').bold(),
|
||||||
Text('Solar Network 2.0α').fontSize(12).textColor(
|
Text('Canary Preview 2.0α').fontSize(12).textColor(
|
||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.5)),
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.5)),
|
||||||
],
|
],
|
||||||
).padding(
|
).padding(
|
||||||
horizontal: 32,
|
horizontal: 32,
|
||||||
top: math.max(MediaQuery.of(context).padding.top, 16),
|
top: MediaQuery.of(context).padding.top > 16 ? 8 : 16,
|
||||||
bottom: 16,
|
bottom: 16,
|
||||||
),
|
),
|
||||||
...destinations.where((ele) => ele.isPinned).map((ele) {
|
...destinations.where((ele) => ele.isPinned).map((ele) {
|
||||||
|
|||||||
68
lib/widgets/navigation/app_rail_navigation.dart
Normal file
68
lib/widgets/navigation/app_rail_navigation.dart
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
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:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
|
||||||
|
class AppRailNavigation extends StatefulWidget {
|
||||||
|
const AppRailNavigation({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AppRailNavigation> createState() => _AppRailNavigationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppRailNavigationState extends State<AppRailNavigation> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
context
|
||||||
|
.read<NavigationProvider>()
|
||||||
|
.autoDetectIndex(GoRouter.maybeOf(context));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final nav = context.watch<NavigationProvider>();
|
||||||
|
|
||||||
|
return ListenableBuilder(
|
||||||
|
listenable: nav,
|
||||||
|
builder: (context, _) {
|
||||||
|
final destinations =
|
||||||
|
nav.destinations.where((ele) => ele.isPinned).toList();
|
||||||
|
|
||||||
|
return NavigationRail(
|
||||||
|
selectedIndex: nav.currentIndex,
|
||||||
|
destinations: [
|
||||||
|
...destinations.where((ele) => ele.isPinned).map((ele) {
|
||||||
|
return NavigationRailDestination(
|
||||||
|
icon: ele.icon,
|
||||||
|
label: Text(ele.label).tr(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
trailing: Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: StyledWidget(
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.menu),
|
||||||
|
onPressed: () {
|
||||||
|
Scaffold.of(context).openDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).padding(bottom: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDestinationSelected: (idx) {
|
||||||
|
nav.setIndex(idx);
|
||||||
|
GoRouter.of(context).goNamed(destinations[idx].screen);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,76 +2,103 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
|
import 'package:surface/providers/navigation.dart';
|
||||||
|
import 'package:surface/widgets/connection_indicator.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/navigation/app_background.dart';
|
import 'package:surface/widgets/navigation/app_background.dart';
|
||||||
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
||||||
import 'package:surface/widgets/navigation/app_drawer_navigation.dart';
|
import 'package:surface/widgets/navigation/app_drawer_navigation.dart';
|
||||||
|
import 'package:surface/widgets/navigation/app_rail_navigation.dart';
|
||||||
|
|
||||||
class AppScaffold extends StatelessWidget {
|
class AppPageScaffold extends StatelessWidget {
|
||||||
final PreferredSizeWidget? appBar;
|
|
||||||
final FloatingActionButtonLocation? floatingActionButtonLocation;
|
|
||||||
final Widget? floatingActionButton;
|
|
||||||
final String? title;
|
final String? title;
|
||||||
final Widget? body;
|
final Widget? body;
|
||||||
final bool autoImplyAppBar;
|
final bool showAppBar;
|
||||||
final bool showBottomNavigation;
|
final bool showBottomNavigation;
|
||||||
final bool showDrawer;
|
const AppPageScaffold({
|
||||||
const AppScaffold({
|
|
||||||
super.key,
|
super.key,
|
||||||
this.appBar,
|
|
||||||
this.floatingActionButton,
|
|
||||||
this.floatingActionButtonLocation,
|
|
||||||
this.title,
|
this.title,
|
||||||
this.body,
|
this.body,
|
||||||
this.autoImplyAppBar = false,
|
this.showAppBar = true,
|
||||||
this.showBottomNavigation = false,
|
this.showBottomNavigation = false,
|
||||||
this.showDrawer = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isShowDrawer = showDrawer
|
|
||||||
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
|
||||||
: false;
|
|
||||||
final isShowBottomNavigation = (showBottomNavigation)
|
|
||||||
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
final state = GoRouter.maybeOf(context);
|
final state = GoRouter.maybeOf(context);
|
||||||
|
final routeName =
|
||||||
|
state?.routerDelegate.currentConfiguration.last.route.name;
|
||||||
|
|
||||||
final innerWidget = AppBackground(
|
final autoTitle =
|
||||||
child: Scaffold(
|
state != null ? 'screen${routeName?.capitalize()}' : 'screen';
|
||||||
appBar: appBar ??
|
|
||||||
(autoImplyAppBar
|
return Scaffold(
|
||||||
|
appBar: showAppBar
|
||||||
? AppBar(
|
? AppBar(
|
||||||
title: title != null
|
title: Text(title ?? autoTitle.tr()),
|
||||||
? Text(title!)
|
|
||||||
: state != null
|
|
||||||
? Text(
|
|
||||||
('screen${state.routerDelegate.currentConfiguration.last.route.name?.capitalize()}')
|
|
||||||
.tr(),
|
|
||||||
)
|
)
|
||||||
: null)
|
: null,
|
||||||
: null),
|
|
||||||
body: body,
|
body: body,
|
||||||
floatingActionButtonLocation: floatingActionButtonLocation,
|
);
|
||||||
floatingActionButton: floatingActionButton,
|
}
|
||||||
drawer: isShowDrawer ? AppNavigationDrawer() : null,
|
}
|
||||||
|
|
||||||
|
class AppRootScaffold extends StatelessWidget {
|
||||||
|
final Widget body;
|
||||||
|
const AppRootScaffold({super.key, required this.body});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
|
final isCollapseDrawer =
|
||||||
|
ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE);
|
||||||
|
final isExpandDrawer = ResponsiveBreakpoints.of(context).largerThan(TABLET);
|
||||||
|
|
||||||
|
final routeName = GoRouter.of(context)
|
||||||
|
.routerDelegate
|
||||||
|
.currentConfiguration
|
||||||
|
.last
|
||||||
|
.route
|
||||||
|
.name;
|
||||||
|
final isShowBottomNavigation =
|
||||||
|
NavigationProvider.kShowBottomNavScreen.contains(routeName)
|
||||||
|
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
final innerWidget = isCollapseDrawer
|
||||||
|
? body
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1 / devicePixelRatio,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: isExpandDrawer
|
||||||
|
? AppNavigationDrawer(elevation: 0)
|
||||||
|
: AppRailNavigation(),
|
||||||
|
),
|
||||||
|
Expanded(child: body),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return AppBackground(
|
||||||
|
child: Scaffold(
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
ConnectionIndicator(),
|
||||||
|
Expanded(child: innerWidget),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
drawer: !isExpandDrawer ? AppNavigationDrawer() : null,
|
||||||
bottomNavigationBar:
|
bottomNavigationBar:
|
||||||
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (showDrawer) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
AppNavigationDrawer(),
|
|
||||||
VerticalDivider(width: 1, color: Theme.of(context).dividerColor),
|
|
||||||
Expanded(child: innerWidget),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return innerWidget;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/sn_attachment.dart';
|
import 'package:surface/providers/sn_attachment.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/post/post_item.dart';
|
import 'package:surface/widgets/post/post_item.dart';
|
||||||
import 'package:surface/widgets/post/post_mini_editor.dart';
|
import 'package:surface/widgets/post/post_mini_editor.dart';
|
||||||
@@ -14,7 +15,12 @@ import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
|||||||
|
|
||||||
class PostCommentSliverList extends StatefulWidget {
|
class PostCommentSliverList extends StatefulWidget {
|
||||||
final int parentPostId;
|
final int parentPostId;
|
||||||
const PostCommentSliverList({super.key, required this.parentPostId});
|
final double? maxWidth;
|
||||||
|
const PostCommentSliverList({
|
||||||
|
super.key,
|
||||||
|
required this.parentPostId,
|
||||||
|
this.maxWidth,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PostCommentSliverList> createState() => PostCommentSliverListState();
|
State<PostCommentSliverList> createState() => PostCommentSliverListState();
|
||||||
@@ -88,7 +94,12 @@ class PostCommentSliverListState extends State<PostCommentSliverList> {
|
|||||||
onFetchData: _fetchPosts,
|
onFetchData: _fetchPosts,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: widget.maxWidth ?? double.infinity,
|
||||||
|
),
|
||||||
child: PostItem(data: _posts[idx]),
|
child: PostItem(data: _posts[idx]),
|
||||||
|
).center(),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postDetail',
|
'postDetail',
|
||||||
@@ -121,6 +132,7 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final ua = context.watch<UserProvider>();
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
@@ -139,6 +151,7 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
|
if (ua.isAuthorized)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 240,
|
height: 240,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:relative_time/relative_time.dart';
|
import 'package:relative_time/relative_time.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
@@ -34,10 +33,6 @@ class PostItem extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isListAttachments =
|
|
||||||
ResponsiveBreakpoints.of(context).largerThan(MOBILE) ||
|
|
||||||
(data.preload?.attachments?.length ?? 0) > 1;
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -52,7 +47,8 @@ class PostItem extends StatelessWidget {
|
|||||||
data: data.preload!.attachments!,
|
data: data.preload!.attachments!,
|
||||||
bordered: true,
|
bordered: true,
|
||||||
maxHeight: 520,
|
maxHeight: 520,
|
||||||
).padding(horizontal: isListAttachments ? 12 : 0),
|
listPadding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
),
|
||||||
_PostBottomAction(
|
_PostBottomAction(
|
||||||
data: data,
|
data: data,
|
||||||
showComments: showComments,
|
showComments: showComments,
|
||||||
|
|||||||
@@ -7,16 +7,16 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <file_selector_linux/file_selector_plugin.h>
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <flutter_secure_storage/flutter_secure_storage_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStoragePlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_plugin_register_with_registrar(flutter_secure_storage_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
flutter_secure_storage
|
flutter_secure_storage_linux
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Foundation
|
|||||||
|
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
|
import flutter_secure_storage_macos
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
@@ -15,6 +16,7 @@ import url_launcher_macos
|
|||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
|
|||||||
86
pubspec.lock
86
pubspec.lock
@@ -418,10 +418,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file_selector_linux
|
name: file_selector_linux
|
||||||
sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2"
|
sha256: b2b91daf8a68ecfa4a01b778a6f52edef9b14ecd506e771488ea0f2e0784198b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3"
|
version: "0.9.3+1"
|
||||||
file_selector_macos:
|
file_selector_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -532,10 +532,50 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage
|
name: flutter_secure_storage
|
||||||
sha256: "9f3dd2ac3b6875b0fde5b04734789c3ef35ba3965c18e99dd564a7a2f8056df6"
|
sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.1"
|
version: "9.2.2"
|
||||||
|
flutter_secure_storage_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_linux
|
||||||
|
sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_macos
|
||||||
|
sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
|
flutter_secure_storage_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_platform_interface
|
||||||
|
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
flutter_secure_storage_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_web
|
||||||
|
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_windows
|
||||||
|
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
flutter_shaders:
|
flutter_shaders:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -598,10 +638,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: ce89c5a993ca5eea74535f798478502c30a625ecb10a1de4d7fef5cd1bcac2a4
|
sha256: "8ae664a70174163b9f65ea68dd8673e29db8f9095de7b5cd00e167c621f4fef5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.4.1"
|
version: "14.6.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -618,6 +658,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
|
hive:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hive
|
||||||
|
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
|
hive_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: hive_flutter
|
||||||
|
sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
hive_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: hive_generator
|
||||||
|
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
html:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -766,10 +830,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: js
|
name: js
|
||||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.1"
|
version: "0.6.7"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1347,10 +1411,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
|
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.1"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1440,7 +1504,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "0.1.6"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2.0.0+2
|
version: 2.0.0+4
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
@@ -58,7 +58,7 @@ dependencies:
|
|||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
relative_time: ^5.0.0
|
relative_time: ^5.0.0
|
||||||
flutter_secure_storage: ^4.2.1
|
flutter_secure_storage: ^9.2.2
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
cross_file: ^0.3.4+2
|
cross_file: ^0.3.4+2
|
||||||
file_picker: ^8.1.3
|
file_picker: ^8.1.3
|
||||||
@@ -73,6 +73,9 @@ dependencies:
|
|||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
mime: ^2.0.0
|
mime: ^2.0.0
|
||||||
|
web_socket_channel: ^3.0.1
|
||||||
|
hive: ^2.2.3
|
||||||
|
hive_flutter: ^1.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -89,6 +92,7 @@ dev_dependencies:
|
|||||||
json_serializable: ^6.8.0
|
json_serializable: ^6.8.0
|
||||||
icons_launcher: ^3.0.0
|
icons_launcher: ^3.0.0
|
||||||
flutter_native_splash: ^2.4.2
|
flutter_native_splash: ^2.4.2
|
||||||
|
hive_generator: ^2.0.1
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
@@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
|
flutter_secure_storage_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user