ollama-app/lib/settings/export.dart

236 lines
13 KiB
Dart

import 'dart:io';
import 'dart:convert';
import 'package:universal_html/html.dart' as html;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import '../main.dart';
import '../worker/haptic.dart';
import '../worker/desktop.dart';
import '../screen_settings.dart';
import 'package:ollama_app/l10n/gen/app_localizations.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:file_picker/file_picker.dart';
import 'package:file_selector/file_selector.dart' as file_selector;
import 'package:intl/intl.dart';
import 'package:dynamic_color/dynamic_color.dart';
class ScreenSettingsExport extends StatefulWidget {
const ScreenSettingsExport({super.key});
@override
State<ScreenSettingsExport> createState() => _ScreenSettingsExportState();
}
class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
@override
Widget build(BuildContext context) {
return WindowBorder(
color: Theme.of(context).colorScheme.surface,
child: Scaffold(
appBar: AppBar(
title: Row(children: [
Text(AppLocalizations.of(context)!.settingsTitleExport),
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
]),
actions: desktopControlsActions(context)),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 1000),
padding: const EdgeInsets.only(left: 16, right: 16),
child: Column(children: [
Expanded(
child: ListView(children: [
// const SizedBox(height: 8),
button(AppLocalizations.of(context)!.settingsExportChats,
Icons.upload_rounded, () async {
selectionHaptic();
var name =
"ollama-export-${DateFormat('yyyy-MM-dd-H-m-s').format(DateTime.now())}.json";
var content =
jsonEncode(prefs!.getStringList("chats") ?? []);
if (kIsWeb) {
// web fallback
final bytes = utf8.encode(content);
final blob = html.Blob([bytes]);
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.document.createElement("a")
as html.AnchorElement
..href = url
..style.display = "none"
..download = name;
html.document.body!.children.add(anchor);
anchor.click();
html.document.body!.children.remove(anchor);
html.Url.revokeObjectUrl(url);
} else {
String? path = "";
try {
path = (await file_selector
.getSaveLocation(acceptedTypeGroups: [
const file_selector.XTypeGroup(
label: "Ollama App File",
extensions: ["json"])
], suggestedName: name))
?.path;
} catch (_) {
path = await FilePicker.platform.saveFile(
type: FileType.custom,
allowedExtensions: ["json"],
fileName: name,
bytes: utf8.encode(jsonEncode(
prefs!.getStringList("chats") ?? [])));
}
selectionHaptic();
if (path == null) return;
if (desktopFeature()) {
File(path).writeAsString(content);
}
}
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
// ignore: use_build_context_synchronously
content: Text(AppLocalizations.of(context)!
.settingsExportChatsSuccess),
showCloseIcon: true));
}),
allowMultipleChats
? button(
AppLocalizations.of(context)!.settingsImportChats,
Icons.download_rounded, () async {
selectionHaptic();
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
AppLocalizations.of(context)!
.settingsImportChatsTitle),
content: Text(AppLocalizations.of(
context)!
.settingsImportChatsDescription),
actions: [
TextButton(
onPressed: () {
selectionHaptic();
Navigator.of(context).pop();
},
child: Text(AppLocalizations.of(
context)!
.settingsImportChatsCancel)),
TextButton(
onPressed: () async {
selectionHaptic();
String content;
try {
if (kIsWeb) {
throw Exception(
"web must use file picker");
}
file_selector.XFile? result =
await file_selector.openFile(
acceptedTypeGroups: [
const file_selector
.XTypeGroup(
label:
"Ollama App File",
extensions: [
"json"
])
]);
if (result == null) {
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
return;
}
content = await result
.readAsString();
} catch (_) {
FilePickerResult? result =
await FilePicker.platform
.pickFiles(
type: FileType
.custom,
allowedExtensions: [
"json"
]);
if (result == null) {
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
return;
}
try {
File file = File(result
.files.single.path!);
content = await file
.readAsString();
} catch (_) {
// web fallback
content = utf8.decode(result
.files
.single
.bytes as List<int>);
}
}
List<dynamic> tmpHistory =
jsonDecode(content);
List<String> history = [];
for (var i = 0;
i < tmpHistory.length;
i++) {
history.add(tmpHistory[i]);
}
prefs!.setStringList(
"chats", history);
messages = [];
chatUuid = null;
setState(() {});
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(AppLocalizations
// ignore: use_build_context_synchronously
.of(context)!
.settingsImportChatsSuccess),
showCloseIcon: true));
},
child: Text(AppLocalizations.of(
context)!
.settingsImportChatsImport))
]);
});
})
: const SizedBox.shrink()
]),
),
const SizedBox(height: 8),
button(AppLocalizations.of(context)!.settingsExportInfo,
Icons.info_rounded, null,
color: Colors.grey.harmonizeWith(
Theme.of(context).colorScheme.primary)),
button(AppLocalizations.of(context)!.settingsExportWarning,
Icons.warning_rounded, null,
color: Colors.orange
.harmonizeWith(Theme.of(context).colorScheme.primary))
])),
)),
);
}
}