mirror of
https://github.com/foss42/apidash.git
synced 2025-06-22 23:39:11 +08:00
Merge pull request #464 from foss42/dev-workspace
Users can select the persistent data location
This commit is contained in:
@ -1,3 +1,6 @@
|
||||
import 'package:apidash/models/settings_model.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/services/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@ -34,17 +37,24 @@ class ApidashTestHelper {
|
||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
|
||||
|
||||
await app.initApp();
|
||||
await app.initApp(false);
|
||||
await app.initWindow(sz: size);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
static Future<void> loadApp(WidgetTester tester) async {
|
||||
await app.initApp();
|
||||
await app.initApp(false);
|
||||
await tester.pumpWidget(
|
||||
const ProviderScope(
|
||||
child: DashApp(),
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
settingsProvider.overrideWith(
|
||||
(ref) => ThemeStateNotifier(
|
||||
settingsModel: const SettingsModel()
|
||||
.copyWithPath(workspaceFolderPath: "test")),
|
||||
)
|
||||
],
|
||||
child: const DashApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -123,6 +133,7 @@ void apidashWidgetTest(
|
||||
size: width != null ? Size(width, kMinWindowSize.height) : null);
|
||||
await ApidashTestHelper.loadApp(widgetTester);
|
||||
await test(widgetTester, ApidashTestHelper(widgetTester));
|
||||
await clearSharedPrefs();
|
||||
},
|
||||
semanticsEnabled: false,
|
||||
);
|
||||
|
55
lib/app.dart
55
lib/app.dart
@ -4,8 +4,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:window_manager/window_manager.dart' hide WindowCaption;
|
||||
import 'widgets/widgets.dart' show WindowCaption;
|
||||
import 'widgets/widgets.dart' show WindowCaption, WorkspaceSelector;
|
||||
import 'providers/providers.dart';
|
||||
import 'services/services.dart';
|
||||
import 'extensions/extensions.dart';
|
||||
import 'screens/screens.dart';
|
||||
import 'consts.dart';
|
||||
@ -107,29 +108,49 @@ class DashApp extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isDarkMode =
|
||||
ref.watch(settingsProvider.select((value) => value.isDark));
|
||||
final workspaceFolderPath = ref
|
||||
.watch(settingsProvider.select((value) => value.workspaceFolderPath));
|
||||
final showWorkspaceSelector = kIsDesktop && (workspaceFolderPath == null);
|
||||
return Portal(
|
||||
child: MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: kLightMaterialAppTheme,
|
||||
darkTheme: kDarkMaterialAppTheme,
|
||||
themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
home: Stack(
|
||||
children: [
|
||||
!kIsLinux && !kIsMobile
|
||||
? const App()
|
||||
: context.isMediumWindow
|
||||
? const MobileDashboard()
|
||||
: const Dashboard(),
|
||||
if (kIsWindows)
|
||||
SizedBox(
|
||||
height: 29,
|
||||
child: WindowCaption(
|
||||
backgroundColor: Colors.transparent,
|
||||
brightness: isDarkMode ? Brightness.dark : Brightness.light,
|
||||
),
|
||||
home: showWorkspaceSelector
|
||||
? WorkspaceSelector(
|
||||
onContinue: (val) async {
|
||||
await openBoxes(kIsDesktop, val);
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.update(workspaceFolderPath: val);
|
||||
},
|
||||
onCancel: () async {
|
||||
try {
|
||||
await windowManager.destroy();
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
},
|
||||
)
|
||||
: Stack(
|
||||
children: [
|
||||
!kIsLinux && !kIsMobile
|
||||
? const App()
|
||||
: context.isMediumWindow
|
||||
? const MobileDashboard()
|
||||
: const Dashboard(),
|
||||
if (kIsWindows)
|
||||
SizedBox(
|
||||
height: 29,
|
||||
child: WindowCaption(
|
||||
backgroundColor: Colors.transparent,
|
||||
brightness:
|
||||
isDarkMode ? Brightness.dark : Brightness.light,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -744,6 +744,9 @@ const kLabelSaving = "Saving";
|
||||
const kLabelSaved = "Saved";
|
||||
const kLabelCode = "Code";
|
||||
const kLabelDuplicate = "Duplicate";
|
||||
const kLabelSelect = "Select";
|
||||
const kLabelContinue = "Continue";
|
||||
const kLabelCancel = "Cancel";
|
||||
// Request Pane
|
||||
const kLabelRequest = "Request";
|
||||
const kLabelHideCode = "Hide Code";
|
||||
@ -778,3 +781,5 @@ const kNullResponseModelError = "Error: Response data does not exist.";
|
||||
const kMsgNullBody = "Response body is missing (null).";
|
||||
const kMsgNoContent = "No content";
|
||||
const kMsgUnknowContentType = "Unknown Response Content-Type";
|
||||
// Workspace Selector
|
||||
const kMsgSelectWorkspace = "Create your workspace";
|
||||
|
@ -1,35 +1,82 @@
|
||||
import 'package:apidash/providers/settings_providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'models/models.dart';
|
||||
import 'providers/providers.dart';
|
||||
import 'services/services.dart';
|
||||
import 'consts.dart' show kIsLinux, kIsMacOS, kIsWindows;
|
||||
import 'consts.dart';
|
||||
import 'app.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await initApp();
|
||||
await initWindow();
|
||||
var settingsModel = await getSettingsFromSharedPrefs();
|
||||
final initStatus = await initApp(
|
||||
kIsDesktop,
|
||||
settingsModel: settingsModel,
|
||||
);
|
||||
if (kIsDesktop) {
|
||||
await initWindow(settingsModel: settingsModel);
|
||||
}
|
||||
if (!initStatus) {
|
||||
settingsModel = settingsModel?.copyWithPath(workspaceFolderPath: null);
|
||||
}
|
||||
|
||||
runApp(
|
||||
const ProviderScope(
|
||||
child: DashApp(),
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
settingsProvider.overrideWith(
|
||||
(ref) => ThemeStateNotifier(settingsModel: settingsModel),
|
||||
)
|
||||
],
|
||||
child: const DashApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> initApp() async {
|
||||
Future<bool> initApp(
|
||||
bool initializeUsingPath, {
|
||||
SettingsModel? settingsModel,
|
||||
}) async {
|
||||
GoogleFonts.config.allowRuntimeFetching = false;
|
||||
await openBoxes();
|
||||
await autoClearHistory();
|
||||
try {
|
||||
debugPrint("initializeUsingPath: $initializeUsingPath");
|
||||
debugPrint("workspaceFolderPath: ${settingsModel?.workspaceFolderPath}");
|
||||
final openBoxesStatus = await openBoxes(
|
||||
initializeUsingPath,
|
||||
settingsModel?.workspaceFolderPath,
|
||||
);
|
||||
debugPrint("openBoxesStatus: $openBoxesStatus");
|
||||
if (openBoxesStatus) {
|
||||
await autoClearHistory(settingsModel: settingsModel);
|
||||
}
|
||||
return openBoxesStatus;
|
||||
} catch (e) {
|
||||
debugPrint("initApp failed due to $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> initWindow({Size? sz}) async {
|
||||
Future<void> initWindow({
|
||||
Size? sz,
|
||||
SettingsModel? settingsModel,
|
||||
}) async {
|
||||
if (kIsLinux) {
|
||||
await setupInitialWindow(sz: sz);
|
||||
await setupInitialWindow(
|
||||
sz: sz ?? settingsModel?.size,
|
||||
);
|
||||
}
|
||||
if (kIsMacOS || kIsWindows) {
|
||||
var win = sz != null ? (sz, const Offset(100, 100)) : getInitialSize();
|
||||
await setupWindow(sz: win.$1, off: win.$2);
|
||||
if (sz != null) {
|
||||
await setupWindow(
|
||||
sz: sz,
|
||||
off: const Offset(100, 100),
|
||||
);
|
||||
} else {
|
||||
await setupWindow(
|
||||
sz: settingsModel?.size,
|
||||
off: settingsModel?.offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ class SettingsModel {
|
||||
this.promptBeforeClosing = true,
|
||||
this.activeEnvironmentId,
|
||||
this.historyRetentionPeriod = HistoryRetentionPeriod.oneWeek,
|
||||
this.workspaceFolderPath,
|
||||
});
|
||||
|
||||
final bool isDark;
|
||||
@ -26,6 +27,7 @@ class SettingsModel {
|
||||
final bool promptBeforeClosing;
|
||||
final String? activeEnvironmentId;
|
||||
final HistoryRetentionPeriod historyRetentionPeriod;
|
||||
final String? workspaceFolderPath;
|
||||
|
||||
SettingsModel copyWith({
|
||||
bool? isDark,
|
||||
@ -38,6 +40,7 @@ class SettingsModel {
|
||||
bool? promptBeforeClosing,
|
||||
String? activeEnvironmentId,
|
||||
HistoryRetentionPeriod? historyRetentionPeriod,
|
||||
String? workspaceFolderPath,
|
||||
}) {
|
||||
return SettingsModel(
|
||||
isDark: isDark ?? this.isDark,
|
||||
@ -52,6 +55,25 @@ class SettingsModel {
|
||||
activeEnvironmentId: activeEnvironmentId ?? this.activeEnvironmentId,
|
||||
historyRetentionPeriod:
|
||||
historyRetentionPeriod ?? this.historyRetentionPeriod,
|
||||
workspaceFolderPath: workspaceFolderPath ?? this.workspaceFolderPath,
|
||||
);
|
||||
}
|
||||
|
||||
SettingsModel copyWithPath({
|
||||
String? workspaceFolderPath,
|
||||
}) {
|
||||
return SettingsModel(
|
||||
isDark: isDark,
|
||||
alwaysShowCollectionPaneScrollbar: alwaysShowCollectionPaneScrollbar,
|
||||
size: size,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
defaultCodeGenLang: defaultCodeGenLang,
|
||||
offset: offset,
|
||||
saveResponses: saveResponses,
|
||||
promptBeforeClosing: promptBeforeClosing,
|
||||
activeEnvironmentId: activeEnvironmentId,
|
||||
historyRetentionPeriod: historyRetentionPeriod,
|
||||
workspaceFolderPath: workspaceFolderPath,
|
||||
);
|
||||
}
|
||||
|
||||
@ -86,8 +108,7 @@ class SettingsModel {
|
||||
final promptBeforeClosing = data["promptBeforeClosing"] as bool?;
|
||||
final activeEnvironmentId = data["activeEnvironmentId"] as String?;
|
||||
final historyRetentionPeriodStr = data["historyRetentionPeriod"] as String?;
|
||||
HistoryRetentionPeriod historyRetentionPeriod =
|
||||
HistoryRetentionPeriod.oneWeek;
|
||||
HistoryRetentionPeriod? historyRetentionPeriod;
|
||||
if (historyRetentionPeriodStr != null) {
|
||||
try {
|
||||
historyRetentionPeriod =
|
||||
@ -96,6 +117,7 @@ class SettingsModel {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
final workspaceFolderPath = data["workspaceFolderPath"] as String?;
|
||||
|
||||
const sm = SettingsModel();
|
||||
|
||||
@ -109,7 +131,9 @@ class SettingsModel {
|
||||
saveResponses: saveResponses,
|
||||
promptBeforeClosing: promptBeforeClosing,
|
||||
activeEnvironmentId: activeEnvironmentId,
|
||||
historyRetentionPeriod: historyRetentionPeriod,
|
||||
historyRetentionPeriod:
|
||||
historyRetentionPeriod ?? HistoryRetentionPeriod.oneWeek,
|
||||
workspaceFolderPath: workspaceFolderPath,
|
||||
);
|
||||
}
|
||||
|
||||
@ -127,12 +151,13 @@ class SettingsModel {
|
||||
"promptBeforeClosing": promptBeforeClosing,
|
||||
"activeEnvironmentId": activeEnvironmentId,
|
||||
"historyRetentionPeriod": historyRetentionPeriod.name,
|
||||
"workspaceFolderPath": workspaceFolderPath,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toJson().toString();
|
||||
return kJsonEncoder.convert(toJson());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -149,7 +174,8 @@ class SettingsModel {
|
||||
other.saveResponses == saveResponses &&
|
||||
other.promptBeforeClosing == promptBeforeClosing &&
|
||||
other.activeEnvironmentId == activeEnvironmentId &&
|
||||
other.historyRetentionPeriod == historyRetentionPeriod;
|
||||
other.historyRetentionPeriod == historyRetentionPeriod &&
|
||||
other.workspaceFolderPath == workspaceFolderPath;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -166,6 +192,7 @@ class SettingsModel {
|
||||
promptBeforeClosing,
|
||||
activeEnvironmentId,
|
||||
historyRetentionPeriod,
|
||||
workspaceFolderPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../models/models.dart';
|
||||
import '../services/services.dart' show hiveHandler, HiveHandler;
|
||||
import '../services/services.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
final codegenLanguageStateProvider = StateProvider<CodegenLanguage>((ref) =>
|
||||
@ -11,14 +11,13 @@ final activeEnvironmentIdStateProvider = StateProvider<String?>((ref) =>
|
||||
ref.watch(settingsProvider.select((value) => value.activeEnvironmentId)));
|
||||
|
||||
final StateNotifierProvider<ThemeStateNotifier, SettingsModel>
|
||||
settingsProvider =
|
||||
StateNotifierProvider((ref) => ThemeStateNotifier(hiveHandler));
|
||||
settingsProvider = StateNotifierProvider((ref) => ThemeStateNotifier());
|
||||
|
||||
class ThemeStateNotifier extends StateNotifier<SettingsModel> {
|
||||
ThemeStateNotifier(this.hiveHandler) : super(const SettingsModel()) {
|
||||
state = SettingsModel.fromJson(hiveHandler.settings);
|
||||
ThemeStateNotifier({this.settingsModel}) : super(const SettingsModel()) {
|
||||
state = settingsModel ?? const SettingsModel();
|
||||
}
|
||||
final HiveHandler hiveHandler;
|
||||
final SettingsModel? settingsModel;
|
||||
|
||||
Future<void> update({
|
||||
bool? isDark,
|
||||
@ -31,6 +30,7 @@ class ThemeStateNotifier extends StateNotifier<SettingsModel> {
|
||||
bool? promptBeforeClosing,
|
||||
String? activeEnvironmentId,
|
||||
HistoryRetentionPeriod? historyRetentionPeriod,
|
||||
String? workspaceFolderPath,
|
||||
}) async {
|
||||
state = state.copyWith(
|
||||
isDark: isDark,
|
||||
@ -43,7 +43,8 @@ class ThemeStateNotifier extends StateNotifier<SettingsModel> {
|
||||
promptBeforeClosing: promptBeforeClosing,
|
||||
activeEnvironmentId: activeEnvironmentId,
|
||||
historyRetentionPeriod: historyRetentionPeriod,
|
||||
workspaceFolderPath: workspaceFolderPath,
|
||||
);
|
||||
await hiveHandler.saveSettings(state.toJson());
|
||||
await setSettingsToSharedPrefs(state);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../providers/providers.dart';
|
||||
import '../services/services.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
import '../common/utils.dart';
|
||||
import '../consts.dart';
|
||||
@ -206,6 +207,7 @@ class SettingsPage extends ConsumerWidget {
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context, 'Yes');
|
||||
await clearSharedPrefs();
|
||||
await ref
|
||||
.read(collectionStateNotifierProvider
|
||||
.notifier)
|
||||
|
@ -1,18 +1,9 @@
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'hive_services.dart';
|
||||
|
||||
Future<void> autoClearHistory() async {
|
||||
final settingsMap = hiveHandler.settings;
|
||||
final retentionPeriod = settingsMap['historyRetentionPeriod'];
|
||||
|
||||
HistoryRetentionPeriod historyRetentionPeriod =
|
||||
HistoryRetentionPeriod.oneWeek;
|
||||
if (retentionPeriod != null) {
|
||||
historyRetentionPeriod =
|
||||
HistoryRetentionPeriod.values.byName(retentionPeriod);
|
||||
}
|
||||
Future<void> autoClearHistory({SettingsModel? settingsModel}) async {
|
||||
final historyRetentionPeriod = settingsModel?.historyRetentionPeriod;
|
||||
DateTime? retentionDate = getRetentionDate(historyRetentionPeriod);
|
||||
|
||||
if (retentionDate == null) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
|
||||
const String kDataBox = "apidash-data";
|
||||
@ -11,54 +10,46 @@ const String kHistoryMetaBox = "apidash-history-meta";
|
||||
const String kHistoryBoxIds = "historyIds";
|
||||
const String kHistoryLazyBox = "apidash-history-lazy";
|
||||
|
||||
const String kSettingsBox = "apidash-settings";
|
||||
Future<bool> openBoxes(
|
||||
bool initializeUsingPath,
|
||||
String? workspaceFolderPath,
|
||||
) async {
|
||||
try {
|
||||
if (initializeUsingPath) {
|
||||
if (workspaceFolderPath != null) {
|
||||
Hive.init(workspaceFolderPath);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
await Hive.initFlutter();
|
||||
}
|
||||
|
||||
Future<void> openBoxes() async {
|
||||
await Hive.initFlutter();
|
||||
await Hive.openBox(kDataBox);
|
||||
await Hive.openBox(kSettingsBox);
|
||||
await Hive.openBox(kEnvironmentBox);
|
||||
await Hive.openBox(kHistoryMetaBox);
|
||||
await Hive.openLazyBox(kHistoryLazyBox);
|
||||
}
|
||||
|
||||
(Size?, Offset?) getInitialSize() {
|
||||
Size? sz;
|
||||
Offset? off;
|
||||
var settingsBox = Hive.box(kSettingsBox);
|
||||
double? w = settingsBox.get("width") as double?;
|
||||
double? h = settingsBox.get("height") as double?;
|
||||
if (w != null && h != null) {
|
||||
sz = Size(w, h);
|
||||
await Hive.openBox(kDataBox);
|
||||
await Hive.openBox(kEnvironmentBox);
|
||||
await Hive.openBox(kHistoryMetaBox);
|
||||
await Hive.openLazyBox(kHistoryLazyBox);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
double? dx = settingsBox.get("dx") as double?;
|
||||
double? dy = settingsBox.get("dy") as double?;
|
||||
if (dx != null && dy != null) {
|
||||
off = Offset(dx, dy);
|
||||
}
|
||||
return (sz, off);
|
||||
}
|
||||
|
||||
final hiveHandler = HiveHandler();
|
||||
|
||||
class HiveHandler {
|
||||
late final Box dataBox;
|
||||
late final Box settingsBox;
|
||||
late final Box environmentBox;
|
||||
late final Box historyMetaBox;
|
||||
late final LazyBox historyLazyBox;
|
||||
|
||||
HiveHandler() {
|
||||
dataBox = Hive.box(kDataBox);
|
||||
settingsBox = Hive.box(kSettingsBox);
|
||||
environmentBox = Hive.box(kEnvironmentBox);
|
||||
historyMetaBox = Hive.box(kHistoryMetaBox);
|
||||
historyLazyBox = Hive.lazyBox(kHistoryLazyBox);
|
||||
}
|
||||
|
||||
Map get settings => settingsBox.toMap();
|
||||
Future<void> saveSettings(Map data) => settingsBox.putAll(data);
|
||||
|
||||
dynamic getIds() => dataBox.get(kKeyDataBoxIds);
|
||||
Future<void> setIds(List<String>? ids) => dataBox.put(kKeyDataBoxIds, ids);
|
||||
|
||||
|
@ -2,3 +2,4 @@ export 'http_service.dart';
|
||||
export 'hive_services.dart';
|
||||
export 'history_service.dart';
|
||||
export 'window_services.dart';
|
||||
export 'shared_preferences_services.dart';
|
||||
|
28
lib/services/shared_preferences_services.dart
Normal file
28
lib/services/shared_preferences_services.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const String kSharedPrefSettingsKey = 'apidash-settings';
|
||||
|
||||
Future<SettingsModel?> getSettingsFromSharedPrefs() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var settingsStr = prefs.getString(kSharedPrefSettingsKey);
|
||||
if (settingsStr != null) {
|
||||
var jsonSettings = kJsonDecoder.convert(settingsStr);
|
||||
var jsonMap = Map<String, Object?>.from(jsonSettings);
|
||||
var settingsModel = SettingsModel.fromJson(jsonMap);
|
||||
return settingsModel;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setSettingsToSharedPrefs(SettingsModel settingsModel) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(kSharedPrefSettingsKey, settingsModel.toString());
|
||||
}
|
||||
|
||||
Future<void> clearSharedPrefs() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(kSharedPrefSettingsKey);
|
||||
}
|
@ -114,7 +114,7 @@ List<HistoryMetaModel> getRequestGroup(
|
||||
return requestGroup;
|
||||
}
|
||||
|
||||
DateTime? getRetentionDate(HistoryRetentionPeriod retentionPeriod) {
|
||||
DateTime? getRetentionDate(HistoryRetentionPeriod? retentionPeriod) {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime today = stripTime(now);
|
||||
|
||||
|
54
lib/widgets/field_outlined.dart
Normal file
54
lib/widgets/field_outlined.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class OutlinedField extends StatelessWidget {
|
||||
const OutlinedField({
|
||||
super.key,
|
||||
this.keyId,
|
||||
this.initialValue,
|
||||
this.hintText,
|
||||
this.onChanged,
|
||||
this.colorScheme,
|
||||
});
|
||||
|
||||
final String? keyId;
|
||||
final String? initialValue;
|
||||
final String? hintText;
|
||||
final void Function(String)? onChanged;
|
||||
final ColorScheme? colorScheme;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var clrScheme = colorScheme ?? Theme.of(context).colorScheme;
|
||||
return TextFormField(
|
||||
key: keyId != null ? Key(keyId!) : null,
|
||||
initialValue: initialValue,
|
||||
style: kCodeStyle.copyWith(
|
||||
color: clrScheme.onSurface,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: kCodeStyle.copyWith(
|
||||
color: clrScheme.outline.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
hintText: hintText,
|
||||
contentPadding: kP10,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: clrScheme.primary.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: clrScheme.surfaceContainerHighest,
|
||||
),
|
||||
),
|
||||
isDense: true,
|
||||
),
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ export 'field_cell_obscurable.dart';
|
||||
export 'field_cell.dart';
|
||||
export 'field_header.dart';
|
||||
export 'field_json_search.dart';
|
||||
export 'field_outlined.dart';
|
||||
export 'field_raw.dart';
|
||||
export 'field_read_only.dart';
|
||||
export 'field_url.dart';
|
||||
@ -60,3 +61,4 @@ export 'tabs.dart';
|
||||
export 'texts.dart';
|
||||
export 'uint8_audio_player.dart';
|
||||
export 'window_caption.dart';
|
||||
export 'workspace_selector.dart';
|
||||
|
133
lib/widgets/workspace_selector.dart
Normal file
133
lib/widgets/workspace_selector.dart
Normal file
@ -0,0 +1,133 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:file_selector/file_selector.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'field_outlined.dart';
|
||||
|
||||
class WorkspaceSelector extends HookWidget {
|
||||
const WorkspaceSelector({
|
||||
super.key,
|
||||
required this.onContinue,
|
||||
this.onCancel,
|
||||
});
|
||||
|
||||
final Future<void> Function(String)? onContinue;
|
||||
final Future<void> Function()? onCancel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var selectedDirectory = useState<String?>(null);
|
||||
var workspaceName = useState<String?>(null);
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 400,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
kMsgSelectWorkspace,
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
kVSpacer20,
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"CHOOSE DIRECTORY",
|
||||
style: kCodeStyle.copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
kVSpacer5,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
),
|
||||
borderRadius: kBorderRadius6,
|
||||
),
|
||||
padding: kP4,
|
||||
child: Text(
|
||||
style: kTextStyleButtonSmall,
|
||||
selectedDirectory.value ?? "",
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
kHSpacer10,
|
||||
FilledButton.tonalIcon(
|
||||
onPressed: () async {
|
||||
selectedDirectory.value = await getDirectoryPath();
|
||||
},
|
||||
label: const Text(kLabelSelect),
|
||||
icon: const Icon(Icons.folder_rounded),
|
||||
),
|
||||
],
|
||||
),
|
||||
kVSpacer10,
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"WORKSPACE NAME [OPTIONAL]\n(FOLDER WILL BE CREATED IN THE SELECTED DIRECTORY)",
|
||||
style: kCodeStyle.copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
kVSpacer5,
|
||||
OutlinedField(
|
||||
keyId: "workspace-name",
|
||||
onChanged: (value) {
|
||||
workspaceName.value = value.trim();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
kVSpacer40,
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: selectedDirectory.value == null
|
||||
? null
|
||||
: () async {
|
||||
String finalPath = selectedDirectory.value!;
|
||||
if (workspaceName.value != null &&
|
||||
workspaceName.value!.trim().isNotEmpty) {
|
||||
finalPath =
|
||||
p.join(finalPath, workspaceName.value);
|
||||
}
|
||||
await onContinue?.call(finalPath);
|
||||
},
|
||||
child: const Text(kLabelContinue),
|
||||
),
|
||||
kHSpacer10,
|
||||
FilledButton(
|
||||
onPressed: onCancel,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? kColorDarkDanger
|
||||
: kColorLightDanger,
|
||||
surfaceTintColor: kColorRed,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimary),
|
||||
child: const Text(kLabelCancel),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
56
pubspec.lock
56
pubspec.lock
@ -1215,6 +1215,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.8"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.2"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -63,6 +63,7 @@ dependencies:
|
||||
provider: ^6.1.2
|
||||
riverpod: ^2.5.1
|
||||
scrollable_positioned_list: ^0.3.8
|
||||
shared_preferences: ^2.3.2
|
||||
url_launcher: ^6.2.5
|
||||
uuid: ^4.3.3
|
||||
vector_graphics_compiler: ^1.1.9+1
|
||||
|
@ -15,6 +15,7 @@ void main() {
|
||||
promptBeforeClosing: true,
|
||||
activeEnvironmentId: null,
|
||||
historyRetentionPeriod: HistoryRetentionPeriod.oneWeek,
|
||||
workspaceFolderPath: null,
|
||||
);
|
||||
|
||||
test('Testing toJson()', () {
|
||||
@ -31,6 +32,7 @@ void main() {
|
||||
"promptBeforeClosing": true,
|
||||
"activeEnvironmentId": null,
|
||||
"historyRetentionPeriod": "oneWeek",
|
||||
"workspaceFolderPath": null,
|
||||
};
|
||||
expect(sm.toJson(), expectedResult);
|
||||
});
|
||||
@ -49,6 +51,7 @@ void main() {
|
||||
"promptBeforeClosing": true,
|
||||
"activeEnvironmentId": null,
|
||||
"historyRetentionPeriod": "oneWeek",
|
||||
"workspaceFolderPath": null,
|
||||
};
|
||||
expect(SettingsModel.fromJson(input), sm);
|
||||
});
|
||||
@ -75,8 +78,21 @@ void main() {
|
||||
});
|
||||
|
||||
test('Testing toString()', () {
|
||||
const expectedResult =
|
||||
"{isDark: false, alwaysShowCollectionPaneScrollbar: true, width: 300.0, height: 200.0, dx: 100.0, dy: 150.0, defaultUriScheme: http, defaultCodeGenLang: curl, saveResponses: true, promptBeforeClosing: true, activeEnvironmentId: null, historyRetentionPeriod: oneWeek}";
|
||||
const expectedResult = '''{
|
||||
"isDark": false,
|
||||
"alwaysShowCollectionPaneScrollbar": true,
|
||||
"width": 300.0,
|
||||
"height": 200.0,
|
||||
"dx": 100.0,
|
||||
"dy": 150.0,
|
||||
"defaultUriScheme": "http",
|
||||
"defaultCodeGenLang": "curl",
|
||||
"saveResponses": true,
|
||||
"promptBeforeClosing": true,
|
||||
"activeEnvironmentId": null,
|
||||
"historyRetentionPeriod": "oneWeek",
|
||||
"workspaceFolderPath": null
|
||||
}''';
|
||||
expect(sm.toString(), expectedResult);
|
||||
});
|
||||
|
||||
|
@ -39,7 +39,7 @@ void main() {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
await openBoxes();
|
||||
await openBoxes(false, null);
|
||||
final flamante = rootBundle.load('google_fonts/OpenSans-Medium.ttf');
|
||||
final fontLoader = FontLoader('OpenSans')..addFont(flamante);
|
||||
await fontLoader.load();
|
||||
|
Reference in New Issue
Block a user