Skip to content

Commit

Permalink
Merge branch 'master' into 423-add-token-by-paste-link
Browse files Browse the repository at this point in the history
  • Loading branch information
frankmer committed Nov 8, 2024
2 parents d7ed719 + f78f020 commit 4339ac5
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* privacyIDEA Authenticator
*
* Author: Frank Merkel <[email protected]>
*
* Copyright (c) 2024 NetKnights GmbH
*
* 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:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class PageViewDotIndicator extends ConsumerStatefulWidget {
final PageController controller;
final List<Widget> icons;
const PageViewDotIndicator({super.key, required this.controller, required this.icons});

@override
ConsumerState<PageViewDotIndicator> createState() => _PageViewDotIndicatorState();
}

class _PageViewDotIndicatorState extends ConsumerState<PageViewDotIndicator> {
int _currentPage = 0;

@override
void initState() {
super.initState();
widget.controller.addListener(() {
setState(() {
_currentPage = widget.controller.page?.round() ?? 0;
});
});
}

@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final widthPerIcon = constraints.maxWidth / (widget.icons.length + 1);
final space = widthPerIcon * 0.1;
final double iconWidth = widthPerIcon - space;
return SizedBox(
width: constraints.maxWidth,
height: 50,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (int i = 0; i < widget.icons.length; i++) ...[
GestureDetector(
onTap: () {
final pageDifference = (i - _currentPage).abs();
widget.controller.animateToPage(i, duration: Duration(milliseconds: 200 * pageDifference + 150), curve: Curves.easeInOut);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
width: _currentPage == i ? iconWidth * 2 : iconWidth,
decoration: BoxDecoration(
color: _currentPage == i ? Theme.of(context).primaryColor : Theme.of(context).disabledColor,
borderRadius: BorderRadius.circular(5),
),
child: widget.icons[i],
),
),
if (i < widget.icons.length - 1) SizedBox(width: space),
]
],
),
);
},
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* privacyIDEA Authenticator
*
* Author: Frank Merkel <[email protected]>
*
* Copyright (c) 2024 NetKnights GmbH
*
* 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:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:privacyidea_authenticator/utils/riverpod_providers.dart';

import '../../../l10n/app_localizations.dart';

class LinkInputView extends ConsumerStatefulWidget {
const LinkInputView({super.key});

@override
ConsumerState<LinkInputView> createState() => _LinkInputViewState();
}

class _LinkInputViewState extends ConsumerState<LinkInputView> {
final textController = TextEditingController();

Future<void> addToken(Uri link) async {
if (link.scheme != 'otpauth') {
ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.linkMustOtpAuth, '');
return;
}
await ref.read(tokenProvider.notifier).handleLink(link);
if (!mounted) return;
Navigator.of(context).pop();
}

@override
Widget build(BuildContext context) => Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Row(
children: [
Expanded(
child: TextFormField(
controller: textController,
decoration: InputDecoration(
labelText: AppLocalizations.of(context)!.tokenLink,
),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
onFieldSubmitted: (text) => addToken(Uri.parse(text)),
validator: (value) => value != null
? Uri.tryParse(value) == null
? AppLocalizations.of(context)!.invalidUrl
: null
: null,
),
),
SizedBox(width: 8),
IconButton(
icon: Icon(Icons.paste),
onPressed: () async {
ClipboardData? data = await Clipboard.getData('text/plain');
if (data == null || data.text == null || data.text!.isEmpty) {
if (context.mounted) ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.clipboardEmpty, '');
return;
}
setState(() => textController.text = data.text ?? '');
},
),
],
),
Expanded(child: SizedBox()),
SizedBox(
width: double.infinity,
child: ElevatedButton(
child: Text(
AppLocalizations.of(context)!.addToken,
style: Theme.of(context).textTheme.headlineSmall,
overflow: TextOverflow.fade,
softWrap: false,
),
onPressed: () => addToken(Uri.parse(textController.text)),
),
),
],
);
}

0 comments on commit 4339ac5

Please sign in to comment.