mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-21 22:58:49 +00:00
various fixes, layout tweaks, and refactoring
This commit is contained in:
parent
6ed6ffb18b
commit
6990d60b9b
4 changed files with 234 additions and 100 deletions
|
@ -1645,7 +1645,9 @@ class StackTheme {
|
||||||
backgroundInt: parseColor(json["colors"]["background"] as String),
|
backgroundInt: parseColor(json["colors"]["background"] as String),
|
||||||
backgroundAppBarInt:
|
backgroundAppBarInt:
|
||||||
parseColor(json["colors"]["background_app_bar"] as String),
|
parseColor(json["colors"]["background_app_bar"] as String),
|
||||||
gradientBackgroundString: json["colors"]["gradients"] as String?,
|
gradientBackgroundString: json["colors"]["gradients"] != null
|
||||||
|
? jsonEncode(json["colors"]["gradients"])
|
||||||
|
: null,
|
||||||
standardBoxShadowString:
|
standardBoxShadowString:
|
||||||
jsonEncode(json["colors"]["box_shadows"]["standard"] as Map),
|
jsonEncode(json["colors"]["box_shadows"]["standard"] as Map),
|
||||||
homeViewButtonBarBoxShadowString:
|
homeViewButtonBarBoxShadowString:
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/pages/settings_views/global_settings_view/appearance_settings/sub_widgets/stack_theme_card.dart';
|
||||||
import 'package:stackwallet/themes/stack_colors.dart';
|
import 'package:stackwallet/themes/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/themes/theme_service.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
|
||||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
|
||||||
|
|
||||||
class ManageThemesView extends StatefulWidget {
|
class ManageThemesView extends ConsumerStatefulWidget {
|
||||||
const ManageThemesView({Key? key}) : super(key: key);
|
const ManageThemesView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
static const String routeName = "/manageThemes";
|
static const String routeName = "/manageThemes";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ManageThemesView> createState() => _ManageThemesViewState();
|
ConsumerState<ManageThemesView> createState() => _ManageThemesViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ManageThemesViewState extends State<ManageThemesView> {
|
class _ManageThemesViewState extends ConsumerState<ManageThemesView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ConditionalParent(
|
return ConditionalParent(
|
||||||
|
@ -36,105 +37,64 @@ class _ManageThemesViewState extends State<ManageThemesView> {
|
||||||
style: STextStyles.navBarTitle(context),
|
style: STextStyles.navBarTitle(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: Column(
|
||||||
child: Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(
|
Expanded(
|
||||||
horizontal: 16,
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: child,
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: SecondaryButton(
|
||||||
|
label: "Install theme file",
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
FutureBuilder(
|
||||||
height: 16,
|
future: ref.watch(pThemeService).fetchThemes(),
|
||||||
),
|
builder: (
|
||||||
GridView.builder(
|
context,
|
||||||
shrinkWrap: true,
|
AsyncSnapshot<List<StackThemeMetaData>> snapshot,
|
||||||
primary: false,
|
) {
|
||||||
itemCount: 100,
|
if (snapshot.connectionState == ConnectionState.done &&
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
snapshot.hasData) {
|
||||||
crossAxisCount: 2,
|
return Wrap(
|
||||||
crossAxisSpacing: 16,
|
spacing: 16,
|
||||||
mainAxisSpacing: 16,
|
runSpacing: 16,
|
||||||
childAspectRatio: 2 / 2.7,
|
children: snapshot.data!
|
||||||
),
|
.map(
|
||||||
itemBuilder: (_, index) {
|
(e) => SizedBox(
|
||||||
return StackThemeCard(
|
width: (MediaQuery.of(context).size.width - 48) / 2,
|
||||||
name: index.toString(),
|
child: StackThemeCard(
|
||||||
size: "lol GB",
|
data: e,
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Center(
|
||||||
|
child: LoadingIndicator(
|
||||||
|
width: (MediaQuery.of(context).size.width - 48) / 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(
|
|
||||||
height: 28,
|
|
||||||
),
|
|
||||||
SecondaryButton(
|
|
||||||
label: "Install theme file",
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StackThemeCard extends StatefulWidget {
|
|
||||||
const StackThemeCard({
|
|
||||||
Key? key,
|
|
||||||
required this.name,
|
|
||||||
required this.size,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
final String size;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StackThemeCard> createState() => _StackThemeCardState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _StackThemeCardState extends State<StackThemeCard> {
|
|
||||||
String buttonLabel = "Download";
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return RoundedWhiteContainer(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 18,
|
|
||||||
),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1,
|
|
||||||
child: RoundedContainer(
|
|
||||||
color: Colors.grey,
|
|
||||||
radiusMultiplier: 100,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
widget.name,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 6,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
widget.size,
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
PrimaryButton(
|
|
||||||
label: buttonLabel,
|
|
||||||
buttonHeight: ButtonHeight.l,
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
import 'package:stackwallet/models/isar/stack_theme.dart';
|
||||||
|
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||||
|
import 'package:stackwallet/themes/theme_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
|
|
||||||
|
class StackThemeCard extends ConsumerStatefulWidget {
|
||||||
|
const StackThemeCard({
|
||||||
|
Key? key,
|
||||||
|
required this.data,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final StackThemeMetaData data;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<StackThemeCard> createState() => _StackThemeCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StackThemeCardState extends ConsumerState<StackThemeCard> {
|
||||||
|
String buttonLabel = "Download";
|
||||||
|
|
||||||
|
late bool _hasTheme;
|
||||||
|
|
||||||
|
Future<bool> _downloadAndInstall() async {
|
||||||
|
final service = ref.read(pThemeService);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final data = await service.fetchTheme(
|
||||||
|
themeMetaData: widget.data,
|
||||||
|
);
|
||||||
|
|
||||||
|
await service.install(themeArchive: data);
|
||||||
|
return true;
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Failed _downloadAndInstall of ${widget.data.id}: $e\n$s",
|
||||||
|
level: LogLevel.Warning,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _downloadPressed() async {
|
||||||
|
final result = await showLoading(
|
||||||
|
whileFuture: _downloadAndInstall(),
|
||||||
|
context: context,
|
||||||
|
message: "Downloading and installing theme...",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
final message = result
|
||||||
|
? "${widget.data.name} theme installed!"
|
||||||
|
: "Failed to install ${widget.data.name} theme";
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => StackOkDialog(
|
||||||
|
title: message,
|
||||||
|
onOkPressed: (_) {
|
||||||
|
setState(() {
|
||||||
|
_hasTheme = result;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
late final StreamSubscription<void> _subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_hasTheme = ref
|
||||||
|
.read(mainDBProvider)
|
||||||
|
.isar
|
||||||
|
.stackThemes
|
||||||
|
.where()
|
||||||
|
.themeIdEqualTo(widget.data.id)
|
||||||
|
.countSync() >
|
||||||
|
0;
|
||||||
|
|
||||||
|
_subscription = ref
|
||||||
|
.read(mainDBProvider)
|
||||||
|
.isar
|
||||||
|
.stackThemes
|
||||||
|
.watchLazy()
|
||||||
|
.listen((event) async {
|
||||||
|
final hasTheme = (await ref
|
||||||
|
.read(mainDBProvider)
|
||||||
|
.isar
|
||||||
|
.stackThemes
|
||||||
|
.where()
|
||||||
|
.themeIdEqualTo(widget.data.id)
|
||||||
|
.count()) >
|
||||||
|
0;
|
||||||
|
if (_hasTheme != hasTheme && mounted) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
|
setState(() {
|
||||||
|
_hasTheme = hasTheme;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_subscription.resume();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subscription.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return RoundedWhiteContainer(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 18,
|
||||||
|
),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(100),
|
||||||
|
child: Image.network(
|
||||||
|
widget.data.previewImageUrl,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
widget.data.name,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 6,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
widget.data.size,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
PrimaryButton(
|
||||||
|
label: buttonLabel,
|
||||||
|
enabled: !_hasTheme,
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: _downloadPressed,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,7 +117,7 @@ class ThemeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<StackThemeMetaData>> fetchThemeList() async {
|
Future<List<StackThemeMetaData>> fetchThemes() async {
|
||||||
try {
|
try {
|
||||||
final response = await get(Uri.parse("$baseServerUrl/themes"));
|
final response = await get(Uri.parse("$baseServerUrl/themes"));
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ class ThemeService {
|
||||||
|
|
||||||
final result = List<Map<String, dynamic>>.from(jsonList)
|
final result = List<Map<String, dynamic>>.from(jsonList)
|
||||||
.map((e) => StackThemeMetaData.fromMap(e))
|
.map((e) => StackThemeMetaData.fromMap(e))
|
||||||
|
.where((e) => e.id != "light" && e.id != "dark")
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -178,12 +179,14 @@ class StackThemeMetaData {
|
||||||
final String name;
|
final String name;
|
||||||
final String id;
|
final String id;
|
||||||
final String sha256;
|
final String sha256;
|
||||||
|
final String size;
|
||||||
final String previewImageUrl;
|
final String previewImageUrl;
|
||||||
|
|
||||||
StackThemeMetaData({
|
StackThemeMetaData({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.sha256,
|
required this.sha256,
|
||||||
|
required this.size,
|
||||||
required this.previewImageUrl,
|
required this.previewImageUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -193,6 +196,7 @@ class StackThemeMetaData {
|
||||||
name: map["name"] as String,
|
name: map["name"] as String,
|
||||||
id: map["id"] as String,
|
id: map["id"] as String,
|
||||||
sha256: map["sha256"] as String,
|
sha256: map["sha256"] as String,
|
||||||
|
size: map["size"] as String,
|
||||||
previewImageUrl: map["previewImageUrl"] as String,
|
previewImageUrl: map["previewImageUrl"] as String,
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
@ -210,6 +214,7 @@ class StackThemeMetaData {
|
||||||
"name: $name, "
|
"name: $name, "
|
||||||
"id: $id, "
|
"id: $id, "
|
||||||
"sha256: $sha256, "
|
"sha256: $sha256, "
|
||||||
|
"size: $size, "
|
||||||
"previewImageUrl: $previewImageUrl"
|
"previewImageUrl: $previewImageUrl"
|
||||||
")";
|
")";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue