yubioath-flutter/lib/oath/icon_provider/icon_pack_dialog.dart

236 lines
8.1 KiB
Dart
Raw Normal View History

2023-02-21 19:51:55 +03:00
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:file_picker/file_picker.dart';
2023-02-22 13:27:05 +03:00
import 'package:flutter/gestures.dart';
2023-02-21 19:51:55 +03:00
import 'package:flutter/material.dart';
2023-02-22 17:23:57 +03:00
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
2023-02-21 19:51:55 +03:00
import 'package:flutter_riverpod/flutter_riverpod.dart';
2024-03-08 11:30:47 +03:00
import 'package:material_symbols_icons/symbols.dart';
2023-02-21 19:51:55 +03:00
import 'package:url_launcher/url_launcher.dart';
import '../../app/message.dart';
import '../../app/state.dart';
2024-01-10 18:06:12 +03:00
import '../../widgets/file_drop_overlay.dart';
import '../../widgets/file_drop_target.dart';
import '../../widgets/responsive_dialog.dart';
import 'icon_pack.dart';
import 'icon_pack_manager.dart';
2023-02-21 19:51:55 +03:00
class IconPackDialog extends ConsumerWidget {
const IconPackDialog({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
2023-02-22 17:23:57 +03:00
final l10n = AppLocalizations.of(context)!;
2023-02-23 12:46:33 +03:00
final iconPack = ref.watch(iconPackProvider);
2024-01-10 18:06:12 +03:00
return FileDropTarget(
onFileDropped: (file) async {
final importStatus = await ref
.read(iconPackProvider.notifier)
.importPack(l10n, file.path);
await ref.read(withContextProvider)((context) async {
if (importStatus) {
showMessage(context, l10n.l_icon_pack_imported);
} else {
showMessage(
context,
l10n.l_import_icon_pack_failed(
ref.read(iconPackProvider.notifier).lastError ??
l10n.l_import_error));
}
});
},
overlay: FileDropOverlay(
title: iconPack.when(
data: (IconPack? data) => data != null
? l10n.s_replace_icon_pack
: l10n.s_load_icon_pack,
error: (Object error, StackTrace stackTrace) =>
l10n.s_load_icon_pack,
loading: () => null),
2023-02-21 19:51:55 +03:00
),
2024-01-10 18:06:12 +03:00
child: ResponsiveDialog(
title: Text(l10n.s_custom_icons),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_DialogDescription(),
const SizedBox(height: 4),
_action(iconPack, l10n),
_loadedIconPackRow(iconPack),
]
.map((e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: e,
))
.toList(),
),
),
));
2023-02-21 19:51:55 +03:00
}
2023-02-23 12:46:33 +03:00
Widget? _loadedIconPackRow(AsyncValue<IconPack?> iconPack) {
return iconPack.when(
data: (IconPack? data) =>
(data != null) ? _IconPackDescription(data) : null,
error: (Object error, StackTrace stackTrace) => null,
loading: () => const Padding(
// Add extra padding to have same size as _IconPackDescription
padding: EdgeInsets.symmetric(vertical: 18.0),
child: LinearProgressIndicator(),
));
2023-02-23 12:46:33 +03:00
}
2023-02-21 19:51:55 +03:00
2023-02-23 12:46:33 +03:00
Widget? _action(AsyncValue<IconPack?> iconPack, AppLocalizations l10n) =>
iconPack.when(
2023-03-01 11:34:13 +03:00
data: (IconPack? data) => _ImportActionChip(
data != null ? l10n.s_replace_icon_pack : l10n.s_load_icon_pack),
2023-02-23 12:46:33 +03:00
error: (Object error, StackTrace stackTrace) =>
_ImportActionChip(l10n.s_load_icon_pack),
2023-02-23 12:46:33 +03:00
loading: () => _ImportActionChip(
2023-03-01 11:34:13 +03:00
l10n.l_loading_icon_pack,
disabled: true,
2023-02-23 12:46:33 +03:00
));
}
2023-02-21 19:51:55 +03:00
2023-02-23 12:46:33 +03:00
class _DialogDescription extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final l10n = AppLocalizations.of(context)!;
return RichText(
text: TextSpan(
2023-03-01 11:34:13 +03:00
text: l10n.p_custom_icons_description,
2023-02-23 12:46:33 +03:00
style: theme.textTheme.bodyMedium,
children: [const TextSpan(text: ' '), _createLearnMoreLink(context)],
2023-02-23 12:46:33 +03:00
),
);
2023-02-21 19:51:55 +03:00
}
2023-02-22 13:27:05 +03:00
2023-02-22 18:35:18 +03:00
Uri get _learnMoreUri =>
Uri.parse('https://yubi.co/ya-custom-account-icons-doc');
2023-02-22 18:35:18 +03:00
2023-02-22 17:47:44 +03:00
TextSpan _createLearnMoreLink(BuildContext context) {
2023-02-22 13:27:05 +03:00
final theme = Theme.of(context);
return TextSpan(
text: AppLocalizations.of(context)!.s_learn_more,
2023-02-22 17:47:44 +03:00
style: theme.textTheme.bodyMedium
?.copyWith(color: theme.colorScheme.primary),
2023-02-22 13:27:05 +03:00
recognizer: TapGestureRecognizer()
..onTap = () async {
2023-02-22 18:35:18 +03:00
await launchUrl(_learnMoreUri, mode: LaunchMode.externalApplication);
2023-02-22 13:27:05 +03:00
},
2023-02-22 17:47:44 +03:00
children: const [
TextSpan(text: ' ') // without this the recognizer takes over whole row
],
2023-02-22 13:27:05 +03:00
);
}
2023-02-21 19:51:55 +03:00
}
2023-02-23 12:46:33 +03:00
class _IconPackDescription extends ConsumerWidget {
final IconPack iconPack;
const _IconPackDescription(this.iconPack);
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context)!;
final theme = Theme.of(context);
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
fit: FlexFit.loose,
child: RichText(
text: TextSpan(
text: iconPack.name,
style: theme.textTheme.bodyMedium,
children: [TextSpan(text: ' (${iconPack.version})')]))),
Row(
children: [
IconButton(
tooltip: l10n.s_remove_icon_pack,
2023-02-23 12:46:33 +03:00
onPressed: () async {
final removePackStatus =
await ref.read(iconPackProvider.notifier).removePack();
await ref.read(withContextProvider)(
(context) async {
if (removePackStatus) {
2023-03-01 11:34:13 +03:00
showMessage(context, l10n.l_icon_pack_removed);
2023-02-23 12:46:33 +03:00
} else {
2023-03-01 11:34:13 +03:00
showMessage(context, l10n.l_remove_icon_pack_failed);
2023-02-23 12:46:33 +03:00
}
},
);
},
2024-03-08 16:05:06 +03:00
icon: const Icon(Symbols.delete)),
2023-02-23 12:46:33 +03:00
],
)
]);
}
}
class _ImportActionChip extends ConsumerWidget {
final String _label;
final bool disabled;
2023-02-23 12:46:33 +03:00
const _ImportActionChip(this._label, {this.disabled = false});
2023-02-23 12:46:33 +03:00
@override
Widget build(BuildContext context, WidgetRef ref) {
return ActionChip(
2023-12-20 17:09:31 +03:00
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
onPressed: !disabled
? () async {
_importAction(context, ref);
}
: null,
2024-03-08 11:30:47 +03:00
avatar: const Icon(Symbols.download),
2023-02-23 12:46:33 +03:00
label: Text(_label));
}
void _importAction(BuildContext context, WidgetRef ref) async {
final l10n = AppLocalizations.of(context)!;
final result = await FilePicker.platform.pickFiles(
allowedExtensions: ['zip'],
type: FileType.custom,
allowMultiple: false,
lockParentWindow: true,
dialogTitle: l10n.s_choose_icon_pack);
2023-02-23 12:46:33 +03:00
if (result != null && result.files.isNotEmpty) {
final importStatus = await ref
.read(iconPackProvider.notifier)
.importPack(l10n, result.paths.first!);
await ref.read(withContextProvider)((context) async {
if (importStatus) {
2023-03-01 11:34:13 +03:00
showMessage(context, l10n.l_icon_pack_imported);
2023-02-23 12:46:33 +03:00
} else {
showMessage(
context,
2023-03-01 11:34:13 +03:00
l10n.l_import_icon_pack_failed(
2023-02-23 12:46:33 +03:00
ref.read(iconPackProvider.notifier).lastError ??
2023-03-01 11:34:13 +03:00
l10n.l_import_error));
2023-02-23 12:46:33 +03:00
}
});
}
}
}