✨ Account screen after logged in
This commit is contained in:
parent
d7d9e41db3
commit
057ab16381
@ -21,5 +21,6 @@
|
||||
"nickname": "Nickname",
|
||||
"email": "Email",
|
||||
"fieldCannotBeEmpty": "This field cannot be empty.",
|
||||
"fieldEmailAddressMustBeValid": "The email address must be valid."
|
||||
"fieldEmailAddressMustBeValid": "The email address must be valid.",
|
||||
"logout": "Logout"
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/theme.dart';
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -48,13 +49,22 @@ void main() async {
|
||||
|
||||
final _appRouter = AppRouter();
|
||||
|
||||
class IslandApp extends ConsumerWidget {
|
||||
class IslandApp extends HookConsumerWidget {
|
||||
const IslandApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themeProvider);
|
||||
|
||||
useEffect(() {
|
||||
final userNotifier = ref.read(userInfoProvider.notifier);
|
||||
Future(() {
|
||||
userNotifier.fetchUser();
|
||||
print('user fetched');
|
||||
});
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return MaterialApp.router(
|
||||
theme: theme?.light,
|
||||
darkTheme: theme?.dark,
|
||||
|
42
lib/models/user.dart
Normal file
42
lib/models/user.dart
Normal file
@ -0,0 +1,42 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
|
||||
part 'user.freezed.dart';
|
||||
part 'user.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class SnAccount with _$SnAccount {
|
||||
const factory SnAccount({
|
||||
required int id,
|
||||
required String name,
|
||||
required String nick,
|
||||
required String language,
|
||||
required bool isSuperuser,
|
||||
required SnAccountProfile profile,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
}) = _SnAccount;
|
||||
|
||||
factory SnAccount.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnAccountFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class SnAccountProfile with _$SnAccountProfile {
|
||||
const factory SnAccountProfile({
|
||||
required int id,
|
||||
required String? firstName,
|
||||
required String? middleName,
|
||||
required String? lastName,
|
||||
required String? bio,
|
||||
required SnCloudFile? picture,
|
||||
required SnCloudFile? background,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
}) = _SnAccountProfile;
|
||||
|
||||
factory SnAccountProfile.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnAccountProfileFromJson(json);
|
||||
}
|
398
lib/models/user.freezed.dart
Normal file
398
lib/models/user.freezed.dart
Normal file
@ -0,0 +1,398 @@
|
||||
// dart format width=80
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnAccount {
|
||||
|
||||
int get id; String get name; String get nick; String get language; bool get isSuperuser; SnAccountProfile get profile; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
/// Create a copy of SnAccount
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnAccountCopyWith<SnAccount> get copyWith => _$SnAccountCopyWithImpl<SnAccount>(this as SnAccount, _$identity);
|
||||
|
||||
/// Serializes this SnAccount to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,name,nick,language,isSuperuser,profile,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, isSuperuser: $isSuperuser, profile: $profile, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnAccountCopyWith<$Res> {
|
||||
factory $SnAccountCopyWith(SnAccount value, $Res Function(SnAccount) _then) = _$SnAccountCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
int id, String name, String nick, String language, bool isSuperuser, SnAccountProfile profile, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
$SnAccountProfileCopyWith<$Res> get profile;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnAccountCopyWithImpl<$Res>
|
||||
implements $SnAccountCopyWith<$Res> {
|
||||
_$SnAccountCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnAccount _self;
|
||||
final $Res Function(SnAccount) _then;
|
||||
|
||||
/// Create a copy of SnAccount
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? isSuperuser = null,Object? profile = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as int,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
|
||||
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
|
||||
as String,isSuperuser: null == isSuperuser ? _self.isSuperuser : isSuperuser // ignore: cast_nullable_to_non_nullable
|
||||
as bool,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
||||
as SnAccountProfile,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnAccount
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnAccountProfileCopyWith<$Res> get profile {
|
||||
|
||||
return $SnAccountProfileCopyWith<$Res>(_self.profile, (value) {
|
||||
return _then(_self.copyWith(profile: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnAccount implements SnAccount {
|
||||
const _SnAccount({required this.id, required this.name, required this.nick, required this.language, required this.isSuperuser, required this.profile, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||
factory _SnAccount.fromJson(Map<String, dynamic> json) => _$SnAccountFromJson(json);
|
||||
|
||||
@override final int id;
|
||||
@override final String name;
|
||||
@override final String nick;
|
||||
@override final String language;
|
||||
@override final bool isSuperuser;
|
||||
@override final SnAccountProfile profile;
|
||||
@override final DateTime createdAt;
|
||||
@override final DateTime updatedAt;
|
||||
@override final DateTime? deletedAt;
|
||||
|
||||
/// Create a copy of SnAccount
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnAccountCopyWith<_SnAccount> get copyWith => __$SnAccountCopyWithImpl<_SnAccount>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnAccountToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccount&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.language, language) || other.language == language)&&(identical(other.isSuperuser, isSuperuser) || other.isSuperuser == isSuperuser)&&(identical(other.profile, profile) || other.profile == profile)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,name,nick,language,isSuperuser,profile,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAccount(id: $id, name: $name, nick: $nick, language: $language, isSuperuser: $isSuperuser, profile: $profile, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnAccountCopyWith<$Res> implements $SnAccountCopyWith<$Res> {
|
||||
factory _$SnAccountCopyWith(_SnAccount value, $Res Function(_SnAccount) _then) = __$SnAccountCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
int id, String name, String nick, String language, bool isSuperuser, SnAccountProfile profile, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
@override $SnAccountProfileCopyWith<$Res> get profile;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnAccountCopyWithImpl<$Res>
|
||||
implements _$SnAccountCopyWith<$Res> {
|
||||
__$SnAccountCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnAccount _self;
|
||||
final $Res Function(_SnAccount) _then;
|
||||
|
||||
/// Create a copy of SnAccount
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? nick = null,Object? language = null,Object? isSuperuser = null,Object? profile = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_SnAccount(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as int,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,nick: null == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
|
||||
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
|
||||
as String,isSuperuser: null == isSuperuser ? _self.isSuperuser : isSuperuser // ignore: cast_nullable_to_non_nullable
|
||||
as bool,profile: null == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
||||
as SnAccountProfile,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnAccount
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnAccountProfileCopyWith<$Res> get profile {
|
||||
|
||||
return $SnAccountProfileCopyWith<$Res>(_self.profile, (value) {
|
||||
return _then(_self.copyWith(profile: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnAccountProfile {
|
||||
|
||||
int get id; String? get firstName; String? get middleName; String? get lastName; String? get bio; SnCloudFile? get picture; SnCloudFile? get background; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnAccountProfileCopyWith<SnAccountProfile> get copyWith => _$SnAccountProfileCopyWithImpl<SnAccountProfile>(this as SnAccountProfile, _$identity);
|
||||
|
||||
/// Serializes this SnAccountProfile to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,firstName,middleName,lastName,bio,picture,background,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, picture: $picture, background: $background, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnAccountProfileCopyWith<$Res> {
|
||||
factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
int id, String? firstName, String? middleName, String? lastName, String? bio, SnCloudFile? picture, SnCloudFile? background, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnAccountProfileCopyWithImpl<$Res>
|
||||
implements $SnAccountProfileCopyWith<$Res> {
|
||||
_$SnAccountProfileCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnAccountProfile _self;
|
||||
final $Res Function(SnAccountProfile) _then;
|
||||
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = freezed,Object? middleName = freezed,Object? lastName = freezed,Object? bio = freezed,Object? picture = freezed,Object? background = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as int,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,middleName: freezed == middleName ? _self.middleName : middleName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,lastName: freezed == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,bio: freezed == bio ? _self.bio : bio // ignore: cast_nullable_to_non_nullable
|
||||
as String?,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res>? get picture {
|
||||
if (_self.picture == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
|
||||
return _then(_self.copyWith(picture: value));
|
||||
});
|
||||
}/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res>? get background {
|
||||
if (_self.background == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
|
||||
return _then(_self.copyWith(background: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnAccountProfile implements SnAccountProfile {
|
||||
const _SnAccountProfile({required this.id, required this.firstName, required this.middleName, required this.lastName, required this.bio, required this.picture, required this.background, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json);
|
||||
|
||||
@override final int id;
|
||||
@override final String? firstName;
|
||||
@override final String? middleName;
|
||||
@override final String? lastName;
|
||||
@override final String? bio;
|
||||
@override final SnCloudFile? picture;
|
||||
@override final SnCloudFile? background;
|
||||
@override final DateTime createdAt;
|
||||
@override final DateTime updatedAt;
|
||||
@override final DateTime? deletedAt;
|
||||
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnAccountProfileCopyWith<_SnAccountProfile> get copyWith => __$SnAccountProfileCopyWithImpl<_SnAccountProfile>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnAccountProfileToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,firstName,middleName,lastName,bio,picture,background,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, picture: $picture, background: $background, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnAccountProfileCopyWith<$Res> implements $SnAccountProfileCopyWith<$Res> {
|
||||
factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
int id, String? firstName, String? middleName, String? lastName, String? bio, SnCloudFile? picture, SnCloudFile? background, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnAccountProfileCopyWithImpl<$Res>
|
||||
implements _$SnAccountProfileCopyWith<$Res> {
|
||||
__$SnAccountProfileCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnAccountProfile _self;
|
||||
final $Res Function(_SnAccountProfile) _then;
|
||||
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = freezed,Object? middleName = freezed,Object? lastName = freezed,Object? bio = freezed,Object? picture = freezed,Object? background = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_SnAccountProfile(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as int,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,middleName: freezed == middleName ? _self.middleName : middleName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,lastName: freezed == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,bio: freezed == bio ? _self.bio : bio // ignore: cast_nullable_to_non_nullable
|
||||
as String?,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res>? get picture {
|
||||
if (_self.picture == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) {
|
||||
return _then(_self.copyWith(picture: value));
|
||||
});
|
||||
}/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res>? get background {
|
||||
if (_self.background == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.background!, (value) {
|
||||
return _then(_self.copyWith(background: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
74
lib/models/user.g.dart
Normal file
74
lib/models/user.g.dart
Normal file
@ -0,0 +1,74 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_SnAccount _$SnAccountFromJson(Map<String, dynamic> json) => _SnAccount(
|
||||
id: (json['id'] as num).toInt(),
|
||||
name: json['name'] as String,
|
||||
nick: json['nick'] as String,
|
||||
language: json['language'] as String,
|
||||
isSuperuser: json['is_superuser'] as bool,
|
||||
profile: SnAccountProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
deletedAt:
|
||||
json['deleted_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnAccountToJson(_SnAccount instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'nick': instance.nick,
|
||||
'language': instance.language,
|
||||
'is_superuser': instance.isSuperuser,
|
||||
'profile': instance.profile.toJson(),
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
_SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
||||
_SnAccountProfile(
|
||||
id: (json['id'] as num).toInt(),
|
||||
firstName: json['first_name'] as String?,
|
||||
middleName: json['middle_name'] as String?,
|
||||
lastName: json['last_name'] as String?,
|
||||
bio: json['bio'] as String?,
|
||||
picture:
|
||||
json['picture'] == null
|
||||
? null
|
||||
: SnCloudFile.fromJson(json['picture'] as Map<String, dynamic>),
|
||||
background:
|
||||
json['background'] == null
|
||||
? null
|
||||
: SnCloudFile.fromJson(
|
||||
json['background'] as Map<String, dynamic>,
|
||||
),
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
deletedAt:
|
||||
json['deleted_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'first_name': instance.firstName,
|
||||
'middle_name': instance.middleName,
|
||||
'last_name': instance.lastName,
|
||||
'bio': instance.bio,
|
||||
'picture': instance.picture?.toJson(),
|
||||
'background': instance.background?.toJson(),
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
};
|
40
lib/pods/userinfo.dart
Normal file
40
lib/pods/userinfo.dart
Normal file
@ -0,0 +1,40 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/user.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
|
||||
class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> {
|
||||
final Ref _ref;
|
||||
|
||||
UserInfoNotifier(this._ref) : super(const AsyncValue.data(null));
|
||||
|
||||
Future<String?> getAccessToken() async {
|
||||
final prefs = _ref.read(sharedPreferencesProvider);
|
||||
return prefs.getString('dyn_user_atk');
|
||||
}
|
||||
|
||||
Future<void> fetchUser() async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
final client = _ref.read(apiClientProvider);
|
||||
final response = await client.get('/accounts/me');
|
||||
final user = SnAccount.fromJson(response.data);
|
||||
state = AsyncValue.data(user);
|
||||
} catch (error, stackTrace) {
|
||||
print('Failed to fetch user: $error');
|
||||
state = AsyncValue.error(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logOut() async {
|
||||
state = const AsyncValue.data(null);
|
||||
final prefs = _ref.read(sharedPreferencesProvider);
|
||||
await prefs.remove(kTokenPairStoreKey);
|
||||
}
|
||||
}
|
||||
|
||||
final userInfoProvider =
|
||||
StateNotifierProvider<UserInfoNotifier, AsyncValue<SnAccount?>>(
|
||||
(ref) => UserInfoNotifier(ref),
|
||||
);
|
@ -8,8 +8,17 @@ class AppRouter extends RootStackRouter {
|
||||
|
||||
@override
|
||||
List<AutoRoute> get routes => [
|
||||
AutoRoute(page: ExploreRoute.page, path: '/'),
|
||||
AutoRoute(page: AccountRoute.page, path: '/account'),
|
||||
AutoRoute(
|
||||
page: ExploreRoute.page,
|
||||
path: '/',
|
||||
meta: {'bottomNav': true},
|
||||
initial: true,
|
||||
),
|
||||
AutoRoute(
|
||||
page: AccountRoute.page,
|
||||
path: '/account',
|
||||
meta: {'bottomNav': true},
|
||||
),
|
||||
AutoRoute(page: LoginRoute.page, path: '/auth/login'),
|
||||
AutoRoute(page: CreateAccountRoute.page, path: '/auth/create-account'),
|
||||
];
|
||||
|
@ -1,12 +1,102 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/route.gr.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
@RoutePage()
|
||||
class AccountScreen extends StatelessWidget {
|
||||
class AccountScreen extends HookConsumerWidget {
|
||||
const AccountScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final user = ref.watch(userInfoProvider);
|
||||
|
||||
if (!user.hasValue) return _UnauthorizedAccountScreen();
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(title: const Text('Account')),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Card(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (user.value!.profile.background != null)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 7,
|
||||
child: CloudFileWidget(
|
||||
item: user.value!.profile.background!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
ProfilePictureWidget(
|
||||
item: user.value!.profile.picture,
|
||||
radius: 24,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 4,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(user.value!.nick).bold().fontSize(16),
|
||||
Text('@${user.value!.name}'),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
user.value!.profile.bio ?? 'No description yet.',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 16, vertical: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(LucideIcons.logOut),
|
||||
trailing: const Icon(LucideIcons.chevronRight),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||
title: Text('logout').tr(),
|
||||
subtitle: Text('Log out of your account.'),
|
||||
onTap: () {
|
||||
final userNotifier = ref.read(userInfoProvider.notifier);
|
||||
userNotifier.logOut();
|
||||
},
|
||||
),
|
||||
],
|
||||
).padding(
|
||||
horizontal: 8,
|
||||
top: 8,
|
||||
bottom: MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UnauthorizedAccountScreen extends StatelessWidget {
|
||||
const _UnauthorizedAccountScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
@ -14,15 +104,23 @@ class AccountScreen extends StatelessWidget {
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text('Login'),
|
||||
leading: const Icon(LucideIcons.userPlus),
|
||||
trailing: const Icon(LucideIcons.chevronRight),
|
||||
title: Text('createAccount').tr(),
|
||||
subtitle: Text('New to here? We got you covered!'),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||
onTap: () {
|
||||
context.router.push(LoginRoute());
|
||||
context.router.push(CreateAccountRoute());
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Create an account'),
|
||||
leading: const Icon(LucideIcons.logIn),
|
||||
trailing: const Icon(LucideIcons.chevronRight),
|
||||
subtitle: Text('Existing user? We\'re welcome you back!'),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||
title: Text('login').tr(),
|
||||
onTap: () {
|
||||
context.router.push(CreateAccountRoute());
|
||||
context.router.push(LoginRoute());
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -9,7 +9,7 @@ import 'package:island/pods/network.dart';
|
||||
import 'package:island/route.gr.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
@ -83,7 +83,7 @@ class CreateAccountScreen extends HookConsumerWidget {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: CircleAvatar(
|
||||
radius: 26,
|
||||
child: Icon(MdiIcons.accountPlus, size: 28),
|
||||
child: const Icon(LucideIcons.userPlus, size: 28),
|
||||
).padding(bottom: 8),
|
||||
),
|
||||
Text(
|
||||
@ -223,7 +223,10 @@ class CreateAccountScreen extends HookConsumerWidget {
|
||||
children: [
|
||||
Text('termAcceptLink').tr(),
|
||||
const Gap(4),
|
||||
Icon(MdiIcons.launch, size: 14),
|
||||
const Icon(
|
||||
LucideIcons.externalLink,
|
||||
size: 14,
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
@ -250,7 +253,7 @@ class CreateAccountScreen extends HookConsumerWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("next").tr(),
|
||||
Icon(MdiIcons.chevronRight),
|
||||
const Icon(LucideIcons.chevronRight),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -8,9 +8,10 @@ import 'package:gap/gap.dart';
|
||||
import 'package:island/models/auth.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
@ -128,7 +129,8 @@ class _LoginCheckScreen extends HookConsumerWidget {
|
||||
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
||||
ref.invalidate(tokenPairProvider);
|
||||
if (!context.mounted) return;
|
||||
// TODO userinfo
|
||||
final userNotifier = ref.read(userInfoProvider.notifier);
|
||||
userNotifier.fetchUser();
|
||||
Navigator.pop(context, true);
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
@ -145,7 +147,7 @@ class _LoginCheckScreen extends HookConsumerWidget {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: CircleAvatar(
|
||||
radius: 26,
|
||||
child: Icon(MdiIcons.formTextboxPassword, size: 28),
|
||||
child: const Icon(LucideIcons.squareAsterisk, size: 28),
|
||||
).padding(bottom: 8),
|
||||
),
|
||||
Text(
|
||||
@ -178,7 +180,10 @@ class _LoginCheckScreen extends HookConsumerWidget {
|
||||
onPressed: isBusy.value ? null : () => performCheckTicket(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Text('next').tr(), Icon(MdiIcons.chevronRight)],
|
||||
children: [
|
||||
Text('next').tr(),
|
||||
const Icon(LucideIcons.chevronRight),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -242,7 +247,7 @@ class _LoginPickerScreen extends HookConsumerWidget {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: CircleAvatar(
|
||||
radius: 26,
|
||||
child: Icon(MdiIcons.security, size: 28),
|
||||
child: const Icon(LucideIcons.lock, size: 28),
|
||||
).padding(bottom: 8),
|
||||
),
|
||||
Text(
|
||||
@ -260,7 +265,7 @@ class _LoginPickerScreen extends HookConsumerWidget {
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
secondary: Icon(MdiIcons.fileQuestion),
|
||||
secondary: const Icon(LucideIcons.shieldQuestion),
|
||||
title: Text('unknown').tr(),
|
||||
enabled: !ticket!.blacklistFactors.contains(x.id),
|
||||
value: factorPicked.value == x.id,
|
||||
@ -288,7 +293,10 @@ class _LoginPickerScreen extends HookConsumerWidget {
|
||||
onPressed: isBusy.value ? null : () => performGetFactorCode(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Text('next'.tr()), Icon(MdiIcons.chevronRight)],
|
||||
children: [
|
||||
Text('next'.tr()),
|
||||
const Icon(LucideIcons.chevronRight),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -375,7 +383,7 @@ class _LoginLookupScreen extends HookConsumerWidget {
|
||||
alignment: Alignment.centerLeft,
|
||||
child: CircleAvatar(
|
||||
radius: 26,
|
||||
child: Icon(MdiIcons.login, size: 28),
|
||||
child: const Icon(LucideIcons.logIn, size: 28),
|
||||
).padding(bottom: 8),
|
||||
),
|
||||
Text(
|
||||
@ -409,7 +417,10 @@ class _LoginLookupScreen extends HookConsumerWidget {
|
||||
onPressed: isBusy.value ? null : () => performNewTicket(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Text('next').tr(), Icon(MdiIcons.chevronRight)],
|
||||
children: [
|
||||
Text('next').tr(),
|
||||
const Icon(LucideIcons.chevronRight),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -440,7 +451,7 @@ class _LoginLookupScreen extends HookConsumerWidget {
|
||||
children: [
|
||||
Text('termAcceptLink'.tr()),
|
||||
const Gap(4),
|
||||
Icon(MdiIcons.launch, size: 14),
|
||||
const Icon(LucideIcons.externalLink, size: 14),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
|
@ -4,9 +4,11 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:island/route.gr.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@ -79,28 +81,54 @@ class WindowScaffold extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
extendBody: true,
|
||||
extendBodyBehindAppBar: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
body: SizedBox.expand(child: child),
|
||||
key: rootScaffoldKey,
|
||||
bottomNavigationBar: NavigationBar(
|
||||
destinations: [
|
||||
NavigationDestination(icon: Icon(MdiIcons.compass), label: 'Explore'),
|
||||
NavigationDestination(icon: Icon(MdiIcons.account), label: 'Account'),
|
||||
],
|
||||
onDestinationSelected: (idx) {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
router.replace(ExploreRoute());
|
||||
break;
|
||||
case 1:
|
||||
router.replace(AccountRoute());
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
bottomNavigationBar:
|
||||
router.current.meta['bottomNav'] == true || router.currentPath == '/'
|
||||
? AppBottomNavigationBar(router: router)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppBottomNavigationBar extends HookConsumerWidget {
|
||||
const AppBottomNavigationBar({super.key, required this.router});
|
||||
|
||||
final AppRouter router;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final destination = useState(0);
|
||||
|
||||
return NavigationBar(
|
||||
selectedIndex: destination.value,
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
icon: Icon(LucideIcons.compass),
|
||||
label: 'Explore',
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(LucideIcons.userCircle),
|
||||
label: 'Account',
|
||||
),
|
||||
],
|
||||
onDestinationSelected: (idx) {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
destination.value = idx;
|
||||
router.replace(ExploreRoute());
|
||||
break;
|
||||
case 1:
|
||||
destination.value = idx;
|
||||
router.replace(AccountRoute());
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import 'image.dart';
|
||||
import 'video.dart';
|
||||
@ -35,7 +35,7 @@ class CloudFileWidget extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
default:
|
||||
return Placeholder();
|
||||
return Text('Unable render for ${item.mimeType}');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,7 @@ class ProfilePictureWidget extends ConsumerWidget {
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
child:
|
||||
item == null
|
||||
? Icon(MdiIcons.account)
|
||||
? Icon(LucideIcons.userCircle)
|
||||
: CloudFileWidget(item: item!),
|
||||
),
|
||||
);
|
||||
|
16
pubspec.lock
16
pubspec.lock
@ -733,6 +733,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lucide_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lucide_icons
|
||||
sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.257.0"
|
||||
markdown:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -757,14 +765,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
material_design_icons_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: material_design_icons_flutter
|
||||
sha256: "6f986b7a51f3ad4c00e33c5c84e8de1bdd140489bbcdc8b66fc1283dad4dea5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.7296"
|
||||
media_kit:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -63,13 +63,13 @@ dependencies:
|
||||
media_kit_libs_video: ^1.0.6
|
||||
flutter_cache_manager: ^3.4.1
|
||||
flutter_platform_alert: ^0.7.0
|
||||
material_design_icons_flutter: ^7.0.7296
|
||||
email_validator: ^3.0.0
|
||||
easy_localization: ^3.0.7+1
|
||||
flutter_inappwebview: ^6.1.5
|
||||
animations: ^2.0.11
|
||||
package_info_plus: ^8.3.0
|
||||
device_info_plus: ^11.4.0
|
||||
lucide_icons: ^0.257.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
x
Reference in New Issue
Block a user