✨ Custom app detail page
This commit is contained in:
@@ -914,5 +914,13 @@
|
|||||||
"other": "{} articles"
|
"other": "{} articles"
|
||||||
},
|
},
|
||||||
"webFeedSubscribed": "The feed has been subscribed",
|
"webFeedSubscribed": "The feed has been subscribed",
|
||||||
"webFeedUnsubscribed": "The feed has been unsubscribed"
|
"webFeedUnsubscribed": "The feed has been unsubscribed",
|
||||||
|
"appDetails": "App Details",
|
||||||
|
"secrets": "Secrets",
|
||||||
|
"appNotFound": "App not found.",
|
||||||
|
"secretCopied": "Secret copied to clipboard.",
|
||||||
|
"deleteSecret": "Delete Secret",
|
||||||
|
"deleteSecretHint": "Are you sure you want to delete this secret? This action cannot be undone.",
|
||||||
|
"generateSecret": "Generate New Secret",
|
||||||
|
"created_at": "Created at {}"
|
||||||
}
|
}
|
@@ -843,5 +843,13 @@
|
|||||||
"socialCreditsLevelPoor": "糟糕",
|
"socialCreditsLevelPoor": "糟糕",
|
||||||
"socialCreditsLevelNormal": "正常",
|
"socialCreditsLevelNormal": "正常",
|
||||||
"socialCreditsLevelGood": "良好",
|
"socialCreditsLevelGood": "良好",
|
||||||
"socialCreditsLevelExcellent": "优秀"
|
"socialCreditsLevelExcellent": "优秀",
|
||||||
|
"appDetails": "应用详情",
|
||||||
|
"secrets": "密钥",
|
||||||
|
"appNotFound": "应用未找到。",
|
||||||
|
"secretCopied": "密钥已复制到剪贴板。",
|
||||||
|
"deleteSecret": "删除密钥",
|
||||||
|
"deleteSecretHint": "您确定要删除此密钥吗?此操作无法撤销。",
|
||||||
|
"generateSecret": "生成新密钥",
|
||||||
|
"created_at": "创建于 {}"
|
||||||
}
|
}
|
||||||
|
@@ -811,5 +811,13 @@
|
|||||||
"filesListAdditional": {
|
"filesListAdditional": {
|
||||||
"one": "+{} 個文件被摺疊",
|
"one": "+{} 個文件被摺疊",
|
||||||
"other": "+{} 個文件被摺疊"
|
"other": "+{} 個文件被摺疊"
|
||||||
}
|
},
|
||||||
|
"appDetails": "應用程式詳情",
|
||||||
|
"secrets": "密鑰",
|
||||||
|
"appNotFound": "找不到應用程式。",
|
||||||
|
"secretCopied": "密鑰已複製到剪貼簿。",
|
||||||
|
"deleteSecret": "刪除密鑰",
|
||||||
|
"deleteSecretHint": "您確定要刪除此密鑰嗎?此操作無法復原。",
|
||||||
|
"generateSecret": "產生新密鑰",
|
||||||
|
"created_at": "建立於 {}"
|
||||||
}
|
}
|
17
lib/models/custom_app_secret.dart
Normal file
17
lib/models/custom_app_secret.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'custom_app_secret.freezed.dart';
|
||||||
|
part 'custom_app_secret.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class CustomAppSecret with _$CustomAppSecret {
|
||||||
|
const factory CustomAppSecret({
|
||||||
|
required String id,
|
||||||
|
required String secret,
|
||||||
|
required DateTime createdAt,
|
||||||
|
String? description,
|
||||||
|
}) = _CustomAppSecret;
|
||||||
|
|
||||||
|
factory CustomAppSecret.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CustomAppSecretFromJson(json);
|
||||||
|
}
|
286
lib/models/custom_app_secret.freezed.dart
Normal file
286
lib/models/custom_app_secret.freezed.dart
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// coverage:ignore-file
|
||||||
|
// 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 'custom_app_secret.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$CustomAppSecret {
|
||||||
|
|
||||||
|
String get id; String get secret; DateTime get createdAt; String? get description;
|
||||||
|
/// Create a copy of CustomAppSecret
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$CustomAppSecretCopyWith<CustomAppSecret> get copyWith => _$CustomAppSecretCopyWithImpl<CustomAppSecret>(this as CustomAppSecret, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this CustomAppSecret to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is CustomAppSecret&&(identical(other.id, id) || other.id == id)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.description, description) || other.description == description));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,secret,createdAt,description);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'CustomAppSecret(id: $id, secret: $secret, createdAt: $createdAt, description: $description)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $CustomAppSecretCopyWith<$Res> {
|
||||||
|
factory $CustomAppSecretCopyWith(CustomAppSecret value, $Res Function(CustomAppSecret) _then) = _$CustomAppSecretCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String secret, DateTime createdAt, String? description
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$CustomAppSecretCopyWithImpl<$Res>
|
||||||
|
implements $CustomAppSecretCopyWith<$Res> {
|
||||||
|
_$CustomAppSecretCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final CustomAppSecret _self;
|
||||||
|
final $Res Function(CustomAppSecret) _then;
|
||||||
|
|
||||||
|
/// Create a copy of CustomAppSecret
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? secret = null,Object? createdAt = null,Object? description = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [CustomAppSecret].
|
||||||
|
extension CustomAppSecretPatterns on CustomAppSecret {
|
||||||
|
/// 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( _CustomAppSecret value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _CustomAppSecret() 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( _CustomAppSecret value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _CustomAppSecret():
|
||||||
|
return $default(_that);case _:
|
||||||
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 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( _CustomAppSecret value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _CustomAppSecret() 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 secret, DateTime createdAt, String? description)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _CustomAppSecret() when $default != null:
|
||||||
|
return $default(_that.id,_that.secret,_that.createdAt,_that.description);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 secret, DateTime createdAt, String? description) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _CustomAppSecret():
|
||||||
|
return $default(_that.id,_that.secret,_that.createdAt,_that.description);case _:
|
||||||
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 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 secret, DateTime createdAt, String? description)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _CustomAppSecret() when $default != null:
|
||||||
|
return $default(_that.id,_that.secret,_that.createdAt,_that.description);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _CustomAppSecret implements CustomAppSecret {
|
||||||
|
const _CustomAppSecret({required this.id, required this.secret, required this.createdAt, this.description});
|
||||||
|
factory _CustomAppSecret.fromJson(Map<String, dynamic> json) => _$CustomAppSecretFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String secret;
|
||||||
|
@override final DateTime createdAt;
|
||||||
|
@override final String? description;
|
||||||
|
|
||||||
|
/// Create a copy of CustomAppSecret
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$CustomAppSecretCopyWith<_CustomAppSecret> get copyWith => __$CustomAppSecretCopyWithImpl<_CustomAppSecret>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$CustomAppSecretToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CustomAppSecret&&(identical(other.id, id) || other.id == id)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.description, description) || other.description == description));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,secret,createdAt,description);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'CustomAppSecret(id: $id, secret: $secret, createdAt: $createdAt, description: $description)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$CustomAppSecretCopyWith<$Res> implements $CustomAppSecretCopyWith<$Res> {
|
||||||
|
factory _$CustomAppSecretCopyWith(_CustomAppSecret value, $Res Function(_CustomAppSecret) _then) = __$CustomAppSecretCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String secret, DateTime createdAt, String? description
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$CustomAppSecretCopyWithImpl<$Res>
|
||||||
|
implements _$CustomAppSecretCopyWith<$Res> {
|
||||||
|
__$CustomAppSecretCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _CustomAppSecret _self;
|
||||||
|
final $Res Function(_CustomAppSecret) _then;
|
||||||
|
|
||||||
|
/// Create a copy of CustomAppSecret
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? secret = null,Object? createdAt = null,Object? description = freezed,}) {
|
||||||
|
return _then(_CustomAppSecret(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
23
lib/models/custom_app_secret.g.dart
Normal file
23
lib/models/custom_app_secret.g.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'custom_app_secret.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_CustomAppSecret _$CustomAppSecretFromJson(Map<String, dynamic> json) =>
|
||||||
|
_CustomAppSecret(
|
||||||
|
id: json['id'] as String,
|
||||||
|
secret: json['secret'] as String,
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
description: json['description'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$CustomAppSecretToJson(_CustomAppSecret instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'secret': instance.secret,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'description': instance.description,
|
||||||
|
};
|
@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/screens/about.dart';
|
import 'package:island/screens/about.dart';
|
||||||
import 'package:island/screens/account/credits.dart';
|
import 'package:island/screens/account/credits.dart';
|
||||||
|
import 'package:island/screens/developers/app_detail.dart';
|
||||||
import 'package:island/screens/developers/bot_detail.dart';
|
import 'package:island/screens/developers/bot_detail.dart';
|
||||||
import 'package:island/screens/developers/edit_app.dart';
|
import 'package:island/screens/developers/edit_app.dart';
|
||||||
import 'package:island/screens/developers/edit_bot.dart';
|
import 'package:island/screens/developers/edit_bot.dart';
|
||||||
@@ -349,6 +350,16 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
id: state.pathParameters['id']!,
|
id: state.pathParameters['id']!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppDetail',
|
||||||
|
path: 'apps/:appId',
|
||||||
|
builder:
|
||||||
|
(context, state) => AppDetailScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
appId: state.pathParameters['appId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'developerBotDetail',
|
name: 'developerBotDetail',
|
||||||
path: 'bots/:botId',
|
path: 'bots/:botId',
|
||||||
|
131
lib/screens/developers/app_detail.dart
Normal file
131
lib/screens/developers/app_detail.dart
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/custom_app.dart';
|
||||||
|
import 'package:island/screens/developers/app_secrets.dart';
|
||||||
|
import 'package:island/screens/developers/apps.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
class AppDetailScreen extends HookConsumerWidget {
|
||||||
|
final String publisherName;
|
||||||
|
final String projectId;
|
||||||
|
final String appId;
|
||||||
|
|
||||||
|
const AppDetailScreen({
|
||||||
|
super.key,
|
||||||
|
required this.publisherName,
|
||||||
|
required this.projectId,
|
||||||
|
required this.appId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tabController = useTabController(initialLength: 2);
|
||||||
|
final appData = ref.watch(customAppProvider(publisherName, projectId, appId));
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(appData.value?.name ?? 'appDetails'.tr()),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.edit),
|
||||||
|
onPressed: appData.value == null
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
context.pushNamed(
|
||||||
|
'developerAppEdit',
|
||||||
|
pathParameters: {
|
||||||
|
'name': publisherName,
|
||||||
|
'projectId': projectId,
|
||||||
|
'id': appId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
bottom: TabBar(
|
||||||
|
controller: tabController,
|
||||||
|
tabs: [Tab(text: 'overview'.tr()), Tab(text: 'secrets'.tr())],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: appData.when(
|
||||||
|
data: (app) {
|
||||||
|
return TabBarView(
|
||||||
|
controller: tabController,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
_AppOverview(app: app),
|
||||||
|
AppSecretsScreen(
|
||||||
|
publisherName: publisherName,
|
||||||
|
projectId: projectId,
|
||||||
|
appId: appId,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (err, stack) => ResponseErrorWidget(
|
||||||
|
error: err,
|
||||||
|
onRetry: () => ref.invalidate(
|
||||||
|
customAppProvider(publisherName, projectId, appId),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppOverview extends StatelessWidget {
|
||||||
|
final CustomApp app;
|
||||||
|
const _AppOverview({required this.app});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 16 / 7,
|
||||||
|
child: Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
child: app.background != null
|
||||||
|
? CloudFileWidget(
|
||||||
|
item: app.background!,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 20,
|
||||||
|
bottom: -32,
|
||||||
|
child: ProfilePictureWidget(
|
||||||
|
fileId: app.picture?.id,
|
||||||
|
radius: 40,
|
||||||
|
fallbackIcon: Symbols.apps,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).padding(bottom: 32),
|
||||||
|
ListTile(title: Text('name'.tr()), subtitle: Text(app.name)),
|
||||||
|
ListTile(title: Text('slug'.tr()), subtitle: Text(app.slug)),
|
||||||
|
if (app.description?.isNotEmpty ?? false)
|
||||||
|
ListTile(
|
||||||
|
title: Text('description'.tr()),
|
||||||
|
subtitle: Text(app.description!),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(bottom: 24),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
161
lib/screens/developers/app_secrets.dart
Normal file
161
lib/screens/developers/app_secrets.dart
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/custom_app_secret.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'app_secrets.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<List<CustomAppSecret>> customAppSecrets(
|
||||||
|
Ref ref,
|
||||||
|
String publisherName,
|
||||||
|
String projectId,
|
||||||
|
String appId,
|
||||||
|
) async {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
final resp = await client.get(
|
||||||
|
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets',
|
||||||
|
);
|
||||||
|
return (resp.data as List)
|
||||||
|
.map((e) => CustomAppSecret.fromJson(e))
|
||||||
|
.cast<CustomAppSecret>()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppSecretsScreen extends HookConsumerWidget {
|
||||||
|
final String publisherName;
|
||||||
|
final String projectId;
|
||||||
|
final String appId;
|
||||||
|
|
||||||
|
const AppSecretsScreen({
|
||||||
|
super.key,
|
||||||
|
required this.publisherName,
|
||||||
|
required this.projectId,
|
||||||
|
required this.appId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final secrets = ref.watch(
|
||||||
|
customAppSecretsProvider(publisherName, projectId, appId),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> generateSecret() async {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
try {
|
||||||
|
showLoadingModal(context);
|
||||||
|
await client
|
||||||
|
.post(
|
||||||
|
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets',
|
||||||
|
)
|
||||||
|
.then((_) {
|
||||||
|
ref.invalidate(
|
||||||
|
customAppSecretsProvider(publisherName, projectId, appId),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return secrets.when(
|
||||||
|
data: (data) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh:
|
||||||
|
() => ref.refresh(
|
||||||
|
customAppSecretsProvider(
|
||||||
|
publisherName,
|
||||||
|
projectId,
|
||||||
|
appId,
|
||||||
|
).future,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Symbols.add),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
title: Text('appSecretsGenerate').tr(),
|
||||||
|
onTap: generateSecret,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: data.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final secret = data[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(secret.id),
|
||||||
|
subtitle: Text(
|
||||||
|
'created_at'.tr(
|
||||||
|
args: [secret.createdAt.toIso8601String()],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.copy_all),
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: secret.secret),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('secretCopied'.tr())),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.delete, color: Colors.red),
|
||||||
|
onPressed: () {
|
||||||
|
showConfirmAlert(
|
||||||
|
'deleteSecretHint'.tr(),
|
||||||
|
'deleteSecret'.tr(),
|
||||||
|
).then((confirm) {
|
||||||
|
if (confirm) {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
client
|
||||||
|
.delete(
|
||||||
|
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets/${secret.id}',
|
||||||
|
)
|
||||||
|
.then((_) {
|
||||||
|
ref.invalidate(
|
||||||
|
customAppSecretsProvider(
|
||||||
|
publisherName,
|
||||||
|
projectId,
|
||||||
|
appId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error:
|
||||||
|
(err, stack) => ResponseErrorWidget(
|
||||||
|
error: err,
|
||||||
|
onRetry:
|
||||||
|
() => ref.invalidate(
|
||||||
|
customAppSecretsProvider(publisherName, projectId, appId),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
188
lib/screens/developers/app_secrets.g.dart
Normal file
188
lib/screens/developers/app_secrets.g.dart
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'app_secrets.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$customAppSecretsHash() => r'1bc62ad812487883ce739793b22a76168d656752';
|
||||||
|
|
||||||
|
/// Copied from Dart SDK
|
||||||
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [customAppSecrets].
|
||||||
|
@ProviderFor(customAppSecrets)
|
||||||
|
const customAppSecretsProvider = CustomAppSecretsFamily();
|
||||||
|
|
||||||
|
/// See also [customAppSecrets].
|
||||||
|
class CustomAppSecretsFamily extends Family<AsyncValue<List<CustomAppSecret>>> {
|
||||||
|
/// See also [customAppSecrets].
|
||||||
|
const CustomAppSecretsFamily();
|
||||||
|
|
||||||
|
/// See also [customAppSecrets].
|
||||||
|
CustomAppSecretsProvider call(
|
||||||
|
String publisherName,
|
||||||
|
String projectId,
|
||||||
|
String appId,
|
||||||
|
) {
|
||||||
|
return CustomAppSecretsProvider(publisherName, projectId, appId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
CustomAppSecretsProvider getProviderOverride(
|
||||||
|
covariant CustomAppSecretsProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.publisherName, provider.projectId, provider.appId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'customAppSecretsProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [customAppSecrets].
|
||||||
|
class CustomAppSecretsProvider
|
||||||
|
extends AutoDisposeFutureProvider<List<CustomAppSecret>> {
|
||||||
|
/// See also [customAppSecrets].
|
||||||
|
CustomAppSecretsProvider(String publisherName, String projectId, String appId)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => customAppSecrets(
|
||||||
|
ref as CustomAppSecretsRef,
|
||||||
|
publisherName,
|
||||||
|
projectId,
|
||||||
|
appId,
|
||||||
|
),
|
||||||
|
from: customAppSecretsProvider,
|
||||||
|
name: r'customAppSecretsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$customAppSecretsHash,
|
||||||
|
dependencies: CustomAppSecretsFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
CustomAppSecretsFamily._allTransitiveDependencies,
|
||||||
|
publisherName: publisherName,
|
||||||
|
projectId: projectId,
|
||||||
|
appId: appId,
|
||||||
|
);
|
||||||
|
|
||||||
|
CustomAppSecretsProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.publisherName,
|
||||||
|
required this.projectId,
|
||||||
|
required this.appId,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String publisherName;
|
||||||
|
final String projectId;
|
||||||
|
final String appId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<List<CustomAppSecret>> Function(CustomAppSecretsRef provider)
|
||||||
|
create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: CustomAppSecretsProvider._internal(
|
||||||
|
(ref) => create(ref as CustomAppSecretsRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
publisherName: publisherName,
|
||||||
|
projectId: projectId,
|
||||||
|
appId: appId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<List<CustomAppSecret>> createElement() {
|
||||||
|
return _CustomAppSecretsProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is CustomAppSecretsProvider &&
|
||||||
|
other.publisherName == publisherName &&
|
||||||
|
other.projectId == projectId &&
|
||||||
|
other.appId == appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, publisherName.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, projectId.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, appId.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin CustomAppSecretsRef
|
||||||
|
on AutoDisposeFutureProviderRef<List<CustomAppSecret>> {
|
||||||
|
/// The parameter `publisherName` of this provider.
|
||||||
|
String get publisherName;
|
||||||
|
|
||||||
|
/// The parameter `projectId` of this provider.
|
||||||
|
String get projectId;
|
||||||
|
|
||||||
|
/// The parameter `appId` of this provider.
|
||||||
|
String get appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomAppSecretsProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<List<CustomAppSecret>>
|
||||||
|
with CustomAppSecretsRef {
|
||||||
|
_CustomAppSecretsProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get publisherName =>
|
||||||
|
(origin as CustomAppSecretsProvider).publisherName;
|
||||||
|
@override
|
||||||
|
String get projectId => (origin as CustomAppSecretsProvider).projectId;
|
||||||
|
@override
|
||||||
|
String get appId => (origin as CustomAppSecretsProvider).appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
@@ -14,6 +14,20 @@ import 'package:styled_widget/styled_widget.dart';
|
|||||||
|
|
||||||
part 'apps.g.dart';
|
part 'apps.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<CustomApp> customApp(
|
||||||
|
Ref ref,
|
||||||
|
String publisherName,
|
||||||
|
String projectId,
|
||||||
|
String appId,
|
||||||
|
) async {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
final resp = await client.get(
|
||||||
|
'/develop/developers/$publisherName/projects/$projectId/apps/$appId',
|
||||||
|
);
|
||||||
|
return CustomApp.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
Future<List<CustomApp>> customApps(
|
Future<List<CustomApp>> customApps(
|
||||||
Ref ref,
|
Ref ref,
|
||||||
@@ -81,98 +95,114 @@ class CustomAppsScreen extends HookConsumerWidget {
|
|||||||
final app = data[index];
|
final app = data[index];
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.all(8.0),
|
margin: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
clipBehavior: Clip.antiAlias,
|
||||||
children: [
|
child: InkWell(
|
||||||
SizedBox(
|
onTap: () {
|
||||||
height: 150,
|
context.pushNamed(
|
||||||
child: Stack(
|
'developerAppDetail',
|
||||||
fit: StackFit.expand,
|
pathParameters: {
|
||||||
children: [
|
'name': publisherName,
|
||||||
if (app.background != null)
|
'projectId': projectId,
|
||||||
CloudFileWidget(
|
'appId': app.id,
|
||||||
item: app.background!,
|
},
|
||||||
fit: BoxFit.cover,
|
);
|
||||||
).clipRRect(topLeft: 8, topRight: 8),
|
},
|
||||||
if (app.picture != null)
|
child: Column(
|
||||||
Positioned(
|
children: [
|
||||||
left: 16,
|
SizedBox(
|
||||||
bottom: 16,
|
height: 150,
|
||||||
child: ProfilePictureWidget(
|
child: Stack(
|
||||||
fileId: app.picture!.id,
|
fit: StackFit.expand,
|
||||||
radius: 40,
|
children: [
|
||||||
fallbackIcon: Symbols.apps,
|
if (app.background != null)
|
||||||
),
|
CloudFileWidget(
|
||||||
),
|
item: app.background!,
|
||||||
],
|
fit: BoxFit.cover,
|
||||||
),
|
).clipRRect(topLeft: 8, topRight: 8),
|
||||||
),
|
if (app.picture != null)
|
||||||
ListTile(
|
Positioned(
|
||||||
title: Text(app.name),
|
left: 16,
|
||||||
subtitle: Text(
|
bottom: 16,
|
||||||
app.slug,
|
child: ProfilePictureWidget(
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
fileId: app.picture!.id,
|
||||||
),
|
radius: 40,
|
||||||
contentPadding: EdgeInsets.only(left: 20, right: 12),
|
fallbackIcon: Symbols.apps,
|
||||||
trailing: PopupMenuButton(
|
|
||||||
itemBuilder:
|
|
||||||
(context) => [
|
|
||||||
PopupMenuItem(
|
|
||||||
value: 'edit',
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Symbols.edit),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text('edit').tr(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
],
|
||||||
value: 'delete',
|
),
|
||||||
child: Row(
|
),
|
||||||
children: [
|
ListTile(
|
||||||
const Icon(
|
title: Text(app.name),
|
||||||
Symbols.delete,
|
subtitle: Text(
|
||||||
color: Colors.red,
|
app.slug,
|
||||||
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.only(left: 20, right: 12),
|
||||||
|
trailing: PopupMenuButton(
|
||||||
|
itemBuilder:
|
||||||
|
(context) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'edit',
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.edit),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text('edit').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: 'delete',
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Symbols.delete,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
'delete',
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSelected: (value) {
|
||||||
|
if (value == 'edit') {
|
||||||
|
context.pushNamed(
|
||||||
|
'developerAppEdit',
|
||||||
|
pathParameters: {
|
||||||
|
'name': publisherName,
|
||||||
|
'projectId': projectId,
|
||||||
|
'id': app.id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (value == 'delete') {
|
||||||
|
showConfirmAlert(
|
||||||
|
'deleteCustomAppHint'.tr(),
|
||||||
|
'deleteCustomApp'.tr(),
|
||||||
|
).then((confirm) {
|
||||||
|
if (confirm) {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
client.delete(
|
||||||
|
'/develop/developers/$publisherName/projects/$projectId/apps/${app.id}',
|
||||||
|
);
|
||||||
|
ref.invalidate(
|
||||||
|
customAppsProvider(
|
||||||
|
publisherName,
|
||||||
|
projectId,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
);
|
||||||
Text(
|
}
|
||||||
'delete',
|
});
|
||||||
style: TextStyle(color: Colors.red),
|
}
|
||||||
).tr(),
|
},
|
||||||
],
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onSelected: (value) {
|
|
||||||
if (value == 'edit') {
|
|
||||||
context.pushNamed(
|
|
||||||
'developerAppEdit',
|
|
||||||
pathParameters: {
|
|
||||||
'name': publisherName,
|
|
||||||
'projectId': projectId,
|
|
||||||
'id': app.id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else if (value == 'delete') {
|
|
||||||
showConfirmAlert(
|
|
||||||
'deleteCustomAppHint'.tr(),
|
|
||||||
'deleteCustomApp'.tr(),
|
|
||||||
).then((confirm) {
|
|
||||||
if (confirm) {
|
|
||||||
final client = ref.read(apiClientProvider);
|
|
||||||
client.delete(
|
|
||||||
'/develop/developers/$publisherName/projects/$projectId/apps/${app.id}',
|
|
||||||
);
|
|
||||||
ref.invalidate(
|
|
||||||
customAppsProvider(publisherName, projectId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@@ -6,7 +6,7 @@ part of 'apps.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$customAppsHash() => r'450bedaf4220b8963cb44afeb14d4c0e80f01b11';
|
String _$customAppHash() => r'be05431ba8bf06fd20ee988a61c3663a68e15fc9';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
@@ -29,6 +29,148 @@ class _SystemHash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See also [customApp].
|
||||||
|
@ProviderFor(customApp)
|
||||||
|
const customAppProvider = CustomAppFamily();
|
||||||
|
|
||||||
|
/// See also [customApp].
|
||||||
|
class CustomAppFamily extends Family<AsyncValue<CustomApp>> {
|
||||||
|
/// See also [customApp].
|
||||||
|
const CustomAppFamily();
|
||||||
|
|
||||||
|
/// See also [customApp].
|
||||||
|
CustomAppProvider call(String publisherName, String projectId, String appId) {
|
||||||
|
return CustomAppProvider(publisherName, projectId, appId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
CustomAppProvider getProviderOverride(covariant CustomAppProvider provider) {
|
||||||
|
return call(provider.publisherName, provider.projectId, provider.appId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'customAppProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [customApp].
|
||||||
|
class CustomAppProvider extends AutoDisposeFutureProvider<CustomApp> {
|
||||||
|
/// See also [customApp].
|
||||||
|
CustomAppProvider(String publisherName, String projectId, String appId)
|
||||||
|
: this._internal(
|
||||||
|
(ref) =>
|
||||||
|
customApp(ref as CustomAppRef, publisherName, projectId, appId),
|
||||||
|
from: customAppProvider,
|
||||||
|
name: r'customAppProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$customAppHash,
|
||||||
|
dependencies: CustomAppFamily._dependencies,
|
||||||
|
allTransitiveDependencies: CustomAppFamily._allTransitiveDependencies,
|
||||||
|
publisherName: publisherName,
|
||||||
|
projectId: projectId,
|
||||||
|
appId: appId,
|
||||||
|
);
|
||||||
|
|
||||||
|
CustomAppProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.publisherName,
|
||||||
|
required this.projectId,
|
||||||
|
required this.appId,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String publisherName;
|
||||||
|
final String projectId;
|
||||||
|
final String appId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<CustomApp> Function(CustomAppRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: CustomAppProvider._internal(
|
||||||
|
(ref) => create(ref as CustomAppRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
publisherName: publisherName,
|
||||||
|
projectId: projectId,
|
||||||
|
appId: appId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<CustomApp> createElement() {
|
||||||
|
return _CustomAppProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is CustomAppProvider &&
|
||||||
|
other.publisherName == publisherName &&
|
||||||
|
other.projectId == projectId &&
|
||||||
|
other.appId == appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, publisherName.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, projectId.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, appId.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin CustomAppRef on AutoDisposeFutureProviderRef<CustomApp> {
|
||||||
|
/// The parameter `publisherName` of this provider.
|
||||||
|
String get publisherName;
|
||||||
|
|
||||||
|
/// The parameter `projectId` of this provider.
|
||||||
|
String get projectId;
|
||||||
|
|
||||||
|
/// The parameter `appId` of this provider.
|
||||||
|
String get appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomAppProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<CustomApp>
|
||||||
|
with CustomAppRef {
|
||||||
|
_CustomAppProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get publisherName => (origin as CustomAppProvider).publisherName;
|
||||||
|
@override
|
||||||
|
String get projectId => (origin as CustomAppProvider).projectId;
|
||||||
|
@override
|
||||||
|
String get appId => (origin as CustomAppProvider).appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$customAppsHash() => r'450bedaf4220b8963cb44afeb14d4c0e80f01b11';
|
||||||
|
|
||||||
/// See also [customApps].
|
/// See also [customApps].
|
||||||
@ProviderFor(customApps)
|
@ProviderFor(customApps)
|
||||||
const customAppsProvider = CustomAppsFamily();
|
const customAppsProvider = CustomAppsFamily();
|
||||||
|
@@ -183,7 +183,7 @@ class ArticlesScreen extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -6,7 +6,7 @@ part of 'articles.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$subscribedFeedsHash() => r'cd2f5d7d4ea49ad00dc731f8fc2ed65450a3f0e4';
|
String _$subscribedFeedsHash() => r'5c0c8c30c5f543f6ea1d39786a6778f77ba5b3df';
|
||||||
|
|
||||||
/// See also [subscribedFeeds].
|
/// See also [subscribedFeeds].
|
||||||
@ProviderFor(subscribedFeeds)
|
@ProviderFor(subscribedFeeds)
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:island/screens/chat/chat.dart';
|
import 'package:island/screens/chat/chat.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:island/models/chat.dart';
|
import 'package:island/models/chat.dart';
|
||||||
|
Reference in New Issue
Block a user