Compare commits
	
		
			3 Commits
		
	
	
		
			2.3.2+65
			...
			f0a3bbe023
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f0a3bbe023 | |||
| df81c84438 | |||
| 8b12395fca | 
@@ -220,6 +220,7 @@ class PostWriteController extends ChangeNotifier {
 | 
			
		||||
        contentController.text = post.body['content'] ?? '';
 | 
			
		||||
        aliasController.text = post.alias ?? '';
 | 
			
		||||
        rewardController.text = post.body['reward']?.toString() ?? '';
 | 
			
		||||
        videoAttachment = post.preload?.video;
 | 
			
		||||
        publishedAt = post.publishedAt;
 | 
			
		||||
        publishedUntil = post.publishedUntil;
 | 
			
		||||
        visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
 | 
			
		||||
 
 | 
			
		||||
@@ -192,11 +192,6 @@ final _appRoutes = [
 | 
			
		||||
      child: const RealmScreen(),
 | 
			
		||||
    ),
 | 
			
		||||
    routes: [
 | 
			
		||||
      GoRoute(
 | 
			
		||||
        path: '/:alias',
 | 
			
		||||
        name: 'realmDetail',
 | 
			
		||||
        builder: (context, state) => RealmDetailScreen(alias: state.pathParameters['alias']!),
 | 
			
		||||
      ),
 | 
			
		||||
      GoRoute(
 | 
			
		||||
        path: '/manage',
 | 
			
		||||
        name: 'realmManage',
 | 
			
		||||
@@ -204,6 +199,11 @@ final _appRoutes = [
 | 
			
		||||
          editingRealmAlias: state.uri.queryParameters['editing'],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      GoRoute(
 | 
			
		||||
        path: '/:alias',
 | 
			
		||||
        name: 'realmDetail',
 | 
			
		||||
        builder: (context, state) => RealmDetailScreen(alias: state.pathParameters['alias']!),
 | 
			
		||||
      ),
 | 
			
		||||
    ],
 | 
			
		||||
  ),
 | 
			
		||||
  GoRoute(path: '/news', name: 'news', builder: (context, state) => const NewsScreen(), routes: [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import 'package:collection/collection.dart';
 | 
			
		||||
import 'package:dio/dio.dart';
 | 
			
		||||
import 'package:dropdown_button2/dropdown_button2.dart';
 | 
			
		||||
import 'package:easy_localization/easy_localization.dart';
 | 
			
		||||
@@ -17,6 +18,7 @@ import 'package:uuid/uuid.dart';
 | 
			
		||||
 | 
			
		||||
class ChatManageScreen extends StatefulWidget {
 | 
			
		||||
  final String? editingChannelAlias;
 | 
			
		||||
 | 
			
		||||
  const ChatManageScreen({super.key, this.editingChannelAlias});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -33,6 +35,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
  List<SnRealm>? _realms;
 | 
			
		||||
  SnRealm? _belongToRealm;
 | 
			
		||||
 | 
			
		||||
  SnChannel? _editingChannel;
 | 
			
		||||
 | 
			
		||||
  Future<void> _fetchRealms() async {
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
    try {
 | 
			
		||||
@@ -41,6 +45,9 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
      _realms = List<SnRealm>.from(
 | 
			
		||||
        resp.data?.map((e) => SnRealm.fromJson(e)) ?? [],
 | 
			
		||||
      );
 | 
			
		||||
      if (_editingChannel != null) {
 | 
			
		||||
        _belongToRealm = _realms?.firstWhereOrNull((e) => e.id == _editingChannel!.realmId);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (mounted) context.showErrorDialog(err);
 | 
			
		||||
    } finally {
 | 
			
		||||
@@ -48,8 +55,6 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SnChannel? _editingChannel;
 | 
			
		||||
 | 
			
		||||
  Future<void> _fetchChannel() async {
 | 
			
		||||
    setState(() => _isBusy = true);
 | 
			
		||||
 | 
			
		||||
@@ -124,9 +129,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AppScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: widget.editingChannelAlias != null
 | 
			
		||||
            ? Text('screenChatManage').tr()
 | 
			
		||||
            : Text('screenChatNew').tr(),
 | 
			
		||||
        title: widget.editingChannelAlias != null ? Text('screenChatManage').tr() : Text('screenChatNew').tr(),
 | 
			
		||||
      ),
 | 
			
		||||
      body: SingleChildScrollView(
 | 
			
		||||
        child: Column(
 | 
			
		||||
@@ -138,8 +141,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                leadingPadding: const EdgeInsets.only(left: 10, right: 20),
 | 
			
		||||
                dividerColor: Colors.transparent,
 | 
			
		||||
                content: Text(
 | 
			
		||||
                  'channelEditingNotice'
 | 
			
		||||
                      .tr(args: ['#${_editingChannel!.alias}']),
 | 
			
		||||
                  'channelEditingNotice'.tr(args: ['#${_editingChannel!.alias}']),
 | 
			
		||||
                ),
 | 
			
		||||
                actions: [
 | 
			
		||||
                  TextButton(
 | 
			
		||||
@@ -162,6 +164,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                items: [
 | 
			
		||||
                  ...(_realms?.map(
 | 
			
		||||
                        (SnRealm item) => DropdownMenuItem<SnRealm>(
 | 
			
		||||
                          enabled: _editingChannel == null || _editingChannel?.realmId == item.id,
 | 
			
		||||
                          value: item,
 | 
			
		||||
                          child: Row(
 | 
			
		||||
                            children: [
 | 
			
		||||
@@ -179,15 +182,12 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                                  mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                  crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                                  children: [
 | 
			
		||||
                                    Text(item.name).textStyle(Theme.of(context)
 | 
			
		||||
                                        .textTheme
 | 
			
		||||
                                        .bodyMedium!),
 | 
			
		||||
                                    Text(item.name).textStyle(Theme.of(context).textTheme.bodyMedium!),
 | 
			
		||||
                                    Text(
 | 
			
		||||
                                      item.description,
 | 
			
		||||
                                      maxLines: 1,
 | 
			
		||||
                                      overflow: TextOverflow.ellipsis,
 | 
			
		||||
                                    ).textStyle(
 | 
			
		||||
                                        Theme.of(context).textTheme.bodySmall!),
 | 
			
		||||
                                    ).textStyle(Theme.of(context).textTheme.bodySmall!),
 | 
			
		||||
                                  ],
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
@@ -197,14 +197,14 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                      ) ??
 | 
			
		||||
                      []),
 | 
			
		||||
                  DropdownMenuItem<SnRealm>(
 | 
			
		||||
                    enabled: _editingChannel == null,
 | 
			
		||||
                    value: null,
 | 
			
		||||
                    child: Row(
 | 
			
		||||
                      children: [
 | 
			
		||||
                        CircleAvatar(
 | 
			
		||||
                          radius: 16,
 | 
			
		||||
                          backgroundColor: Colors.transparent,
 | 
			
		||||
                          foregroundColor:
 | 
			
		||||
                              Theme.of(context).colorScheme.onSurface,
 | 
			
		||||
                          foregroundColor: Theme.of(context).colorScheme.onSurface,
 | 
			
		||||
                          child: const Icon(Symbols.clear),
 | 
			
		||||
                        ),
 | 
			
		||||
                        const Gap(12),
 | 
			
		||||
@@ -213,9 +213,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                            children: [
 | 
			
		||||
                              Text('fieldChatBelongToRealmUnset')
 | 
			
		||||
                                  .tr()
 | 
			
		||||
                                  .textStyle(
 | 
			
		||||
                              Text('fieldChatBelongToRealmUnset').tr().textStyle(
 | 
			
		||||
                                    Theme.of(context).textTheme.bodyMedium!,
 | 
			
		||||
                                  ),
 | 
			
		||||
                            ],
 | 
			
		||||
@@ -231,10 +229,10 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                },
 | 
			
		||||
                buttonStyleData: const ButtonStyleData(
 | 
			
		||||
                  padding: EdgeInsets.only(right: 16),
 | 
			
		||||
                  height: 60,
 | 
			
		||||
                  height: 48,
 | 
			
		||||
                ),
 | 
			
		||||
                menuItemStyleData: const MenuItemStyleData(
 | 
			
		||||
                  height: 60,
 | 
			
		||||
                  height: 48,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@@ -250,8 +248,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                    helperText: 'fieldChatAliasHint'.tr(),
 | 
			
		||||
                    helperMaxLines: 2,
 | 
			
		||||
                  ),
 | 
			
		||||
                  onTapOutside: (_) =>
 | 
			
		||||
                      FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                  onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                ),
 | 
			
		||||
                const Gap(4),
 | 
			
		||||
                TextField(
 | 
			
		||||
@@ -260,8 +257,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                    border: const UnderlineInputBorder(),
 | 
			
		||||
                    labelText: 'fieldChatName'.tr(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  onTapOutside: (_) =>
 | 
			
		||||
                      FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                  onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                ),
 | 
			
		||||
                const Gap(4),
 | 
			
		||||
                TextField(
 | 
			
		||||
@@ -272,8 +268,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
 | 
			
		||||
                    border: const UnderlineInputBorder(),
 | 
			
		||||
                    labelText: 'fieldChatDescription'.tr(),
 | 
			
		||||
                  ),
 | 
			
		||||
                  onTapOutside: (_) =>
 | 
			
		||||
                      FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                  onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
			
		||||
                ),
 | 
			
		||||
                const Gap(12),
 | 
			
		||||
                Row(
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter/gestures.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:flutter_context_menu/flutter_context_menu.dart';
 | 
			
		||||
import 'package:gap/gap.dart';
 | 
			
		||||
import 'package:hotkey_manager/hotkey_manager.dart';
 | 
			
		||||
import 'package:material_symbols_icons/symbols.dart';
 | 
			
		||||
@@ -14,11 +15,14 @@ import 'package:responsive_framework/responsive_framework.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:surface/controllers/post_write_controller.dart';
 | 
			
		||||
import 'package:surface/providers/config.dart';
 | 
			
		||||
import 'package:surface/providers/sn_attachment.dart';
 | 
			
		||||
import 'package:surface/providers/sn_network.dart';
 | 
			
		||||
import 'package:surface/types/attachment.dart';
 | 
			
		||||
import 'package:surface/types/post.dart';
 | 
			
		||||
import 'package:surface/widgets/account/account_image.dart';
 | 
			
		||||
import 'package:surface/widgets/attachment/attachment_item.dart';
 | 
			
		||||
import 'package:surface/widgets/attachment/pending_attachment_alt.dart';
 | 
			
		||||
import 'package:surface/widgets/attachment/pending_attachment_boost.dart';
 | 
			
		||||
import 'package:surface/widgets/loading_indicator.dart';
 | 
			
		||||
import 'package:surface/widgets/markdown_content.dart';
 | 
			
		||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
 | 
			
		||||
@@ -716,6 +720,74 @@ class _PostVideoEditor extends StatelessWidget {
 | 
			
		||||
    controller.setVideoAttachment(video);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _setAlt(BuildContext context) async {
 | 
			
		||||
    if (controller.videoAttachment == null) return;
 | 
			
		||||
 | 
			
		||||
    final result = await showDialog<SnAttachment?>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) => PendingAttachmentAltDialog(media: PostWriteMedia(controller.videoAttachment)),
 | 
			
		||||
    );
 | 
			
		||||
    if (result == null) return;
 | 
			
		||||
 | 
			
		||||
    controller.setVideoAttachment(result);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _createBoost(BuildContext context) async {
 | 
			
		||||
    if (controller.videoAttachment == null) return;
 | 
			
		||||
 | 
			
		||||
    final result = await showDialog<SnAttachmentBoost?>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) => PendingAttachmentBoostDialog(media: PostWriteMedia(controller.videoAttachment)),
 | 
			
		||||
    );
 | 
			
		||||
    if (result == null) return;
 | 
			
		||||
 | 
			
		||||
    final newAttach = controller.videoAttachment!.copyWith(
 | 
			
		||||
      boosts: [...controller.videoAttachment!.boosts, result],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    controller.setVideoAttachment(newAttach);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _setThumbnail(BuildContext context) async {
 | 
			
		||||
    if (controller.videoAttachment == null) return;
 | 
			
		||||
 | 
			
		||||
    final thumbnail = await showDialog<SnAttachment?>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (context) => AttachmentInputDialog(
 | 
			
		||||
        title: 'attachmentSetThumbnail'.tr(),
 | 
			
		||||
        pool: 'interactive',
 | 
			
		||||
        analyzeNow: true,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    if (thumbnail == null) return;
 | 
			
		||||
    if (!context.mounted) return;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final attach = context.read<SnAttachmentProvider>();
 | 
			
		||||
      final newAttach = await attach.updateOne(
 | 
			
		||||
        controller.videoAttachment!,
 | 
			
		||||
        thumbnailId: thumbnail.id,
 | 
			
		||||
      );
 | 
			
		||||
      controller.setVideoAttachment(newAttach);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!context.mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _deleteAttachment(BuildContext context) async {
 | 
			
		||||
    if (controller.videoAttachment == null) return;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
      await sn.client.delete('/cgi/uc/attachments/${controller.videoAttachment!.id}');
 | 
			
		||||
      controller.setVideoAttachment(null);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      if (!context.mounted) return;
 | 
			
		||||
      context.showErrorDialog(err);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Column(
 | 
			
		||||
@@ -772,35 +844,78 @@ class _PostVideoEditor extends StatelessWidget {
 | 
			
		||||
            borderRadius: BorderRadius.circular(16),
 | 
			
		||||
            border: Border.all(color: Theme.of(context).dividerColor),
 | 
			
		||||
          ),
 | 
			
		||||
          child: InkWell(
 | 
			
		||||
            borderRadius: BorderRadius.circular(16),
 | 
			
		||||
            child: AspectRatio(
 | 
			
		||||
              aspectRatio: 16 / 9,
 | 
			
		||||
              child: controller.videoAttachment == null
 | 
			
		||||
                  ? Center(
 | 
			
		||||
                      child: Row(
 | 
			
		||||
                        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                        crossAxisAlignment: CrossAxisAlignment.center,
 | 
			
		||||
                        mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
                        children: [
 | 
			
		||||
                          const Icon(Icons.add),
 | 
			
		||||
                          const Gap(4),
 | 
			
		||||
                          Text('postVideoUpload'.tr()),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                    )
 | 
			
		||||
                  : ClipRRect(
 | 
			
		||||
                      borderRadius: BorderRadius.circular(16),
 | 
			
		||||
                      child: AttachmentItem(
 | 
			
		||||
                        data: controller.videoAttachment!,
 | 
			
		||||
                        heroTag: const Uuid().v4(),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
          child: ContextMenuRegion(
 | 
			
		||||
            contextMenu: ContextMenu(
 | 
			
		||||
              entries: [
 | 
			
		||||
                MenuItem(
 | 
			
		||||
                  label: 'attachmentSetAlt'.tr(),
 | 
			
		||||
                  icon: Symbols.description,
 | 
			
		||||
                  onSelected: () {
 | 
			
		||||
                    _setAlt(context);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                MenuItem(
 | 
			
		||||
                  label: 'attachmentBoost'.tr(),
 | 
			
		||||
                  icon: Symbols.bolt,
 | 
			
		||||
                  onSelected: () {
 | 
			
		||||
                    _createBoost(context);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                MenuItem(
 | 
			
		||||
                  label: 'attachmentSetThumbnail'.tr(),
 | 
			
		||||
                  icon: Symbols.image,
 | 
			
		||||
                  onSelected: () {
 | 
			
		||||
                    _setThumbnail(context);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                MenuItem(
 | 
			
		||||
                  label: 'attachmentCopyRandomId'.tr(),
 | 
			
		||||
                  icon: Symbols.content_copy,
 | 
			
		||||
                  onSelected: () {
 | 
			
		||||
                    Clipboard.setData(ClipboardData(text: controller.videoAttachment!.rid));
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                MenuItem(
 | 
			
		||||
                  label: 'delete'.tr(),
 | 
			
		||||
                  icon: Symbols.delete,
 | 
			
		||||
                  onSelected: () => _deleteAttachment(context),
 | 
			
		||||
                ),
 | 
			
		||||
                MenuItem(
 | 
			
		||||
                  label: 'unlink'.tr(),
 | 
			
		||||
                  icon: Symbols.link_off,
 | 
			
		||||
                  onSelected: () {
 | 
			
		||||
                    controller.setVideoAttachment(null);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            child: InkWell(
 | 
			
		||||
              borderRadius: BorderRadius.circular(16),
 | 
			
		||||
              onTap: controller.videoAttachment != null ? () => _selectVideo(context) : null,
 | 
			
		||||
              child: AspectRatio(
 | 
			
		||||
                aspectRatio: 16 / 9,
 | 
			
		||||
                child: controller.videoAttachment == null
 | 
			
		||||
                    ? Center(
 | 
			
		||||
                        child: Row(
 | 
			
		||||
                          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                          crossAxisAlignment: CrossAxisAlignment.center,
 | 
			
		||||
                          mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
                          children: [
 | 
			
		||||
                            const Icon(Icons.add),
 | 
			
		||||
                            const Gap(4),
 | 
			
		||||
                            Text('postVideoUpload'.tr()),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      )
 | 
			
		||||
                    : ClipRRect(
 | 
			
		||||
                        borderRadius: BorderRadius.circular(16),
 | 
			
		||||
                        child: AttachmentItem(
 | 
			
		||||
                          data: controller.videoAttachment!,
 | 
			
		||||
                          heroTag: const Uuid().v4(),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            onTap: () {
 | 
			
		||||
              if (controller.videoAttachment != null) return;
 | 
			
		||||
              _selectVideo(context);
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:styled_widget/styled_widget.dart';
 | 
			
		||||
import 'package:surface/providers/sn_network.dart';
 | 
			
		||||
import 'package:surface/providers/user_directory.dart';
 | 
			
		||||
import 'package:surface/providers/userinfo.dart';
 | 
			
		||||
import 'package:surface/types/account.dart';
 | 
			
		||||
import 'package:surface/widgets/account/account_image.dart';
 | 
			
		||||
 | 
			
		||||
@@ -49,10 +50,19 @@ class _AccountSelectState extends State<AccountSelect> {
 | 
			
		||||
  Future<void> _getFriends() async {
 | 
			
		||||
    final sn = context.read<SnNetworkProvider>();
 | 
			
		||||
    final resp = await sn.client.get('/cgi/id/users/me/relations?status=1');
 | 
			
		||||
    if (!mounted) return;
 | 
			
		||||
    final ua = context.read<UserProvider>();
 | 
			
		||||
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _relativeUsers.addAll(
 | 
			
		||||
        resp.data?.map((e) => SnRelationship.fromJson(e)) ?? [],
 | 
			
		||||
        resp.data?.map((e) {
 | 
			
		||||
          final rel = SnRelationship.fromJson(e);
 | 
			
		||||
          if (rel.relatedId == ua.user?.id) {
 | 
			
		||||
            return rel.account!;
 | 
			
		||||
          } else {
 | 
			
		||||
            return rel.related!;
 | 
			
		||||
          }
 | 
			
		||||
        }).cast<SnAccount>(),
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
@@ -123,13 +133,9 @@ class _AccountSelectState extends State<AccountSelect> {
 | 
			
		||||
          ),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: ListView.builder(
 | 
			
		||||
              itemCount: _pendingUsers.isEmpty
 | 
			
		||||
                  ? _relativeUsers.length
 | 
			
		||||
                  : _pendingUsers.length,
 | 
			
		||||
              itemCount: _pendingUsers.isEmpty ? _relativeUsers.length : _pendingUsers.length,
 | 
			
		||||
              itemBuilder: (context, index) {
 | 
			
		||||
                var user = _pendingUsers.isEmpty
 | 
			
		||||
                    ? _relativeUsers[index]
 | 
			
		||||
                    : _pendingUsers[index];
 | 
			
		||||
                var user = _pendingUsers.isEmpty ? _relativeUsers[index] : _pendingUsers[index];
 | 
			
		||||
                return ListTile(
 | 
			
		||||
                  title: Text(user.nick),
 | 
			
		||||
                  subtitle: Text(user.name),
 | 
			
		||||
@@ -148,8 +154,7 @@ class _AccountSelectState extends State<AccountSelect> {
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          setState(() {
 | 
			
		||||
                            final idx = _selectedUsers
 | 
			
		||||
                                .indexWhere((x) => x.id == user.id);
 | 
			
		||||
                            final idx = _selectedUsers.indexWhere((x) => x.id == user.id);
 | 
			
		||||
                            if (idx != -1) {
 | 
			
		||||
                              _selectedUsers.removeAt(idx);
 | 
			
		||||
                            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -203,6 +203,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _contentController.dispose();
 | 
			
		||||
    _focusNode.dispose();
 | 
			
		||||
    _dismissEmojiPicker();
 | 
			
		||||
    if (!kIsWeb && !(Platform.isAndroid || Platform.isIOS)) hotKeyManager.unregister(_pasteHotKey);
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user