AppFlowy/frontend/appflowy_flutter/lib/shared/feature_flags.dart

153 lines
4.5 KiB
Dart
Raw Normal View History

import 'dart:convert';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/startup/startup.dart';
2024-03-21 04:02:03 +00:00
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
typedef FeatureFlagMap = Map<FeatureFlag, bool>;
/// The [FeatureFlag] is used to control the front-end features of the app.
///
/// For example, if your feature is still under development,
/// you can set the value to `false` to hide the feature.
enum FeatureFlag {
// used to control the visibility of the collaborative workspace feature
// if it's on, you can see the workspace list and the workspace settings
// in the top-left corner of the app
collaborativeWorkspace,
// used to control the visibility of the members settings
// if it's on, you can see the members settings in the settings page
2024-03-21 04:02:03 +00:00
membersSettings,
// used to control the sync feature of the document
// if it's on, the document will be synced the events from server in real-time
syncDocument,
// used to control the sync feature of the database
// if it's on, the collaborators will show in the database
syncDatabase,
// used for the search feature
search,
// used for controlling whether to show plan+billing options in settings
planBilling,
// used for space design
spaceDesign,
2024-03-21 04:02:03 +00:00
// used for ignore the conflicted feature flag
unknown;
static Future<void> initialize() async {
final values = await getIt<KeyValueStorage>().getWithFormat<FeatureFlagMap>(
KVKeys.featureFlag,
(value) => Map.from(jsonDecode(value)).map(
2024-03-21 04:02:03 +00:00
(key, value) {
final k = FeatureFlag.values.firstWhereOrNull(
(e) => e.name == key,
) ??
FeatureFlag.unknown;
return MapEntry(k, value as bool);
},
),
) ??
{};
_values = {
...{for (final flag in FeatureFlag.values) flag: false},
...values,
};
}
static UnmodifiableMapView<FeatureFlag, bool> get data =>
UnmodifiableMapView(_values);
Future<void> turnOn() async {
await update(true);
}
Future<void> turnOff() async {
await update(false);
}
Future<void> update(bool value) async {
_values[this] = value;
await getIt<KeyValueStorage>().set(
KVKeys.featureFlag,
jsonEncode(
_values.map((key, value) => MapEntry(key.name, value)),
),
);
}
static Future<void> clear() async {
_values = {};
await getIt<KeyValueStorage>().remove(KVKeys.featureFlag);
}
bool get isOn {
if ([
2024-07-26 04:03:29 +00:00
FeatureFlag.planBilling,
// release this feature in version 0.6.1
FeatureFlag.spaceDesign,
2024-06-05 08:01:42 +00:00
// release this feature in version 0.5.9
FeatureFlag.search,
// release this feature in version 0.5.6
FeatureFlag.collaborativeWorkspace,
FeatureFlag.membersSettings,
2024-04-09 12:05:28 +00:00
// release this feature in version 0.5.4
FeatureFlag.syncDatabase,
FeatureFlag.syncDocument,
].contains(this)) {
return true;
}
if (_values.containsKey(this)) {
return _values[this]!;
}
switch (this) {
feat: ai billing (#5741) * feat: start on AI plan+billing UI * chore: enable plan and billing * feat: cache workspace subscription + minor fixes (#5705) * feat: update api from billing * feat: add api for workspace subscription info (#5717) * feat: refactor and start integrating AI plans * feat: refine UI and add business logic for AI * feat: complete UIUX for AI and limits * chore: remove resolved todo * chore: localize remove addon dialog * chore: fix spacing issue for usage * fix: interpret subscription + usage on action * chore: update api for billing (#5735) * chore: update revisions * fix: remove subscription cache * fix: copy improvements + use consistent dialog * chore: update to the latest client api * feat: support updating billing period * Feat/ai billing cancel reason (#5752) * chore: add cancellation reason field * fix: ci add one retry for concurrent sign up * chore: merge with main * chore: half merge * chore: fix conflict * chore: observer error * chore: remove unneeded protobuf and remove unwrap * feat: added subscription plan details * chore: check error code and update sidebar toast * chore: periodically check billing state * chore: editor ai error * chore: return file upload error * chore: fmt * chore: clippy * chore: disable upload image when exceed storage limitation * chore: remove todo * chore: remove openai i18n * chore: update log * chore: update client-api to fix stream error * chore: clippy * chore: fix language file * chore: disable billing UI --------- Co-authored-by: Zack Fu Zi Xiang <speed2exe@live.com.sg> Co-authored-by: nathan <nathan@appflowy.io>
2024-07-22 07:43:48 +00:00
case FeatureFlag.planBilling:
case FeatureFlag.search:
case FeatureFlag.syncDocument:
case FeatureFlag.syncDatabase:
case FeatureFlag.spaceDesign:
return true;
case FeatureFlag.collaborativeWorkspace:
case FeatureFlag.membersSettings:
case FeatureFlag.unknown:
return false;
}
}
String get description {
switch (this) {
case FeatureFlag.collaborativeWorkspace:
return 'if it\'s on, you can see the workspace list and the workspace settings in the top-left corner of the app';
case FeatureFlag.membersSettings:
return 'if it\'s on, you can see the members settings in the settings page';
case FeatureFlag.syncDocument:
return 'if it\'s on, the document will be synced in real-time';
case FeatureFlag.syncDatabase:
return 'if it\'s on, the collaborators will show in the database';
case FeatureFlag.search:
return 'if it\'s on, the command palette and search button will be available';
case FeatureFlag.planBilling:
return 'if it\'s on, plan and billing pages will be available in Settings';
case FeatureFlag.spaceDesign:
return 'if it\'s on, the space design feature will be available';
2024-03-21 04:02:03 +00:00
case FeatureFlag.unknown:
return '';
}
}
String get key => 'appflowy_feature_flag_${toString()}';
}
FeatureFlagMap _values = {};