feat: tela de apps com logos/menu de exclusao, correcao de tema nas mini apps e sidebar de navegacao
Some checks failed
Main Deploy Workflow / deploy_web (push) Has been cancelled
Main Deploy Workflow / deploy_playstore_internal (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
2026-09-19 00:08:35 -03:00
parent 1f692b1e4e
commit 6c689d1f34
23 changed files with 1039 additions and 114 deletions

View File

@@ -10,7 +10,9 @@ import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:material_ui/material_ui.dart';
import 'package:matrix/matrix.dart';
import 'package:tatchat/config/miniapp_config.dart';
import 'package:tatchat/config/setting_keys.dart';
import 'package:tatchat/utils/miniapp_meta.dart';
import 'package:tatchat/utils/miniapp_store.dart';
import 'package:tatchat/widgets/matrix.dart';
@@ -46,12 +48,8 @@ class AppsController extends State<Apps> {
allowedExtensions: ['html', 'htm'],
);
if (file == null) return;
final name = file.name.replaceAll(RegExp(r'\.(html?|htm)$'), '');
final bytes = await file.readAsBytes();
await _importHtml(
name: name.isEmpty ? 'Imported app' : name,
html: _decodeBytes(bytes),
);
await _importHtml(html: _decodeBytes(bytes));
if (!mounted) return;
setState(() {});
}
@@ -68,14 +66,7 @@ class AppsController extends State<Apps> {
if (response.statusCode != 200) {
throw Exception('${response.statusCode} ${response.reasonPhrase}');
}
var name = Uri.parse(url).pathSegments.isNotEmpty
? Uri.parse(url).pathSegments.last.replaceAll(
RegExp(r'\.(html?|htm)$'),
'',
)
: url;
if (name.isEmpty) name = 'Imported app';
await _importHtml(name: name, html: response.body);
await _importHtml(html: response.body);
if (!mounted) return;
setState(() {});
}
@@ -115,16 +106,24 @@ class AppsController extends State<Apps> {
return latin1.decode(bytes, allowInvalid: true);
}
Future<void> _importHtml({
required String name,
required String html,
}) =>
client.importMiniApp(
appId: 'imported-${DateTime.now().millisecondsSinceEpoch}',
name: name,
html: html,
/// Reads the mini app manifest from the meta tags of [html], validates it
/// and installs the app. The manifest `id` is used as the account data key.
///
/// Throws a [FormatException] when the document lacks the required meta
/// tags, or an [Exception] when the id is already taken.
Future<void> _importHtml({required String html}) async {
final meta = parseMiniAppMeta(html);
if (availableMiniApps.any((app) => app.id == meta.id)) {
throw Exception(
'The mini app id "${meta.id}" is reserved and cannot be reused',
);
}
if (client.isMiniAppInstalled(meta.id)) {
throw Exception('A mini app with id "${meta.id}" is already installed');
}
await client.importMiniApp(meta: meta, html: html);
}
@override
Widget build(BuildContext context) => AppsView(this);
}
}