mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 08:46:18 +01:00
Reintroduce legacy sign in using username and password
This commit is contained in:
parent
bfcf0c4586
commit
69dada413b
9 changed files with 419 additions and 31 deletions
|
@ -1501,6 +1501,10 @@
|
|||
"homeTabMapBrowser": "Map",
|
||||
"mapBrowserSetDefaultDateRangeButton": "Set as default",
|
||||
"todayText": "Today",
|
||||
"alternativeSignIn": "Alternative sign in with username and password",
|
||||
"@alternativeSignIn": {
|
||||
"description": "Sign in using username and password instead of the recommended Nextcloud login flow"
|
||||
},
|
||||
|
||||
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
|
||||
"@errorUnauthenticated": {
|
||||
|
|
|
@ -260,6 +260,7 @@
|
|||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText",
|
||||
"alternativeSignIn",
|
||||
"errorUnauthenticated",
|
||||
"errorDisconnected",
|
||||
"errorLocked",
|
||||
|
@ -272,7 +273,8 @@
|
|||
|
||||
"cs": [
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"de": [
|
||||
|
@ -292,7 +294,8 @@
|
|||
"searchLandingPeopleListEmptyText2",
|
||||
"mapBrowserDateRangeLabel",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"el": [
|
||||
|
@ -440,12 +443,14 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"fi": [
|
||||
|
@ -484,7 +489,8 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
|
@ -523,7 +529,8 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"it": [
|
||||
|
@ -567,7 +574,8 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"nl": [
|
||||
|
@ -948,6 +956,7 @@
|
|||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText",
|
||||
"alternativeSignIn",
|
||||
"errorUnauthenticated",
|
||||
"errorDisconnected",
|
||||
"errorLocked",
|
||||
|
@ -998,7 +1007,8 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
|
@ -1057,7 +1067,8 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
|
@ -1096,7 +1107,12 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"tr": [
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
|
@ -1166,7 +1182,8 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
],
|
||||
|
||||
"zh_Hant": [
|
||||
|
@ -1330,6 +1347,7 @@
|
|||
"mapBrowserDateRangeCustom",
|
||||
"homeTabMapBrowser",
|
||||
"mapBrowserSetDefaultDateRangeButton",
|
||||
"todayText"
|
||||
"todayText",
|
||||
"alternativeSignIn"
|
||||
]
|
||||
}
|
||||
|
|
72
app/lib/widget/expandable_container.dart
Normal file
72
app/lib/widget/expandable_container.dart
Normal file
|
@ -0,0 +1,72 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/widget/measure.dart';
|
||||
|
||||
class ExpandableContainer extends StatefulWidget {
|
||||
const ExpandableContainer({
|
||||
super.key,
|
||||
required this.isShow,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => ExpandableContainerState();
|
||||
|
||||
final bool isShow;
|
||||
final Widget child;
|
||||
}
|
||||
|
||||
class ExpandableContainerState extends State<ExpandableContainer>
|
||||
with TickerProviderStateMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
duration: k.animationDurationNormal,
|
||||
vsync: this,
|
||||
value: 0,
|
||||
);
|
||||
_animation = CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ExpandableContainer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.isShow != widget.isShow) {
|
||||
if (widget.isShow) {
|
||||
_animationController.animateTo(1);
|
||||
} else {
|
||||
_animationController.animateBack(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MatrixTransition(
|
||||
animation: _animation,
|
||||
onTransform: (animationValue) => Matrix4.identity()
|
||||
..translate(0.0, -(_size.height / 2) * (1 - animationValue), 0.0)
|
||||
..scale(1.0, animationValue, 1.0),
|
||||
child: MeasureSize(
|
||||
onChange: (size) => setState(() {
|
||||
_size = size;
|
||||
}),
|
||||
child: widget.child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _animation;
|
||||
var _size = Size.zero;
|
||||
}
|
|
@ -12,16 +12,17 @@ import 'package:nc_photos/db/entity_converter.dart';
|
|||
import 'package:nc_photos/entity/pref.dart';
|
||||
import 'package:nc_photos/entity/pref_util.dart' as pref_util;
|
||||
import 'package:nc_photos/exception_event.dart';
|
||||
import 'package:nc_photos/legacy/connect.dart' as legacy;
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/widget/app_intermediate_circular_progress_indicator.dart';
|
||||
import 'package:nc_photos/widget/connect.dart';
|
||||
import 'package:nc_photos/widget/expandable_container.dart';
|
||||
import 'package:nc_photos/widget/home.dart';
|
||||
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||
import 'package:nc_photos/widget/root_picker.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:np_collection/np_collection.dart';
|
||||
import 'package:np_common/unique.dart';
|
||||
import 'package:np_db/np_db.dart';
|
||||
import 'package:np_string/np_string.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
@ -49,7 +50,6 @@ class SignIn extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _WrappedSignIn extends StatefulWidget {
|
||||
const _WrappedSignIn();
|
||||
|
||||
|
@ -57,6 +57,7 @@ class _WrappedSignIn extends StatefulWidget {
|
|||
State<StatefulWidget> createState() => _WrappedSignInState();
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _WrappedSignInState extends State<_WrappedSignIn>
|
||||
with RouteAware, PageVisibilityMixin {
|
||||
@override
|
||||
|
@ -81,10 +82,18 @@ class _WrappedSignInState extends State<_WrappedSignIn>
|
|||
body: MultiBlocListener(
|
||||
listeners: [
|
||||
_BlocListenerT(
|
||||
selector: (state) => state.connectUri,
|
||||
listener: (context, connectUri) {
|
||||
if (connectUri != null) {
|
||||
_onConnect(context, connectUri.value);
|
||||
selector: (state) => state.connectArg,
|
||||
listener: (context, connectArg) {
|
||||
if (connectArg == null) {
|
||||
return;
|
||||
}
|
||||
if (connectArg.username != null &&
|
||||
connectArg.password != null) {
|
||||
_onLegacyConnect(context, connectArg);
|
||||
} else {
|
||||
final uri = Uri.parse(
|
||||
"${connectArg.scheme}://${connectArg.address}");
|
||||
_onConnect(context, uri);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -145,6 +154,39 @@ class _WrappedSignInState extends State<_WrappedSignIn>
|
|||
// we've got a good account
|
||||
context.addEvent(_SetConnectedAccount(account));
|
||||
}
|
||||
|
||||
Future<void> _onLegacyConnect(BuildContext context, _ConnectArg arg) async {
|
||||
Account? account = Account(
|
||||
id: Account.newId(),
|
||||
scheme: arg.scheme,
|
||||
address: arg.address,
|
||||
userId: arg.username!.toCi(),
|
||||
username2: arg.username!,
|
||||
password: arg.password!,
|
||||
roots: [""],
|
||||
);
|
||||
_log.info("[_onLegacyConnect] Try connecting with account: $account");
|
||||
account = await Navigator.pushNamed<Account>(
|
||||
context,
|
||||
legacy.Connect.routeName,
|
||||
arguments: legacy.ConnectArguments(account),
|
||||
);
|
||||
if (account == null) {
|
||||
// connection failed
|
||||
return;
|
||||
}
|
||||
account = await Navigator.pushNamed<Account>(
|
||||
context,
|
||||
RootPicker.routeName,
|
||||
arguments: RootPickerArguments(account),
|
||||
);
|
||||
if (account == null) {
|
||||
// ???
|
||||
return;
|
||||
}
|
||||
// we've got a good account
|
||||
context.addEvent(_SetConnectedAccount(account));
|
||||
}
|
||||
}
|
||||
|
||||
// typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
|
||||
|
|
|
@ -16,10 +16,14 @@ abstract class $_StateCopyWithWorker {
|
|||
_State call(
|
||||
{_Scheme? scheme,
|
||||
String? serverUrl,
|
||||
Unique<Uri>? connectUri,
|
||||
String? username,
|
||||
String? password,
|
||||
bool? shouldObscurePassword,
|
||||
_ConnectArg? connectArg,
|
||||
Account? connectedAccount,
|
||||
bool? isConnecting,
|
||||
bool? isCompleted,
|
||||
bool? isAltMode,
|
||||
ExceptionEvent? error});
|
||||
}
|
||||
|
||||
|
@ -30,22 +34,31 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
|
|||
_State call(
|
||||
{dynamic scheme,
|
||||
dynamic serverUrl,
|
||||
dynamic connectUri = copyWithNull,
|
||||
dynamic username,
|
||||
dynamic password,
|
||||
dynamic shouldObscurePassword,
|
||||
dynamic connectArg = copyWithNull,
|
||||
dynamic connectedAccount = copyWithNull,
|
||||
dynamic isConnecting,
|
||||
dynamic isCompleted,
|
||||
dynamic isAltMode,
|
||||
dynamic error = copyWithNull}) {
|
||||
return _State(
|
||||
scheme: scheme as _Scheme? ?? that.scheme,
|
||||
serverUrl: serverUrl as String? ?? that.serverUrl,
|
||||
connectUri: connectUri == copyWithNull
|
||||
? that.connectUri
|
||||
: connectUri as Unique<Uri>?,
|
||||
username: username as String? ?? that.username,
|
||||
password: password as String? ?? that.password,
|
||||
shouldObscurePassword:
|
||||
shouldObscurePassword as bool? ?? that.shouldObscurePassword,
|
||||
connectArg: connectArg == copyWithNull
|
||||
? that.connectArg
|
||||
: connectArg as _ConnectArg?,
|
||||
connectedAccount: connectedAccount == copyWithNull
|
||||
? that.connectedAccount
|
||||
: connectedAccount as Account?,
|
||||
isConnecting: isConnecting as bool? ?? that.isConnecting,
|
||||
isCompleted: isCompleted as bool? ?? that.isCompleted,
|
||||
isAltMode: isAltMode as bool? ?? that.isAltMode,
|
||||
error: error == copyWithNull ? that.error : error as ExceptionEvent?);
|
||||
}
|
||||
|
||||
|
@ -61,11 +74,11 @@ extension $_StateCopyWith on _State {
|
|||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_WrappedSignInNpLog on _WrappedSignIn {
|
||||
extension _$_WrappedSignInStateNpLog on _WrappedSignInState {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log = Logger("widget.sign_in._WrappedSignIn");
|
||||
static final log = Logger("widget.sign_in._WrappedSignInState");
|
||||
}
|
||||
|
||||
extension _$_BlocNpLog on _Bloc {
|
||||
|
@ -82,7 +95,7 @@ extension _$_BlocNpLog on _Bloc {
|
|||
extension _$_StateToString on _State {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_State {scheme: ${scheme.name}, serverUrl: $serverUrl, connectUri: $connectUri, connectedAccount: $connectedAccount, isConnecting: $isConnecting, isCompleted: $isCompleted, error: $error}";
|
||||
return "_State {scheme: ${scheme.name}, serverUrl: $serverUrl, username: $username, password: $password, shouldObscurePassword: $shouldObscurePassword, connectArg: $connectArg, connectedAccount: $connectedAccount, isConnecting: $isConnecting, isCompleted: $isCompleted, isAltMode: $isAltMode, error: $error}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,9 +127,44 @@ extension _$_SetConnectedAccountToString on _SetConnectedAccount {
|
|||
}
|
||||
}
|
||||
|
||||
extension _$_SetAltModeToString on _SetAltMode {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetAltMode {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetUsernameToString on _SetUsername {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetUsername {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetPasswordToString on _SetPassword {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetPassword {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetObscurePasswordToString on _SetObscurePassword {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetObscurePassword {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetErrorToString on _SetError {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_SetError {error: $error, stackTrace: $stackTrace}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_ConnectArgToString on _ConnectArg {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_ConnectArg {scheme: $scheme, address: $address, username: $username, password: $password}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
on<_SetServerUrl>(_onSetServerUrl);
|
||||
on<_Connect>(_onConnect);
|
||||
on<_SetConnectedAccount>(_onSetConnectedAccount);
|
||||
on<_SetAltMode>(_onSetAltMode);
|
||||
on<_SetUsername>(_onSetUsername);
|
||||
on<_SetPassword>(_onSetPassword);
|
||||
on<_SetObscurePassword>(_onSetObscurePassword);
|
||||
|
||||
on<_SetError>(_onSetError);
|
||||
}
|
||||
|
@ -44,9 +48,22 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
_log.info(ev);
|
||||
final scheme = state.scheme.toValueString();
|
||||
final serverUrl = state.serverUrl.trim().trimRightAny("/");
|
||||
final uri = Uri.parse("$scheme://$serverUrl");
|
||||
_log.info("[_onConnect] Try connecting with url: $uri");
|
||||
emit(state.copyWith(connectUri: Unique(uri)));
|
||||
final _ConnectArg arg;
|
||||
if (!state.isAltMode) {
|
||||
arg = _ConnectArg(
|
||||
scheme: scheme,
|
||||
address: serverUrl,
|
||||
);
|
||||
} else {
|
||||
arg = _ConnectArg(
|
||||
scheme: scheme,
|
||||
address: serverUrl,
|
||||
username: state.username,
|
||||
password: state.password,
|
||||
);
|
||||
}
|
||||
_log.info("[_onConnect] Try connecting: $arg");
|
||||
emit(state.copyWith(connectArg: arg));
|
||||
}
|
||||
|
||||
Future<void> _onSetConnectedAccount(
|
||||
|
@ -65,6 +82,26 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
}
|
||||
}
|
||||
|
||||
void _onSetAltMode(_SetAltMode ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(isAltMode: ev.value));
|
||||
}
|
||||
|
||||
void _onSetUsername(_SetUsername ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(username: ev.value));
|
||||
}
|
||||
|
||||
void _onSetPassword(_SetPassword ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(password: ev.value));
|
||||
}
|
||||
|
||||
void _onSetObscurePassword(_SetObscurePassword ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(shouldObscurePassword: ev.value));
|
||||
}
|
||||
|
||||
void _onSetError(_SetError ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
|
||||
|
|
|
@ -6,18 +6,26 @@ class _State {
|
|||
const _State({
|
||||
required this.scheme,
|
||||
required this.serverUrl,
|
||||
this.connectUri,
|
||||
required this.username,
|
||||
required this.password,
|
||||
required this.shouldObscurePassword,
|
||||
this.connectArg,
|
||||
this.connectedAccount,
|
||||
required this.isConnecting,
|
||||
required this.isCompleted,
|
||||
required this.isAltMode,
|
||||
this.error,
|
||||
});
|
||||
|
||||
factory _State.init() => const _State(
|
||||
scheme: _Scheme.https,
|
||||
serverUrl: "",
|
||||
username: "",
|
||||
password: "",
|
||||
shouldObscurePassword: true,
|
||||
isConnecting: false,
|
||||
isCompleted: false,
|
||||
isAltMode: false,
|
||||
);
|
||||
|
||||
@override
|
||||
|
@ -25,11 +33,16 @@ class _State {
|
|||
|
||||
final _Scheme scheme;
|
||||
final String serverUrl;
|
||||
final Unique<Uri>? connectUri;
|
||||
final String username;
|
||||
final String password;
|
||||
final bool shouldObscurePassword;
|
||||
final _ConnectArg? connectArg;
|
||||
final Account? connectedAccount;
|
||||
final bool isConnecting;
|
||||
final bool isCompleted;
|
||||
|
||||
final bool isAltMode;
|
||||
|
||||
final ExceptionEvent? error;
|
||||
}
|
||||
|
||||
|
@ -73,6 +86,46 @@ class _SetConnectedAccount implements _Event {
|
|||
final Account value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetAltMode implements _Event {
|
||||
const _SetAltMode(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetUsername implements _Event {
|
||||
const _SetUsername(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final String value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetPassword implements _Event {
|
||||
const _SetPassword(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final String value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetObscurePassword implements _Event {
|
||||
const _SetObscurePassword(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetError implements _Event {
|
||||
const _SetError(this.error, [this.stackTrace]);
|
||||
|
|
|
@ -14,3 +14,21 @@ enum _Scheme {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@toString
|
||||
class _ConnectArg {
|
||||
const _ConnectArg({
|
||||
required this.scheme,
|
||||
required this.address,
|
||||
this.username,
|
||||
this.password,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final String scheme;
|
||||
final String address;
|
||||
final String? username;
|
||||
final String? password;
|
||||
}
|
||||
|
|
|
@ -173,6 +173,35 @@ class _SignInBody extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Stack(
|
||||
children: [
|
||||
_BlocSelector(
|
||||
selector: (state) => state.isAltMode,
|
||||
builder: (context, isAltMode) => ExpandableContainer(
|
||||
isShow: isAltMode,
|
||||
child: const _LegacySignInForm(),
|
||||
),
|
||||
),
|
||||
_BlocSelector(
|
||||
selector: (state) => state.isAltMode,
|
||||
builder: (context, isAltMode) => Visibility(
|
||||
visible: !isAltMode,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.addEvent(const _SetAltMode(true));
|
||||
},
|
||||
child: Text(
|
||||
L10n.global().alternativeSignIn,
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -228,3 +257,70 @@ class _ServerUrlInput extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LegacySignInForm extends StatelessWidget {
|
||||
const _LegacySignInForm();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.global().usernameInputHint,
|
||||
),
|
||||
keyboardType: TextInputType.text,
|
||||
validator: (value) {
|
||||
if (!context.state.isAltMode) {
|
||||
return null;
|
||||
}
|
||||
if (value!.trim().isEmpty) {
|
||||
return L10n.global().usernameInputInvalidEmpty;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (value) {
|
||||
context.addEvent(_SetUsername(value));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_BlocSelector(
|
||||
selector: (state) => state.shouldObscurePassword,
|
||||
builder: (context, shouldObscurePassword) => TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.global().passwordInputHint,
|
||||
suffixIcon: shouldObscurePassword
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.visibility_off_outlined),
|
||||
onPressed: () {
|
||||
context.addEvent(const _SetObscurePassword(false));
|
||||
},
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.visibility_outlined),
|
||||
onPressed: () {
|
||||
context.addEvent(const _SetObscurePassword(true));
|
||||
},
|
||||
),
|
||||
),
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: shouldObscurePassword,
|
||||
validator: (value) {
|
||||
if (!context.state.isAltMode) {
|
||||
return null;
|
||||
}
|
||||
if (value!.trim().isEmpty) {
|
||||
return L10n.global().passwordInputInvalidEmpty;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (value) {
|
||||
context.addEvent(_SetPassword(value));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue