Compare commits
5 Commits
9bdf8ba346
...
3.1.0+119
| Author | SHA1 | Date | |
|---|---|---|---|
| 87ae8d2ff4 | |||
| 15c2dbaa0d | |||
| 6b3338b885 | |||
| bb00b1bc6a | |||
| 5e1a15ada2 |
@@ -786,5 +786,6 @@
|
|||||||
"links": "Links",
|
"links": "Links",
|
||||||
"addLink": "Add link",
|
"addLink": "Add link",
|
||||||
"linkKey": "Link Name",
|
"linkKey": "Link Name",
|
||||||
"linkValue": "URL"
|
"linkValue": "URL",
|
||||||
|
"debugOptions": "Debug Options"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:island/models/publisher.dart';
|
||||||
|
|
||||||
part 'developer.freezed.dart';
|
part 'developer.freezed.dart';
|
||||||
part 'developer.g.dart';
|
part 'developer.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnDeveloper with _$SnDeveloper {
|
||||||
|
const factory SnDeveloper({
|
||||||
|
required String id,
|
||||||
|
required String publisherId,
|
||||||
|
SnPublisher? publisher,
|
||||||
|
}) = _SnDeveloper;
|
||||||
|
|
||||||
|
factory SnDeveloper.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnDeveloperFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class DeveloperStats with _$DeveloperStats {
|
sealed class DeveloperStats with _$DeveloperStats {
|
||||||
const factory DeveloperStats({
|
const factory DeveloperStats({@Default(0) int totalCustomApps}) =
|
||||||
@Default(0) int totalCustomApps,
|
_DeveloperStats;
|
||||||
}) = _DeveloperStats;
|
|
||||||
|
|
||||||
factory DeveloperStats.fromJson(Map<String, dynamic> json) =>
|
factory DeveloperStats.fromJson(Map<String, dynamic> json) =>
|
||||||
_$DeveloperStatsFromJson(json);
|
_$DeveloperStatsFromJson(json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,293 @@ part of 'developer.dart';
|
|||||||
// dart format off
|
// dart format off
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnDeveloper {
|
||||||
|
|
||||||
|
String get id; String get publisherId; SnPublisher? get publisher;
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnDeveloperCopyWith<SnDeveloper> get copyWith => _$SnDeveloperCopyWithImpl<SnDeveloper>(this as SnDeveloper, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnDeveloper to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnDeveloper&&(identical(other.id, id) || other.id == id)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,publisherId,publisher);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnDeveloper(id: $id, publisherId: $publisherId, publisher: $publisher)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnDeveloperCopyWith<$Res> {
|
||||||
|
factory $SnDeveloperCopyWith(SnDeveloper value, $Res Function(SnDeveloper) _then) = _$SnDeveloperCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String publisherId, SnPublisher? publisher
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnDeveloperCopyWithImpl<$Res>
|
||||||
|
implements $SnDeveloperCopyWith<$Res> {
|
||||||
|
_$SnDeveloperCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnDeveloper _self;
|
||||||
|
final $Res Function(SnDeveloper) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? publisherId = null,Object? publisher = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPublisher?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher {
|
||||||
|
if (_self.publisher == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||||
|
return _then(_self.copyWith(publisher: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnDeveloper].
|
||||||
|
extension SnDeveloperPatterns on SnDeveloper {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnDeveloper value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnDeveloper value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnDeveloper value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String publisherId, SnPublisher? publisher)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that.id,_that.publisherId,_that.publisher);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String publisherId, SnPublisher? publisher) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper():
|
||||||
|
return $default(_that.id,_that.publisherId,_that.publisher);}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String publisherId, SnPublisher? publisher)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnDeveloper() when $default != null:
|
||||||
|
return $default(_that.id,_that.publisherId,_that.publisher);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnDeveloper implements SnDeveloper {
|
||||||
|
const _SnDeveloper({required this.id, required this.publisherId, this.publisher});
|
||||||
|
factory _SnDeveloper.fromJson(Map<String, dynamic> json) => _$SnDeveloperFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String publisherId;
|
||||||
|
@override final SnPublisher? publisher;
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnDeveloperCopyWith<_SnDeveloper> get copyWith => __$SnDeveloperCopyWithImpl<_SnDeveloper>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnDeveloperToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnDeveloper&&(identical(other.id, id) || other.id == id)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,publisherId,publisher);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnDeveloper(id: $id, publisherId: $publisherId, publisher: $publisher)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnDeveloperCopyWith<$Res> implements $SnDeveloperCopyWith<$Res> {
|
||||||
|
factory _$SnDeveloperCopyWith(_SnDeveloper value, $Res Function(_SnDeveloper) _then) = __$SnDeveloperCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String publisherId, SnPublisher? publisher
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@override $SnPublisherCopyWith<$Res>? get publisher;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnDeveloperCopyWithImpl<$Res>
|
||||||
|
implements _$SnDeveloperCopyWith<$Res> {
|
||||||
|
__$SnDeveloperCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnDeveloper _self;
|
||||||
|
final $Res Function(_SnDeveloper) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? publisherId = null,Object? publisher = freezed,}) {
|
||||||
|
return _then(_SnDeveloper(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPublisher?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnDeveloper
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher {
|
||||||
|
if (_self.publisher == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||||
|
return _then(_self.copyWith(publisher: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$DeveloperStats {
|
mixin _$DeveloperStats {
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,22 @@ part of 'developer.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
|
_SnDeveloper _$SnDeveloperFromJson(Map<String, dynamic> json) => _SnDeveloper(
|
||||||
|
id: json['id'] as String,
|
||||||
|
publisherId: json['publisher_id'] as String,
|
||||||
|
publisher:
|
||||||
|
json['publisher'] == null
|
||||||
|
? null
|
||||||
|
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnDeveloperToJson(_SnDeveloper instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'publisher_id': instance.publisherId,
|
||||||
|
'publisher': instance.publisher?.toJson(),
|
||||||
|
};
|
||||||
|
|
||||||
_DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) =>
|
_DeveloperStats _$DeveloperStatsFromJson(Map<String, dynamic> json) =>
|
||||||
_DeveloperStats(
|
_DeveloperStats(
|
||||||
totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0,
|
totalCustomApps: (json['total_custom_apps'] as num?)?.toInt() ?? 0,
|
||||||
|
|||||||
@@ -102,235 +102,243 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
|
|||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: _errorMessage != null
|
: _errorMessage != null
|
||||||
? Center(child: Text(_errorMessage!))
|
? Center(child: Text(_errorMessage!))
|
||||||
: SingleChildScrollView(
|
: Center(
|
||||||
child: Column(
|
child: ConstrainedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
constraints: const BoxConstraints(maxWidth: 540),
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
const SizedBox(height: 24),
|
child: Column(
|
||||||
// App Icon and Name
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
CircleAvatar(
|
|
||||||
radius: 50,
|
|
||||||
backgroundColor: theme.colorScheme.primary.withOpacity(
|
|
||||||
0.1,
|
|
||||||
),
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/icons/icon.png',
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
_packageInfo.appName,
|
|
||||||
style: theme.textTheme.headlineSmall?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'aboutScreenVersionInfo'.tr(
|
|
||||||
args: [_packageInfo.version, _packageInfo.buildNumber],
|
|
||||||
),
|
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: theme.textTheme.bodySmall?.color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// App Info Card
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'aboutScreenAppInfoSectionTitle'.tr(),
|
|
||||||
children: [
|
children: [
|
||||||
_buildInfoItem(
|
const SizedBox(height: 24),
|
||||||
context,
|
// App Icon and Name
|
||||||
icon: Symbols.info,
|
CircleAvatar(
|
||||||
label: 'aboutScreenPackageNameLabel'.tr(),
|
radius: 50,
|
||||||
value: _packageInfo.packageName,
|
backgroundColor: theme.colorScheme.primary
|
||||||
),
|
.withOpacity(0.1),
|
||||||
_buildInfoItem(
|
child: Image.asset(
|
||||||
context,
|
'assets/icons/icon.png',
|
||||||
icon: Symbols.update,
|
width: 56,
|
||||||
label: 'aboutScreenVersionLabel'.tr(),
|
height: 56,
|
||||||
value: _packageInfo.version,
|
|
||||||
),
|
|
||||||
_buildInfoItem(
|
|
||||||
context,
|
|
||||||
icon: Symbols.build,
|
|
||||||
label: 'aboutScreenBuildNumberLabel'.tr(),
|
|
||||||
value: _packageInfo.buildNumber,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
if (_deviceInfo != null) const SizedBox(height: 16),
|
|
||||||
|
|
||||||
if (_deviceInfo != null)
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'Device Information',
|
|
||||||
children: [
|
|
||||||
_buildInfoItem(
|
|
||||||
context,
|
|
||||||
icon: Symbols.label,
|
|
||||||
label: 'aboutDeviceName'.tr(),
|
|
||||||
value: _deviceInfo?.data['name'],
|
|
||||||
),
|
),
|
||||||
_buildInfoItem(
|
),
|
||||||
context,
|
const SizedBox(height: 16),
|
||||||
icon: Symbols.fingerprint,
|
Text(
|
||||||
label: 'aboutDeviceIdentifier'.tr(),
|
_packageInfo.appName,
|
||||||
value: _deviceUdid ?? 'N/A',
|
style: theme.textTheme.headlineSmall?.copyWith(
|
||||||
copyable: true,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
Text(
|
||||||
|
'aboutScreenVersionInfo'.tr(
|
||||||
|
args: [
|
||||||
|
_packageInfo.version,
|
||||||
|
_packageInfo.buildNumber,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
style: theme.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: theme.textTheme.bodySmall?.color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
// App Info Card
|
||||||
|
_buildSection(
|
||||||
// Links Card
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'aboutScreenLinksSectionTitle'.tr(),
|
|
||||||
children: [
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
context,
|
||||||
icon: Symbols.system_update,
|
title: 'aboutScreenAppInfoSectionTitle'.tr(),
|
||||||
title: 'Check for updates',
|
children: [
|
||||||
onTap: () async {
|
_buildInfoItem(
|
||||||
// Fetch latest release and show the unified sheet
|
context,
|
||||||
final svc = UpdateService();
|
icon: Symbols.info,
|
||||||
// Reuse service fetch + compare to decide content
|
label: 'aboutScreenPackageNameLabel'.tr(),
|
||||||
final release = await svc.fetchLatestRelease();
|
value: _packageInfo.packageName,
|
||||||
if (release != null) {
|
),
|
||||||
await svc.showUpdateSheet(context, release);
|
_buildInfoItem(
|
||||||
} else {
|
context,
|
||||||
// Fallback: show a simple sheet indicating no info
|
icon: Symbols.update,
|
||||||
// Use your SheetScaffold for consistent styling
|
label: 'aboutScreenVersionLabel'.tr(),
|
||||||
// Show a minimal message
|
value: _packageInfo.version,
|
||||||
// ignore: use_build_context_synchronously
|
),
|
||||||
showModalBottomSheet(
|
_buildInfoItem(
|
||||||
context: context,
|
context,
|
||||||
isScrollControlled: true,
|
icon: Symbols.build,
|
||||||
useSafeArea: true,
|
label: 'aboutScreenBuildNumberLabel'.tr(),
|
||||||
showDragHandle: true,
|
value: _packageInfo.buildNumber,
|
||||||
backgroundColor:
|
),
|
||||||
Theme.of(context).colorScheme.surface,
|
],
|
||||||
builder:
|
),
|
||||||
(_) => const SheetScaffold(
|
|
||||||
titleText: 'Update',
|
if (_deviceInfo != null) const SizedBox(height: 16),
|
||||||
child: Center(
|
|
||||||
child: Padding(
|
if (_deviceInfo != null)
|
||||||
padding: EdgeInsets.all(24),
|
_buildSection(
|
||||||
child: Text(
|
context,
|
||||||
'Unable to fetch release info at this time.',
|
title: 'Device Information',
|
||||||
|
children: [
|
||||||
|
_buildInfoItem(
|
||||||
|
context,
|
||||||
|
icon: Symbols.label,
|
||||||
|
label: 'aboutDeviceName'.tr(),
|
||||||
|
value: _deviceInfo?.data['name'],
|
||||||
|
),
|
||||||
|
_buildInfoItem(
|
||||||
|
context,
|
||||||
|
icon: Symbols.fingerprint,
|
||||||
|
label: 'aboutDeviceIdentifier'.tr(),
|
||||||
|
value: _deviceUdid ?? 'N/A',
|
||||||
|
copyable: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Links Card
|
||||||
|
_buildSection(
|
||||||
|
context,
|
||||||
|
title: 'aboutScreenLinksSectionTitle'.tr(),
|
||||||
|
children: [
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.system_update,
|
||||||
|
title: 'Check for updates',
|
||||||
|
onTap: () async {
|
||||||
|
// Fetch latest release and show the unified sheet
|
||||||
|
final svc = UpdateService();
|
||||||
|
// Reuse service fetch + compare to decide content
|
||||||
|
final release = await svc.fetchLatestRelease();
|
||||||
|
if (release != null) {
|
||||||
|
await svc.showUpdateSheet(context, release);
|
||||||
|
} else {
|
||||||
|
// Fallback: show a simple sheet indicating no info
|
||||||
|
// Use your SheetScaffold for consistent styling
|
||||||
|
// Show a minimal message
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
useSafeArea: true,
|
||||||
|
showDragHandle: true,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).colorScheme.surface,
|
||||||
|
builder:
|
||||||
|
(_) => const SheetScaffold(
|
||||||
|
titleText: 'Update',
|
||||||
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(24),
|
||||||
|
child: Text(
|
||||||
|
'Unable to fetch release info at this time.',
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
),
|
}
|
||||||
);
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.privacy_tip,
|
|
||||||
title: 'aboutScreenPrivacyPolicyTitle'.tr(),
|
|
||||||
onTap:
|
|
||||||
() => _launchURL(
|
|
||||||
'https://solsynth.dev/terms/privacy-policy',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.description,
|
|
||||||
title: 'aboutScreenTermsOfServiceTitle'.tr(),
|
|
||||||
onTap:
|
|
||||||
() => _launchURL(
|
|
||||||
'https://solsynth.dev/terms/user-agreement',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.code,
|
|
||||||
title: 'aboutScreenOpenSourceLicensesTitle'.tr(),
|
|
||||||
onTap: () {
|
|
||||||
showLicensePage(
|
|
||||||
context: context,
|
|
||||||
applicationName: _packageInfo.appName,
|
|
||||||
applicationVersion:
|
|
||||||
'Version ${_packageInfo.version}',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Developer Info
|
|
||||||
_buildSection(
|
|
||||||
context,
|
|
||||||
title: 'aboutScreenDeveloperSectionTitle'.tr(),
|
|
||||||
children: [
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.email,
|
|
||||||
title: 'aboutScreenContactUsTitle'.tr(),
|
|
||||||
subtitle: 'lily@solsynth.dev',
|
|
||||||
onTap: () => _launchURL('mailto:lily@solsynth.dev'),
|
|
||||||
),
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.copyright,
|
|
||||||
title: 'aboutScreenLicenseTitle'.tr(),
|
|
||||||
subtitle: 'aboutScreenLicenseContent'.tr(
|
|
||||||
args: [DateTime.now().year.toString()],
|
|
||||||
),
|
|
||||||
onTap:
|
|
||||||
() => _launchURL(
|
|
||||||
'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (kIsWeb || !(Platform.isMacOS || Platform.isIOS))
|
|
||||||
_buildListTile(
|
|
||||||
context,
|
|
||||||
icon: Symbols.favorite,
|
|
||||||
title: 'donate'.tr(),
|
|
||||||
subtitle: 'donateDescription'.tr(),
|
|
||||||
onTap: () {
|
|
||||||
launchUrlString(
|
|
||||||
'https://afdian.com/@littlesheep',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Copyright
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'aboutScreenCopyright'.tr(
|
|
||||||
args: [DateTime.now().year.toString()],
|
|
||||||
),
|
),
|
||||||
style: theme.textTheme.bodySmall,
|
_buildListTile(
|
||||||
textAlign: TextAlign.center,
|
context,
|
||||||
),
|
icon: Symbols.privacy_tip,
|
||||||
const Gap(1),
|
title: 'aboutScreenPrivacyPolicyTitle'.tr(),
|
||||||
Text(
|
onTap:
|
||||||
'aboutScreenMadeWith'.tr(),
|
() => _launchURL(
|
||||||
textAlign: TextAlign.center,
|
'https://solsynth.dev/terms/privacy-policy',
|
||||||
).fontSize(10).opacity(0.8),
|
),
|
||||||
],
|
),
|
||||||
),
|
_buildListTile(
|
||||||
),
|
context,
|
||||||
|
icon: Symbols.description,
|
||||||
|
title: 'aboutScreenTermsOfServiceTitle'.tr(),
|
||||||
|
onTap:
|
||||||
|
() => _launchURL(
|
||||||
|
'https://solsynth.dev/terms/user-agreement',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.code,
|
||||||
|
title: 'aboutScreenOpenSourceLicensesTitle'.tr(),
|
||||||
|
onTap: () {
|
||||||
|
showLicensePage(
|
||||||
|
context: context,
|
||||||
|
applicationName: _packageInfo.appName,
|
||||||
|
applicationVersion:
|
||||||
|
'Version ${_packageInfo.version}',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
Gap(MediaQuery.of(context).padding.bottom + 16),
|
const SizedBox(height: 16),
|
||||||
],
|
|
||||||
|
// Developer Info
|
||||||
|
_buildSection(
|
||||||
|
context,
|
||||||
|
title: 'aboutScreenDeveloperSectionTitle'.tr(),
|
||||||
|
children: [
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.email,
|
||||||
|
title: 'aboutScreenContactUsTitle'.tr(),
|
||||||
|
subtitle: 'lily@solsynth.dev',
|
||||||
|
onTap:
|
||||||
|
() => _launchURL('mailto:lily@solsynth.dev'),
|
||||||
|
),
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.copyright,
|
||||||
|
title: 'aboutScreenLicenseTitle'.tr(),
|
||||||
|
subtitle: 'aboutScreenLicenseContent'.tr(
|
||||||
|
args: [DateTime.now().year.toString()],
|
||||||
|
),
|
||||||
|
onTap:
|
||||||
|
() => _launchURL(
|
||||||
|
'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (kIsWeb || !(Platform.isMacOS || Platform.isIOS))
|
||||||
|
_buildListTile(
|
||||||
|
context,
|
||||||
|
icon: Symbols.favorite,
|
||||||
|
title: 'donate'.tr(),
|
||||||
|
subtitle: 'donateDescription'.tr(),
|
||||||
|
onTap: () {
|
||||||
|
launchUrlString(
|
||||||
|
'https://afdian.com/@littlesheep',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
|
// Copyright
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'aboutScreenCopyright'.tr(
|
||||||
|
args: [DateTime.now().year.toString()],
|
||||||
|
),
|
||||||
|
style: theme.textTheme.bodySmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const Gap(1),
|
||||||
|
Text(
|
||||||
|
'aboutScreenMadeWith'.tr(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
).fontSize(10).opacity(0.8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/pods/message.dart';
|
|
||||||
import 'package:island/pods/network.dart';
|
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/screens/notification.dart';
|
import 'package:island/screens/notification.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
@@ -15,6 +11,7 @@ import 'package:island/widgets/account/status.dart';
|
|||||||
import 'package:island/widgets/account/leveling_progress.dart';
|
import 'package:island/widgets/account/leveling_progress.dart';
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:island/widgets/debug_sheet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
@@ -276,30 +273,6 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('accountSettings');
|
context.pushNamed('accountSettings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (kDebugMode) const Divider(height: 1).padding(vertical: 8),
|
|
||||||
if (kDebugMode)
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.copy_all),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Copy access token'),
|
|
||||||
onTap: () async {
|
|
||||||
final tk = ref.watch(tokenProvider);
|
|
||||||
Clipboard.setData(ClipboardData(text: tk!.token));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (kDebugMode)
|
|
||||||
ListTile(
|
|
||||||
minTileHeight: 48,
|
|
||||||
leading: const Icon(Symbols.delete),
|
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text('Reset database'),
|
|
||||||
onTap: () async {
|
|
||||||
resetDatabase(ref);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Divider(height: 1).padding(vertical: 8),
|
const Divider(height: 1).padding(vertical: 8),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
@@ -311,6 +284,19 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('about');
|
context.pushNamed('about');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.bug_report),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('debugOptions').tr(),
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DebugSheet(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: const Icon(Symbols.logout),
|
leading: const Icon(Symbols.logout),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'package:island/pods/network.dart';
|
|||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/services/color.dart';
|
import 'package:island/services/color.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
|
import 'package:island/services/text.dart';
|
||||||
import 'package:island/services/time.dart';
|
import 'package:island/services/time.dart';
|
||||||
import 'package:island/services/timezone/native.dart';
|
import 'package:island/services/timezone/native.dart';
|
||||||
import 'package:island/widgets/account/account_name.dart';
|
import 'package:island/widgets/account/account_name.dart';
|
||||||
@@ -30,6 +31,7 @@ import 'package:palette_generator/palette_generator.dart';
|
|||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
part 'profile.g.dart';
|
part 'profile.g.dart';
|
||||||
|
|
||||||
@@ -350,6 +352,28 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
).padding(horizontal: 24, vertical: 16),
|
).padding(horizontal: 24, vertical: 16),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget accountProfileLinks(SnAccount data) => Card(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('links').tr().bold().padding(horizontal: 24, top: 12, bottom: 4),
|
||||||
|
for (final link in data.profile.links.entries)
|
||||||
|
ListTile(
|
||||||
|
title: Text(link.key.capitalizeEachWord()),
|
||||||
|
subtitle: Text(link.value),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
launchUrlString(link.value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
Widget accountAction(SnAccount data) => Card(
|
Widget accountAction(SnAccount data) => Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -452,7 +476,7 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 8),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
);
|
);
|
||||||
|
|
||||||
return account.when(
|
return account.when(
|
||||||
@@ -537,6 +561,9 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileBio(data).padding(top: 4),
|
child: accountProfileBio(data).padding(top: 4),
|
||||||
),
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: accountProfileLinks(data),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileDetail(data),
|
child: accountProfileDetail(data),
|
||||||
),
|
),
|
||||||
@@ -633,6 +660,11 @@ class AccountProfileScreen extends HookConsumerWidget {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileBio(data).padding(horizontal: 4),
|
child: accountProfileBio(data).padding(horizontal: 4),
|
||||||
),
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: accountProfileLinks(
|
||||||
|
data,
|
||||||
|
).padding(horizontal: 4),
|
||||||
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: accountProfileDetail(
|
child: accountProfileDetail(
|
||||||
data,
|
data,
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ Future<DeveloperStats?> developerStats(Ref ref, String? uname) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<List<SnPublisher>> developers(Ref ref) async {
|
Future<List<SnDeveloper>> developers(Ref ref) async {
|
||||||
final client = ref.watch(apiClientProvider);
|
final client = ref.watch(apiClientProvider);
|
||||||
final resp = await client.get('/develop/developers');
|
final resp = await client.get('/develop/developers');
|
||||||
return resp.data
|
return resp.data
|
||||||
.map((e) => SnPublisher.fromJson(e))
|
.map((e) => SnDeveloper.fromJson(e))
|
||||||
.cast<SnPublisher>()
|
.cast<SnDeveloper>()
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,25 +74,25 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final developers = ref.watch(developersProvider);
|
final developers = ref.watch(developersProvider);
|
||||||
final currentDeveloper = useState<SnPublisher?>(
|
final currentDeveloper = useState<SnDeveloper?>(
|
||||||
developers.value?.firstOrNull,
|
developers.value?.firstOrNull,
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<DropdownMenuItem<SnPublisher>> developersMenu = developers.when(
|
final List<DropdownMenuItem<SnDeveloper>> developersMenu = developers.when(
|
||||||
data:
|
data:
|
||||||
(data) =>
|
(data) =>
|
||||||
data
|
data
|
||||||
.map(
|
.map(
|
||||||
(item) => DropdownMenuItem<SnPublisher>(
|
(item) => DropdownMenuItem<SnDeveloper>(
|
||||||
value: item,
|
value: item,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: ProfilePictureWidget(
|
leading: ProfilePictureWidget(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
fileId: item.picture?.id,
|
fileId: item.publisher?.picture?.id,
|
||||||
),
|
),
|
||||||
title: Text(item.nick),
|
title: Text(item.publisher!.nick),
|
||||||
subtitle: Text('@${item.name}'),
|
subtitle: Text('@${item.publisher!.name}'),
|
||||||
trailing:
|
trailing:
|
||||||
currentDeveloper.value?.id == item.id
|
currentDeveloper.value?.id == item.id
|
||||||
? const Icon(Icons.check)
|
? const Icon(Icons.check)
|
||||||
@@ -107,7 +107,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final developerStats = ref.watch(
|
final developerStats = ref.watch(
|
||||||
developerStatsProvider(currentDeveloper.value?.name),
|
developerStatsProvider(currentDeveloper.value?.publisher?.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
@@ -117,7 +117,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
title: Text('developerHub').tr(),
|
title: Text('developerHub').tr(),
|
||||||
actions: [
|
actions: [
|
||||||
DropdownButtonHideUnderline(
|
DropdownButtonHideUnderline(
|
||||||
child: DropdownButton2<SnPublisher>(
|
child: DropdownButton2<SnDeveloper>(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
value: currentDeveloper.value,
|
value: currentDeveloper.value,
|
||||||
hint: CircleAvatar(
|
hint: CircleAvatar(
|
||||||
@@ -139,7 +139,7 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
...developersMenu.map(
|
...developersMenu.map(
|
||||||
(e) => ProfilePictureWidget(
|
(e) => ProfilePictureWidget(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
fileId: e.value?.picture?.id,
|
fileId: e.value?.publisher?.picture?.id,
|
||||||
).center().padding(right: 8),
|
).center().padding(right: 8),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -193,10 +193,12 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
...(developers.value?.map(
|
...(developers.value?.map(
|
||||||
(developer) => ListTile(
|
(developer) => ListTile(
|
||||||
leading: ProfilePictureWidget(
|
leading: ProfilePictureWidget(
|
||||||
file: developer.picture,
|
file: developer.publisher?.picture,
|
||||||
|
),
|
||||||
|
title: Text(developer.publisher!.nick),
|
||||||
|
subtitle: Text(
|
||||||
|
'@${developer.publisher!.name}',
|
||||||
),
|
),
|
||||||
title: Text(developer.nick),
|
|
||||||
subtitle: Text('@${developer.name}'),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
currentDeveloper.value = developer;
|
currentDeveloper.value = developer;
|
||||||
},
|
},
|
||||||
@@ -243,7 +245,8 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed(
|
context.pushNamed(
|
||||||
'developerApps',
|
'developerApps',
|
||||||
pathParameters: {
|
pathParameters: {
|
||||||
'name': currentDeveloper.value!.name,
|
'name':
|
||||||
|
currentDeveloper.value!.publisher!.name,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -257,7 +260,9 @@ class DeveloperHubScreen extends HookConsumerWidget {
|
|||||||
error: err,
|
error: err,
|
||||||
onRetry: () {
|
onRetry: () {
|
||||||
ref.invalidate(
|
ref.invalidate(
|
||||||
developerStatsProvider(currentDeveloper.value?.name),
|
developerStatsProvider(
|
||||||
|
currentDeveloper.value?.publisher!.name,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -354,7 +359,7 @@ class _DeveloperEnrollmentSheet extends HookConsumerWidget {
|
|||||||
? Center(
|
? Center(
|
||||||
child:
|
child:
|
||||||
Text(
|
Text(
|
||||||
'noPublishersToEnroll',
|
'noDevelopersToEnroll',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
).tr(),
|
).tr(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -149,12 +149,12 @@ class _DeveloperStatsProviderElement
|
|||||||
String? get uname => (origin as DeveloperStatsProvider).uname;
|
String? get uname => (origin as DeveloperStatsProvider).uname;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$developersHash() => r'04f25db31f511f651a5add128d56631236ed0b39';
|
String _$developersHash() => r'252341098617ac398ce133994453f318dd3edbd2';
|
||||||
|
|
||||||
/// See also [developers].
|
/// See also [developers].
|
||||||
@ProviderFor(developers)
|
@ProviderFor(developers)
|
||||||
final developersProvider =
|
final developersProvider =
|
||||||
AutoDisposeFutureProvider<List<SnPublisher>>.internal(
|
AutoDisposeFutureProvider<List<SnDeveloper>>.internal(
|
||||||
developers,
|
developers,
|
||||||
name: r'developersProvider',
|
name: r'developersProvider',
|
||||||
debugGetCreateSourceHash:
|
debugGetCreateSourceHash:
|
||||||
@@ -167,6 +167,6 @@ final developersProvider =
|
|||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnPublisher>>;
|
typedef DevelopersRef = AutoDisposeFutureProviderRef<List<SnDeveloper>>;
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ class _WebSocketIndicator extends HookConsumerWidget {
|
|||||||
final user = ref.watch(userInfoProvider);
|
final user = ref.watch(userInfoProvider);
|
||||||
final websocketState = ref.watch(websocketStateProvider);
|
final websocketState = ref.watch(websocketStateProvider);
|
||||||
final indicatorHeight =
|
final indicatorHeight =
|
||||||
MediaQuery.of(context).padding.top + (isDesktop ? 27.5 : 20);
|
MediaQuery.of(context).padding.top + (isDesktop ? 27.5 : 25);
|
||||||
|
|
||||||
Color indicatorColor;
|
Color indicatorColor;
|
||||||
String indicatorText;
|
String indicatorText;
|
||||||
|
|||||||
76
lib/widgets/debug_sheet.dart
Normal file
76
lib/widgets/debug_sheet.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/pods/message.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/pods/websocket.dart';
|
||||||
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
|
class DebugSheet extends HookConsumerWidget {
|
||||||
|
const DebugSheet({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final wsNotifier = ref.watch(websocketStateProvider.notifier);
|
||||||
|
|
||||||
|
return SheetScaffold(
|
||||||
|
titleText: 'Debug',
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.wifi),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
title: Text('Connection Status'),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder:
|
||||||
|
(context) => NetworkStatusSheet(
|
||||||
|
onReconnect: () => wsNotifier.connect(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.copy_all),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Copy access token'),
|
||||||
|
onTap: () async {
|
||||||
|
final tk = ref.watch(tokenProvider);
|
||||||
|
Clipboard.setData(ClipboardData(text: tk!.token));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.delete),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Reset database'),
|
||||||
|
onTap: () async {
|
||||||
|
resetDatabase(ref);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.clear),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Clear cache'),
|
||||||
|
onTap: () async {
|
||||||
|
DefaultCacheManager().emptyCache();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 3.1.0+117
|
version: 3.1.0+119
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
|
|||||||
Reference in New Issue
Block a user