Compare commits
5 Commits
abf395ff9a
...
3.2.0+128
Author | SHA1 | Date | |
---|---|---|---|
|
06f04eb3a5 | ||
|
8af97e43b4 | ||
|
d1e8234b93 | ||
|
a03d6015a6 | ||
|
246ac52d0a |
@@ -914,5 +914,17 @@
|
||||
"other": "{} articles"
|
||||
},
|
||||
"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",
|
||||
"createdAt": "Created at {}",
|
||||
"newSecretGenerated": "New Secret Generated",
|
||||
"copySecretHint": "Please copy this secret and store it somewhere safe. You will not be able to see it again.",
|
||||
"expiresIn": "Expires In (seconds)",
|
||||
"isOidc": "OIDC Compliant"
|
||||
}
|
@@ -843,5 +843,17 @@
|
||||
"socialCreditsLevelPoor": "糟糕",
|
||||
"socialCreditsLevelNormal": "正常",
|
||||
"socialCreditsLevelGood": "良好",
|
||||
"socialCreditsLevelExcellent": "优秀"
|
||||
"socialCreditsLevelExcellent": "优秀",
|
||||
"appDetails": "应用详情",
|
||||
"secrets": "密钥",
|
||||
"appNotFound": "应用未找到。",
|
||||
"secretCopied": "密钥已复制到剪贴板。",
|
||||
"deleteSecret": "删除密钥",
|
||||
"deleteSecretHint": "您确定要删除此密钥吗?此操作无法撤销。",
|
||||
"generateSecret": "生成新密钥",
|
||||
"createdAt": "创建于 {}",
|
||||
"newSecretGenerated": "已生成新密钥",
|
||||
"copySecretHint": "请复制此密钥并将其存放在安全的地方。您将无法再次看到它。",
|
||||
"expiresIn": "过期时间(秒)",
|
||||
"isOidc": "OIDC 兼容"
|
||||
}
|
||||
|
@@ -811,5 +811,17 @@
|
||||
"filesListAdditional": {
|
||||
"one": "+{} 個文件被摺疊",
|
||||
"other": "+{} 個文件被摺疊"
|
||||
}
|
||||
},
|
||||
"appDetails": "應用程式詳情",
|
||||
"secrets": "密鑰",
|
||||
"appNotFound": "找不到應用程式。",
|
||||
"secretCopied": "密鑰已複製到剪貼簿。",
|
||||
"deleteSecret": "刪除密鑰",
|
||||
"deleteSecretHint": "您確定要刪除此密鑰嗎?此操作無法復原。",
|
||||
"generateSecret": "產生新密鑰",
|
||||
"createdAt": "建立於 {}",
|
||||
"newSecretGenerated": "已產生新密鑰",
|
||||
"copySecretHint": "請複製此密鑰並將其存放在安全的地方。您將無法再次看到它。",
|
||||
"expiresIn": "過期時間(秒)",
|
||||
"isOidc": "OIDC 相容"
|
||||
}
|
19
lib/models/custom_app_secret.dart
Normal file
19
lib/models/custom_app_secret.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
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,
|
||||
int? expiresIn,
|
||||
bool? isOidc,
|
||||
}) = _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; int? get expiresIn; bool? get isOidc;
|
||||
/// 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)&&(identical(other.expiresIn, expiresIn) || other.expiresIn == expiresIn)&&(identical(other.isOidc, isOidc) || other.isOidc == isOidc));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,secret,createdAt,description,expiresIn,isOidc);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CustomAppSecret(id: $id, secret: $secret, createdAt: $createdAt, description: $description, expiresIn: $expiresIn, isOidc: $isOidc)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @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, int? expiresIn, bool? isOidc
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @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 = freezed,Object? createdAt = null,Object? description = freezed,Object? expiresIn = freezed,Object? isOidc = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,secret: freezed == 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?,expiresIn: freezed == expiresIn ? _self.expiresIn : expiresIn // ignore: cast_nullable_to_non_nullable
|
||||
as int?,isOidc: freezed == isOidc ? _self.isOidc : isOidc // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// 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);}
|
||||
}
|
||||
/// 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, int? expiresIn, bool? isOidc)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CustomAppSecret() when $default != null:
|
||||
return $default(_that.id,_that.secret,_that.createdAt,_that.description,_that.expiresIn,_that.isOidc);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, int? expiresIn, bool? isOidc) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CustomAppSecret():
|
||||
return $default(_that.id,_that.secret,_that.createdAt,_that.description,_that.expiresIn,_that.isOidc);}
|
||||
}
|
||||
/// 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, int? expiresIn, bool? isOidc)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CustomAppSecret() when $default != null:
|
||||
return $default(_that.id,_that.secret,_that.createdAt,_that.description,_that.expiresIn,_that.isOidc);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _CustomAppSecret implements CustomAppSecret {
|
||||
const _CustomAppSecret({required this.id, required this.secret, required this.createdAt, this.description, this.expiresIn, this.isOidc});
|
||||
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;
|
||||
@override final int? expiresIn;
|
||||
@override final bool? isOidc;
|
||||
|
||||
/// 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)&&(identical(other.expiresIn, expiresIn) || other.expiresIn == expiresIn)&&(identical(other.isOidc, isOidc) || other.isOidc == isOidc));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,secret,createdAt,description,expiresIn,isOidc);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CustomAppSecret(id: $id, secret: $secret, createdAt: $createdAt, description: $description, expiresIn: $expiresIn, isOidc: $isOidc)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @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, int? expiresIn, bool? isOidc
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @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 = freezed,Object? createdAt = null,Object? description = freezed,Object? expiresIn = freezed,Object? isOidc = freezed,}) {
|
||||
return _then(_CustomAppSecret(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,secret: freezed == 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?,expiresIn: freezed == expiresIn ? _self.expiresIn : expiresIn // ignore: cast_nullable_to_non_nullable
|
||||
as int?,isOidc: freezed == isOidc ? _self.isOidc : isOidc // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
27
lib/models/custom_app_secret.g.dart
Normal file
27
lib/models/custom_app_secret.g.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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?,
|
||||
expiresIn: (json['expires_in'] as num?)?.toInt(),
|
||||
isOidc: json['is_oidc'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CustomAppSecretToJson(_CustomAppSecret instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'secret': instance.secret,
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'description': instance.description,
|
||||
'expires_in': instance.expiresIn,
|
||||
'is_oidc': instance.isOidc,
|
||||
};
|
@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/screens/about.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/edit_app.dart';
|
||||
import 'package:island/screens/developers/edit_bot.dart';
|
||||
@@ -349,6 +350,16 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
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(
|
||||
name: 'developerBotDetail',
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
252
lib/screens/developers/app_secrets.dart
Normal file
252
lib/screens/developers/app_secrets.dart
Normal file
@@ -0,0 +1,252 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.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/services/time.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/content/sheet.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),
|
||||
);
|
||||
|
||||
void showNewSecretSheet(String newSecret) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder:
|
||||
(context) => SheetScaffold(
|
||||
titleText: 'newSecretGenerated'.tr(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('copySecretHint'.tr()),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: SelectableText(newSecret),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
FilledButton.icon(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: newSecret));
|
||||
},
|
||||
icon: const Icon(Symbols.copy_all),
|
||||
label: Text('copy'.tr()),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
).whenComplete(() {
|
||||
ref.invalidate(
|
||||
customAppSecretsProvider(publisherName, projectId, appId),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void createSecret() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return HookBuilder(
|
||||
builder: (context) {
|
||||
final descriptionController = useTextEditingController();
|
||||
final expiresInController = useTextEditingController();
|
||||
final isOidc = useState(false);
|
||||
|
||||
return SheetScaffold(
|
||||
titleText: 'generateSecret'.tr(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: descriptionController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'description'.tr(),
|
||||
),
|
||||
autofocus: true,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
controller: expiresInController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'expiresIn'.tr(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SwitchListTile(
|
||||
title: Text('isOidc'.tr()),
|
||||
value: isOidc.value,
|
||||
onChanged: (value) => isOidc.value = value,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
FilledButton.icon(
|
||||
onPressed: () async {
|
||||
final description = descriptionController.text;
|
||||
final expiresIn = int.tryParse(
|
||||
expiresInController.text,
|
||||
);
|
||||
Navigator.pop(context); // Close the sheet
|
||||
try {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final resp = await client.post(
|
||||
'/develop/developers/$publisherName/projects/$projectId/apps/$appId/secrets',
|
||||
data: {
|
||||
'description': description,
|
||||
'expires_in': expiresIn,
|
||||
'is_oidc': isOidc.value,
|
||||
},
|
||||
);
|
||||
final newSecret = CustomAppSecret.fromJson(
|
||||
resp.data,
|
||||
);
|
||||
if (newSecret.secret != null) {
|
||||
showNewSecretSheet(newSecret.secret!);
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorAlert(e.toString());
|
||||
}
|
||||
},
|
||||
icon: const Icon(Symbols.add),
|
||||
label: Text('create'.tr()),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return secrets.when(
|
||||
data: (data) {
|
||||
return RefreshIndicator(
|
||||
onRefresh:
|
||||
() => ref.refresh(
|
||||
customAppSecretsProvider(
|
||||
publisherName,
|
||||
projectId,
|
||||
appId,
|
||||
).future,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Symbols.add),
|
||||
title: Text('generateSecret'.tr()),
|
||||
onTap: createSecret,
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: data.length,
|
||||
itemBuilder: (context, index) {
|
||||
final secret = data[index];
|
||||
return ListTile(
|
||||
title: Text(secret.description ?? secret.id),
|
||||
subtitle: Text(
|
||||
'createdAt'.tr(args: [secret.createdAt.formatSystem()]),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
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}',
|
||||
);
|
||||
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';
|
||||
|
||||
@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
|
||||
Future<List<CustomApp>> customApps(
|
||||
Ref ref,
|
||||
@@ -81,98 +95,114 @@ class CustomAppsScreen extends HookConsumerWidget {
|
||||
final app = data[index];
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
if (app.background != null)
|
||||
CloudFileWidget(
|
||||
item: app.background!,
|
||||
fit: BoxFit.cover,
|
||||
).clipRRect(topLeft: 8, topRight: 8),
|
||||
if (app.picture != null)
|
||||
Positioned(
|
||||
left: 16,
|
||||
bottom: 16,
|
||||
child: ProfilePictureWidget(
|
||||
fileId: app.picture!.id,
|
||||
radius: 40,
|
||||
fallbackIcon: Symbols.apps,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(app.name),
|
||||
subtitle: Text(
|
||||
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(),
|
||||
],
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.pushNamed(
|
||||
'developerAppDetail',
|
||||
pathParameters: {
|
||||
'name': publisherName,
|
||||
'projectId': projectId,
|
||||
'appId': app.id,
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
if (app.background != null)
|
||||
CloudFileWidget(
|
||||
item: app.background!,
|
||||
fit: BoxFit.cover,
|
||||
).clipRRect(topLeft: 8, topRight: 8),
|
||||
if (app.picture != null)
|
||||
Positioned(
|
||||
left: 16,
|
||||
bottom: 16,
|
||||
child: ProfilePictureWidget(
|
||||
fileId: app.picture!.id,
|
||||
radius: 40,
|
||||
fallbackIcon: Symbols.apps,
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Symbols.delete,
|
||||
color: Colors.red,
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(app.name),
|
||||
subtitle: Text(
|
||||
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
|
||||
// **************************************************************************
|
||||
|
||||
String _$customAppsHash() => r'450bedaf4220b8963cb44afeb14d4c0e80f01b11';
|
||||
String _$customAppHash() => r'be05431ba8bf06fd20ee988a61c3663a68e15fc9';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
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].
|
||||
@ProviderFor(customApps)
|
||||
const customAppsProvider = CustomAppsFamily();
|
||||
|
@@ -101,7 +101,7 @@ class SliverArticlesList extends ConsumerWidget {
|
||||
publisherId: publisherId,
|
||||
).notifier,
|
||||
contentBuilder:
|
||||
(data, widgetCount, endItemView) => SliverList.builder(
|
||||
(data, widgetCount, endItemView) => SliverList.separated(
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == widgetCount - 1) {
|
||||
@@ -111,6 +111,7 @@ class SliverArticlesList extends ConsumerWidget {
|
||||
final article = data.items[index];
|
||||
return WebArticleCard(article: article, showDetails: true);
|
||||
},
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 12),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -155,7 +156,7 @@ class ArticlesScreen extends ConsumerWidget {
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
top: 12,
|
||||
left: 8,
|
||||
right: 8,
|
||||
),
|
||||
@@ -183,7 +184,7 @@ class ArticlesScreen extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@@ -6,7 +6,7 @@ part of 'articles.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$subscribedFeedsHash() => r'cd2f5d7d4ea49ad00dc731f8fc2ed65450a3f0e4';
|
||||
String _$subscribedFeedsHash() => r'5c0c8c30c5f543f6ea1d39786a6778f77ba5b3df';
|
||||
|
||||
/// See also [subscribedFeeds].
|
||||
@ProviderFor(subscribedFeeds)
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:island/screens/chat/chat.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:island/models/chat.dart';
|
||||
|
@@ -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
|
||||
# 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.
|
||||
version: 3.2.0+127
|
||||
version: 3.2.0+128
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.2
|
||||
|
Reference in New Issue
Block a user