Workspace selector feature

This commit is contained in:
Ashita Prasad
2024-09-09 04:03:52 +05:30
parent 802d9e4607
commit 59fdbae41d
8 changed files with 273 additions and 81 deletions

View File

@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:window_manager/window_manager.dart' hide 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,
),
),
],
),
],
),
),
);
}

View File

@ -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";

View File

@ -10,11 +10,14 @@ import 'app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final settingsModel = await getSettingsFromSharedPrefs();
await initApp(settingsModel: settingsModel);
var settingsModel = await getSettingsFromSharedPrefs();
final initStatus = await initApp(settingsModel: settingsModel);
if (kIsDesktop) {
await initWindow(settingsModel: settingsModel);
}
if (!initStatus) {
settingsModel = settingsModel?.copyWithPath(workspaceFolderPath: null);
}
runApp(
ProviderScope(
@ -28,13 +31,22 @@ void main() async {
);
}
Future<void> initApp({SettingsModel? settingsModel}) async {
Future<bool> initApp({SettingsModel? settingsModel}) async {
GoogleFonts.config.allowRuntimeFetching = false;
await openBoxes(
kIsDesktop,
settingsModel?.workspaceFolderPath,
);
await autoClearHistory(settingsModel: settingsModel);
try {
final openBoxesStatus = await openBoxes(
kIsDesktop,
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({

View File

@ -59,6 +59,24 @@ class SettingsModel {
);
}
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,
);
}
factory SettingsModel.fromJson(Map<dynamic, dynamic> data) {
final isDark = data["isDark"] as bool?;
final alwaysShowCollectionPaneScrollbar =

View File

@ -10,19 +10,29 @@ const String kHistoryMetaBox = "apidash-history-meta";
const String kHistoryBoxIds = "historyIds";
const String kHistoryLazyBox = "apidash-history-lazy";
Future<void> openBoxes(
Future<bool> openBoxes(
bool isDesktop,
String? workspaceFolderPath,
) async {
if (isDesktop) {
Hive.init(workspaceFolderPath);
} else {
await Hive.initFlutter();
try {
if (isDesktop) {
if (workspaceFolderPath != null) {
Hive.init(workspaceFolderPath);
} else {
return false;
}
} else {
await Hive.initFlutter();
}
await Hive.openBox(kDataBox);
await Hive.openBox(kEnvironmentBox);
await Hive.openBox(kHistoryMetaBox);
await Hive.openLazyBox(kHistoryLazyBox);
return true;
} catch (e) {
return false;
}
await Hive.openBox(kDataBox);
await Hive.openBox(kEnvironmentBox);
await Hive.openBox(kHistoryMetaBox);
await Hive.openLazyBox(kHistoryLazyBox);
}
final hiveHandler = HiveHandler();

View 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,
);
}
}

View File

@ -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';

View File

@ -1,62 +1,133 @@
import 'package:file_selector/file_selector.dart';
import 'package:apidash/services/hive_services.dart';
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 StatefulWidget {
final Future<void> Function(String)? onSelect;
class WorkspaceSelector extends HookWidget {
const WorkspaceSelector({
super.key,
required this.onSelect,
required this.onContinue,
this.onCancel,
});
@override
WorkspaceSelectorState createState() => WorkspaceSelectorState();
}
class WorkspaceSelectorState extends State<WorkspaceSelector> {
void selectFolder() async {
String? selectedDirectory = await getDirectoryPath();
if (selectedDirectory != null) {
widget.onSelect?.call(selectedDirectory);
}
}
final Future<void> Function(String)? onContinue;
final Future<void> Function()? onCancel;
@override
Widget build(BuildContext context) {
const circularLoader = MaterialApp(
home: Scaffold(
body: Center(
child: CircularProgressIndicator(),
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),
)
],
)
],
),
),
),
);
return FutureBuilder<String?>(
future: getHiveSaveFolder(),
builder: (BuildContext context, AsyncSnapshot<String?> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return circularLoader;
}
// If there isn't hive selected folder choose it
if (snapshot.data == null) {
selectFolder();
return circularLoader;
}
// Once _hiveSaveFolder is set, display DashApp after hive init
return FutureBuilder<void>(
future: openHiveBoxes(snapshot.data!),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
// if loading show circularLoader
if (snapshot.connectionState != ConnectionState.done) {
return circularLoader;
}
// Display widget
return widget.child;
},
);
},
);
}
}