From 604e4cfde7024c515bf4297dc165d69ad2f8abc6 Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 9 May 2023 17:18:13 -0600 Subject: [PATCH] install theme from zip archive functionality --- lib/themes/theme_service.dart | 66 +++++++++++++++++++++++++++++++++-- pubspec.lock | 2 +- pubspec.yaml | 1 + 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/lib/themes/theme_service.dart b/lib/themes/theme_service.dart index 47ae4b235..dbd5ba330 100644 --- a/lib/themes/theme_service.dart +++ b/lib/themes/theme_service.dart @@ -1,9 +1,14 @@ +import 'dart:convert'; +import 'dart:io'; import 'dart:typed_data'; +import 'package:archive/archive_io.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:isar/isar.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; +import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/stack_file_system.dart'; final pThemeService = Provider<ThemeService>((ref) { return ThemeService.instance; @@ -20,11 +25,68 @@ class ThemeService { void init(MainDB db) => _db ??= db; Future<void> install({required ByteData themeArchive}) async { - // todo unzip, install, and create theme object and add to db + final themesDir = await StackFileSystem.applicationThemesDirectory(); + + final byteStream = InputStream(themeArchive); + final archive = ZipDecoder().decodeBuffer(byteStream); + + final themeJsonFiles = archive.files.where((e) => e.name == "theme.json"); + + if (themeJsonFiles.length != 1) { + throw Exception("Invalid theme archive: Missing theme.json"); + } + + final OutputStream os = OutputStream(); + themeJsonFiles.first.decompress(os); + final String jsonString = utf8.decode(os.getBytes()); + final json = jsonDecode(jsonString) as Map; + + final theme = StackTheme.fromJson( + json: Map<String, dynamic>.from(json), + applicationThemesDirectoryPath: themesDir.path, + ); + + final String assetsPath = "${themesDir.path}/${theme.themeId}"; + + for (final file in archive.files) { + if (file.isFile) { + // TODO more sanitation? + if (file.name.contains("..")) { + Logging.instance.log( + "Bad theme asset file path: ${file.name}", + level: LogLevel.Error, + ); + } else { + final os = OutputFileStream("$assetsPath/${file.name}"); + file.writeContent(os); + await os.close(); + } + } + } + + await db.isar.writeTxn(() async { + await db.isar.stackThemes.put(theme); + }); } Future<void> remove({required String themeId}) async { - // todo delete local files and remove from db + final themesDir = await StackFileSystem.applicationThemesDirectory(); + final isarId = await db.isar.stackThemes + .where() + .themeIdEqualTo(themeId) + .idProperty() + .findFirst(); + if (isarId != null) { + await db.isar.writeTxn(() async { + await db.isar.stackThemes.delete(isarId); + }); + await Directory("${themesDir.path}/$themeId").delete(recursive: true); + } else { + Logging.instance.log( + "Failed to delete theme $themeId", + level: LogLevel.Warning, + ); + } } Future<List<Map<String, dynamic>>> fetchThemeList() async { diff --git a/pubspec.lock b/pubspec.lock index d9654c4b6..50a09b6fa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,7 +42,7 @@ packages: source: hosted version: "4.2.0" archive: - dependency: transitive + dependency: "direct main" description: name: archive sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb" diff --git a/pubspec.yaml b/pubspec.yaml index 91ed72013..9b48c2cf9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -151,6 +151,7 @@ dependencies: dart_bs58check: ^3.0.2 hex: ^0.2.0 rational: ^2.2.2 + archive: ^3.3.2 dev_dependencies: flutter_test: