343 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
						|
import 'package:gap/gap.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:island/models/account.dart';
 | 
						|
import 'package:island/pods/network.dart';
 | 
						|
import 'package:island/widgets/alert.dart';
 | 
						|
import 'package:island/widgets/content/sheet.dart';
 | 
						|
import 'package:material_symbols_icons/symbols.dart';
 | 
						|
import 'package:styled_widget/styled_widget.dart';
 | 
						|
 | 
						|
class ContactMethodSheet extends HookConsumerWidget {
 | 
						|
  final SnContactMethod contact;
 | 
						|
  const ContactMethodSheet({super.key, required this.contact});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    Future<void> deleteContactMethod() async {
 | 
						|
      final confirm = await showConfirmAlert(
 | 
						|
        'contactMethodDeleteHint'.tr(),
 | 
						|
        'contactMethodDelete'.tr(),
 | 
						|
      );
 | 
						|
      if (!confirm || !context.mounted) return;
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final client = ref.read(apiClientProvider);
 | 
						|
        await client.delete('/pass/accounts/me/contacts/${contact.id}');
 | 
						|
        if (context.mounted) Navigator.pop(context, true);
 | 
						|
      } catch (err) {
 | 
						|
        showErrorAlert(err);
 | 
						|
      } finally {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Future<void> verifyContactMethod() async {
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final client = ref.read(apiClientProvider);
 | 
						|
        await client.post('/pass/accounts/me/contacts/${contact.id}/verify');
 | 
						|
        if (context.mounted) {
 | 
						|
          showSnackBar('contactMethodVerificationSent'.tr());
 | 
						|
        }
 | 
						|
      } catch (err) {
 | 
						|
        showErrorAlert(err);
 | 
						|
      } finally {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Future<void> setContactMethodAsPrimary() async {
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final client = ref.read(apiClientProvider);
 | 
						|
        await client.post('/pass/accounts/me/contacts/${contact.id}/primary');
 | 
						|
        if (context.mounted) Navigator.pop(context, true);
 | 
						|
      } catch (err) {
 | 
						|
        showErrorAlert(err);
 | 
						|
      } finally {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Future<void> makeContactMethodPublic() async {
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final client = ref.read(apiClientProvider);
 | 
						|
        await client.post('/pass/accounts/me/contacts/${contact.id}/public');
 | 
						|
        if (context.mounted) Navigator.pop(context, true);
 | 
						|
      } catch (err) {
 | 
						|
        showErrorAlert(err);
 | 
						|
      } finally {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Future<void> makeContactMethodPrivate() async {
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final client = ref.read(apiClientProvider);
 | 
						|
        await client.delete('/pass/accounts/me/contacts/${contact.id}/public');
 | 
						|
        if (context.mounted) Navigator.pop(context, true);
 | 
						|
      } catch (err) {
 | 
						|
        showErrorAlert(err);
 | 
						|
      } finally {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return SheetScaffold(
 | 
						|
      titleText: 'contactMethod'.tr(),
 | 
						|
      child: Column(
 | 
						|
        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
						|
        children: [
 | 
						|
          Column(
 | 
						|
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
						|
            mainAxisAlignment: MainAxisAlignment.center,
 | 
						|
            children: [
 | 
						|
              Icon(switch (contact.type) {
 | 
						|
                0 => Symbols.mail,
 | 
						|
                1 => Symbols.phone,
 | 
						|
                _ => Symbols.home,
 | 
						|
              }, size: 32),
 | 
						|
              const Gap(8),
 | 
						|
              Text(switch (contact.type) {
 | 
						|
                0 => 'contactMethodTypeEmail'.tr(),
 | 
						|
                1 => 'contactMethodTypePhone'.tr(),
 | 
						|
                _ => 'contactMethodTypeAddress'.tr(),
 | 
						|
              }),
 | 
						|
              const Gap(4),
 | 
						|
              Text(
 | 
						|
                contact.content,
 | 
						|
                style: Theme.of(context).textTheme.bodySmall,
 | 
						|
              ),
 | 
						|
              const Gap(10),
 | 
						|
              Row(
 | 
						|
                children: [
 | 
						|
                  if (contact.verifiedAt == null)
 | 
						|
                    Badge(
 | 
						|
                      label: Text('contactMethodUnverified'.tr()),
 | 
						|
                      textColor: Theme.of(context).colorScheme.onSecondary,
 | 
						|
                      backgroundColor: Theme.of(context).colorScheme.secondary,
 | 
						|
                    )
 | 
						|
                  else
 | 
						|
                    Badge(
 | 
						|
                      label: Text('contactMethodVerified'.tr()),
 | 
						|
                      textColor: Theme.of(context).colorScheme.onPrimary,
 | 
						|
                      backgroundColor: Theme.of(context).colorScheme.primary,
 | 
						|
                    ),
 | 
						|
                  if (contact.isPrimary)
 | 
						|
                    Padding(
 | 
						|
                      padding: const EdgeInsets.only(left: 8.0),
 | 
						|
                      child: Badge(
 | 
						|
                        label: Text('contactMethodPrimary'.tr()),
 | 
						|
                        textColor: Theme.of(context).colorScheme.onTertiary,
 | 
						|
                        backgroundColor: Theme.of(context).colorScheme.tertiary,
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  if (contact.isPublic)
 | 
						|
                    Padding(
 | 
						|
                      padding: const EdgeInsets.only(left: 8.0),
 | 
						|
                      child: Badge(
 | 
						|
                        label: Text('contactMethodPublic'.tr()),
 | 
						|
                        textColor: Theme.of(context).colorScheme.onPrimary,
 | 
						|
                        backgroundColor: Theme.of(context).colorScheme.primary,
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                  if (!contact.isPublic)
 | 
						|
                    Padding(
 | 
						|
                      padding: const EdgeInsets.only(left: 8.0),
 | 
						|
                      child: Badge(
 | 
						|
                        label: Text('contactMethodPrivate'.tr()),
 | 
						|
                        textColor: Theme.of(context).colorScheme.onSurface,
 | 
						|
                        backgroundColor:
 | 
						|
                            Theme.of(
 | 
						|
                              context,
 | 
						|
                            ).colorScheme.surfaceContainerHighest,
 | 
						|
                      ),
 | 
						|
                    ),
 | 
						|
                ],
 | 
						|
              ),
 | 
						|
            ],
 | 
						|
          ).padding(all: 20),
 | 
						|
          const Divider(height: 1),
 | 
						|
          if (contact.verifiedAt == null)
 | 
						|
            ListTile(
 | 
						|
              leading: const Icon(Symbols.verified),
 | 
						|
              title: Text('contactMethodVerify').tr(),
 | 
						|
              onTap: verifyContactMethod,
 | 
						|
              contentPadding: EdgeInsets.symmetric(horizontal: 20),
 | 
						|
            ),
 | 
						|
          if (contact.verifiedAt != null && !contact.isPrimary)
 | 
						|
            ListTile(
 | 
						|
              leading: const Icon(Symbols.star),
 | 
						|
              title: Text('contactMethodSetPrimary').tr(),
 | 
						|
              onTap: setContactMethodAsPrimary,
 | 
						|
              contentPadding: EdgeInsets.symmetric(horizontal: 20),
 | 
						|
            ),
 | 
						|
          if (contact.verifiedAt != null && !contact.isPublic)
 | 
						|
            ListTile(
 | 
						|
              leading: const Icon(Symbols.public),
 | 
						|
              title: Text('contactMethodMakePublic').tr(),
 | 
						|
              onTap: makeContactMethodPublic,
 | 
						|
              contentPadding: EdgeInsets.symmetric(horizontal: 20),
 | 
						|
            ),
 | 
						|
          if (contact.verifiedAt != null && contact.isPublic)
 | 
						|
            ListTile(
 | 
						|
              leading: const Icon(Symbols.visibility_off),
 | 
						|
              title: Text('contactMethodMakePrivate').tr(),
 | 
						|
              onTap: makeContactMethodPrivate,
 | 
						|
              contentPadding: EdgeInsets.symmetric(horizontal: 20),
 | 
						|
            ),
 | 
						|
          ListTile(
 | 
						|
            leading: const Icon(Symbols.delete),
 | 
						|
            title: Text('contactMethodDelete').tr(),
 | 
						|
            onTap: deleteContactMethod,
 | 
						|
            contentPadding: EdgeInsets.symmetric(horizontal: 20),
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class ContactMethodNewSheet extends HookConsumerWidget {
 | 
						|
  const ContactMethodNewSheet({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final contactType = useState<int>(0);
 | 
						|
    final contentController = useTextEditingController();
 | 
						|
 | 
						|
    Future<void> addContactMethod() async {
 | 
						|
      if (contentController.text.isEmpty) {
 | 
						|
        showSnackBar('contactMethodContentEmpty'.tr());
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      try {
 | 
						|
        showLoadingModal(context);
 | 
						|
        final apiClient = ref.read(apiClientProvider);
 | 
						|
        await apiClient.post(
 | 
						|
          '/pass/accounts/me/contacts',
 | 
						|
          data: {'type': contactType.value, 'content': contentController.text},
 | 
						|
        );
 | 
						|
        if (context.mounted) {
 | 
						|
          showSnackBar('contactMethodVerificationNeeded'.tr());
 | 
						|
          Navigator.pop(context, true);
 | 
						|
        }
 | 
						|
      } catch (err) {
 | 
						|
        showErrorAlert(err);
 | 
						|
      } finally {
 | 
						|
        if (context.mounted) hideLoadingModal(context);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return SheetScaffold(
 | 
						|
      titleText: 'contactMethodNew'.tr(),
 | 
						|
      child: Column(
 | 
						|
        spacing: 16,
 | 
						|
        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
						|
        children: [
 | 
						|
          DropdownButtonFormField<int>(
 | 
						|
            value: contactType.value,
 | 
						|
            decoration: InputDecoration(
 | 
						|
              labelText: 'contactMethodType'.tr(),
 | 
						|
              border: const OutlineInputBorder(),
 | 
						|
            ),
 | 
						|
            items: [
 | 
						|
              DropdownMenuItem<int>(
 | 
						|
                value: 0,
 | 
						|
                child: Row(
 | 
						|
                  children: [
 | 
						|
                    Icon(Symbols.mail),
 | 
						|
                    const Gap(8),
 | 
						|
                    Text('contactMethodTypeEmail'.tr()),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
              DropdownMenuItem<int>(
 | 
						|
                value: 1,
 | 
						|
                child: Row(
 | 
						|
                  children: [
 | 
						|
                    Icon(Symbols.phone),
 | 
						|
                    const Gap(8),
 | 
						|
                    Text('contactMethodTypePhone'.tr()),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
              DropdownMenuItem<int>(
 | 
						|
                value: 2,
 | 
						|
                child: Row(
 | 
						|
                  children: [
 | 
						|
                    Icon(Symbols.home),
 | 
						|
                    const Gap(8),
 | 
						|
                    Text('contactMethodTypeAddress'.tr()),
 | 
						|
                  ],
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ],
 | 
						|
            onChanged: (value) {
 | 
						|
              if (value != null) {
 | 
						|
                contactType.value = value;
 | 
						|
              }
 | 
						|
            },
 | 
						|
          ),
 | 
						|
          TextField(
 | 
						|
            controller: contentController,
 | 
						|
            decoration: InputDecoration(
 | 
						|
              prefixIcon: Icon(switch (contactType.value) {
 | 
						|
                0 => Symbols.mail,
 | 
						|
                1 => Symbols.phone,
 | 
						|
                _ => Symbols.home,
 | 
						|
              }),
 | 
						|
              labelText: switch (contactType.value) {
 | 
						|
                0 => 'contactMethodTypeEmail'.tr(),
 | 
						|
                1 => 'contactMethodTypePhone'.tr(),
 | 
						|
                _ => 'contactMethodTypeAddress'.tr(),
 | 
						|
              },
 | 
						|
              hintText: switch (contactType.value) {
 | 
						|
                0 => 'contactMethodEmailHint'.tr(),
 | 
						|
                1 => 'contactMethodPhoneHint'.tr(),
 | 
						|
                _ => 'contactMethodAddressHint'.tr(),
 | 
						|
              },
 | 
						|
              border: const OutlineInputBorder(),
 | 
						|
            ),
 | 
						|
            keyboardType: switch (contactType.value) {
 | 
						|
              0 => TextInputType.emailAddress,
 | 
						|
              1 => TextInputType.phone,
 | 
						|
              _ => TextInputType.multiline,
 | 
						|
            },
 | 
						|
            maxLines: switch (contactType.value) {
 | 
						|
              2 => 3,
 | 
						|
              _ => 1,
 | 
						|
            },
 | 
						|
            onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
 | 
						|
          ),
 | 
						|
          Padding(
 | 
						|
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
 | 
						|
            child:
 | 
						|
                Text(switch (contactType.value) {
 | 
						|
                  0 => 'contactMethodEmailDescription',
 | 
						|
                  1 => 'contactMethodPhoneDescription',
 | 
						|
                  _ => 'contactMethodAddressDescription',
 | 
						|
                }).tr(),
 | 
						|
          ),
 | 
						|
          Row(
 | 
						|
            mainAxisAlignment: MainAxisAlignment.end,
 | 
						|
            children: [
 | 
						|
              TextButton.icon(
 | 
						|
                onPressed: addContactMethod,
 | 
						|
                icon: Icon(Symbols.add),
 | 
						|
                label: Text('create').tr(),
 | 
						|
              ),
 | 
						|
            ],
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
      ).padding(horizontal: 20, vertical: 24),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |