feat: mini apps customizados com importacao por arquivo/URL, paleta de cores, avatar autenticado e documentacao
This commit is contained in:
94
lib/utils/miniapp_store.dart
Normal file
94
lib/utils/miniapp_store.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
|
||||
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
/// Account data key that holds the list of installed mini app IDs.
|
||||
const String installedMiniAppsKey = 'im.tatuchat.miniapps.installed';
|
||||
|
||||
/// Prefix of the account data keys that hold a mini app's JSON document.
|
||||
const String miniAppDataKeyPrefix = 'im.tatuchat.miniapps.data.';
|
||||
|
||||
/// Prefix of the account data keys that hold the HTML source of an imported
|
||||
/// mini app. The key is ``{prefix}{appId}`` and the content is
|
||||
/// `{name: '...', html: '...'}`.
|
||||
const String miniAppSourceKeyPrefix = 'im.tatuchat.miniapps.source.';
|
||||
|
||||
/// Extension that gives every [Client] access to the mini apps store which is
|
||||
/// persisted in the user's account data ("localStorage on the server").
|
||||
extension MiniAppStoreExtension on Client {
|
||||
List<String> get installedMiniApps {
|
||||
final apps = accountData[installedMiniAppsKey]?.content['apps'];
|
||||
if (apps is List) return [...apps.whereType<String>()];
|
||||
return [];
|
||||
}
|
||||
|
||||
bool isMiniAppInstalled(String appId) => installedMiniApps.contains(appId);
|
||||
|
||||
Future<void> installMiniApp(String appId) async {
|
||||
final apps = installedMiniApps;
|
||||
if (apps.contains(appId)) return;
|
||||
apps.add(appId);
|
||||
await setAccountData(userID!, installedMiniAppsKey, {'apps': apps});
|
||||
}
|
||||
|
||||
Future<void> uninstallMiniApp(String appId) async {
|
||||
final apps = installedMiniApps..remove(appId);
|
||||
await setAccountData(userID!, installedMiniAppsKey, {'apps': apps});
|
||||
// Clear the custom HTML source if present.
|
||||
await setAccountData(userID!, '$miniAppSourceKeyPrefix$appId', {
|
||||
'html': '',
|
||||
'name': '',
|
||||
});
|
||||
}
|
||||
|
||||
/// Reads the JSON document of the given mini app. Returns an empty map if
|
||||
/// the app never stored anything.
|
||||
Map<String, dynamic> getMiniAppData(String appId) =>
|
||||
accountData['$miniAppDataKeyPrefix$appId']?.content ?? const {};
|
||||
|
||||
/// Overwrites the JSON document of the given mini app.
|
||||
Future<void> setMiniAppData(String appId, Map<String, Object?> data) =>
|
||||
setAccountData(userID!, '$miniAppDataKeyPrefix$appId', data);
|
||||
|
||||
// ---------- Imported (custom) apps ----------
|
||||
|
||||
/// Stores the HTML source and a display name for a user-imported mini app
|
||||
/// and adds it to the installed list if not already present.
|
||||
Future<void> importMiniApp({
|
||||
required String appId,
|
||||
required String name,
|
||||
required String html,
|
||||
}) async {
|
||||
await setAccountData(userID!, '$miniAppSourceKeyPrefix$appId', {
|
||||
'name': name,
|
||||
'html': html,
|
||||
});
|
||||
final apps = installedMiniApps;
|
||||
if (!apps.contains(appId)) {
|
||||
apps.add(appId);
|
||||
await setAccountData(userID!, installedMiniAppsKey, {'apps': apps});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the raw HTML source of an imported mini app, or null if the app
|
||||
/// is a built-in app or was never imported.
|
||||
String? getMiniAppSource(String appId) {
|
||||
final content =
|
||||
accountData['$miniAppSourceKeyPrefix$appId']?.content;
|
||||
if (content == null) return null;
|
||||
final html = content['html'];
|
||||
return (html is String && html.isNotEmpty) ? html : null;
|
||||
}
|
||||
|
||||
/// Returns the display name of an imported mini app, or null.
|
||||
String? getMiniAppName(String appId) {
|
||||
final content =
|
||||
accountData['$miniAppSourceKeyPrefix$appId']?.content;
|
||||
if (content == null) return null;
|
||||
final name = content['name'];
|
||||
return (name is String && name.isNotEmpty) ? name : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user