Restrict desktop settings width, centralized delete dialog, allow changing titles and deleting non current chats
This commit is contained in:
parent
8813571b65
commit
efcca113c8
701
lib/main.dart
701
lib/main.dart
|
@ -424,7 +424,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
if (!chatAllowed) return;
|
if (!chatAllowed &&
|
||||||
|
chatUuid == jsonDecode(item)["uuid"]) return;
|
||||||
if (!allowSettings) return;
|
if (!allowSettings) return;
|
||||||
String oldTitle = jsonDecode(item)["title"];
|
String oldTitle = jsonDecode(item)["title"];
|
||||||
var newTitle = await prompt(context,
|
var newTitle = await prompt(context,
|
||||||
|
@ -480,6 +481,10 @@ class _MainAppState extends State<MainApp> {
|
||||||
width: 24,
|
width: 24,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
if (!chatAllowed &&
|
||||||
|
chatUuid ==
|
||||||
|
jsonDecode(item)["uuid"])
|
||||||
|
return;
|
||||||
if (!allowMultipleChats) {
|
if (!allowMultipleChats) {
|
||||||
for (var i = 0;
|
for (var i = 0;
|
||||||
i <
|
i <
|
||||||
|
@ -511,124 +516,14 @@ class _MainAppState extends State<MainApp> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!allowSettings) {
|
if (!allowSettings) {
|
||||||
if (prefs!.getBool(
|
deleteChatDialog(
|
||||||
"askBeforeDeletion") ??
|
context, setState,
|
||||||
false) {
|
additionalCondition: false,
|
||||||
showDialog(
|
uuid:
|
||||||
context: context,
|
jsonDecode(item)["uuid"],
|
||||||
builder: (context) {
|
popSidebar: true);
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context,
|
|
||||||
setLocalState) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.deleteDialogTitle),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize:
|
|
||||||
MainAxisSize
|
|
||||||
.min,
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.deleteDialogDescription),
|
|
||||||
]),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() {
|
|
||||||
Navigator.of(
|
|
||||||
context)
|
|
||||||
.pop();
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.deleteDialogCancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() {
|
|
||||||
Navigator.of(
|
|
||||||
context)
|
|
||||||
.pop();
|
|
||||||
for (var i =
|
|
||||||
0;
|
|
||||||
i < (prefs!.getStringList("chats") ?? []).length;
|
|
||||||
i++) {
|
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])[
|
|
||||||
"uuid"] ==
|
|
||||||
jsonDecode(
|
|
||||||
item)["uuid"]) {
|
|
||||||
List<String>
|
|
||||||
tmp =
|
|
||||||
prefs!.getStringList("chats")!;
|
|
||||||
tmp.removeAt(
|
|
||||||
i);
|
|
||||||
prefs!.setStringList(
|
|
||||||
"chats",
|
|
||||||
tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chatUuid ==
|
|
||||||
jsonDecode(
|
|
||||||
item)["uuid"]) {
|
|
||||||
messages =
|
|
||||||
[];
|
|
||||||
chatUuid =
|
|
||||||
null;
|
|
||||||
if (!desktopLayoutRequired(
|
|
||||||
context)) {
|
|
||||||
Navigator.of(context)
|
|
||||||
.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setState(
|
|
||||||
() {});
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.deleteDialogDelete))
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (var i = 0;
|
|
||||||
i <
|
|
||||||
(prefs!.getStringList(
|
|
||||||
"chats") ??
|
|
||||||
[])
|
|
||||||
.length;
|
|
||||||
i++) {
|
|
||||||
if (jsonDecode((prefs!
|
|
||||||
.getStringList(
|
|
||||||
"chats") ??
|
|
||||||
[])[i])["uuid"] ==
|
|
||||||
jsonDecode(
|
|
||||||
item)["uuid"]) {
|
|
||||||
List<String> tmp = prefs!
|
|
||||||
.getStringList(
|
|
||||||
"chats")!;
|
|
||||||
tmp.removeAt(i);
|
|
||||||
prefs!.setStringList(
|
|
||||||
"chats", tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chatUuid ==
|
|
||||||
jsonDecode(item)["uuid"]) {
|
|
||||||
messages = [];
|
|
||||||
chatUuid = null;
|
|
||||||
if (!desktopLayoutRequired(
|
|
||||||
context)) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!chatAllowed) return;
|
|
||||||
if (!desktopLayoutRequired(
|
if (!desktopLayoutRequired(
|
||||||
context)) {
|
context)) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -636,7 +531,22 @@ class _MainAppState extends State<MainApp> {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Padding(
|
return Container(
|
||||||
|
decoration: (Theme.of(context)
|
||||||
|
.brightness ==
|
||||||
|
Brightness.dark)
|
||||||
|
? BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors
|
||||||
|
.white),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft:
|
||||||
|
Radius.circular(
|
||||||
|
26),
|
||||||
|
topRight:
|
||||||
|
Radius.circular(
|
||||||
|
26)))
|
||||||
|
: null,
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(
|
const EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
|
@ -655,68 +565,11 @@ class _MainAppState extends State<MainApp> {
|
||||||
() {
|
() {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pop();
|
.pop();
|
||||||
if (prefs!.getBool("askBeforeDeletion") ??
|
deleteChatDialog(
|
||||||
false) {
|
context,
|
||||||
showDialog(
|
setState,
|
||||||
context: context,
|
uuid: jsonDecode(item)["uuid"],
|
||||||
builder: (context) {
|
popSidebar: true);
|
||||||
return StatefulBuilder(builder: (context, setLocalState) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(AppLocalizations.of(context)!.deleteDialogTitle),
|
|
||||||
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
|
||||||
Text(AppLocalizations.of(context)!.deleteDialogDescription),
|
|
||||||
]),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(context)!.deleteDialogCancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
for (var i = 0; i < (prefs!.getStringList("chats") ?? []).length; i++) {
|
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] == jsonDecode(item)["uuid"]) {
|
|
||||||
List<String> tmp = prefs!.getStringList("chats")!;
|
|
||||||
tmp.removeAt(i);
|
|
||||||
prefs!.setStringList("chats", tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chatUuid == jsonDecode(item)["uuid"]) {
|
|
||||||
messages = [];
|
|
||||||
chatUuid = null;
|
|
||||||
if (!desktopLayoutRequired(context)) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(context)!.deleteDialogDelete))
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (var i = 0;
|
|
||||||
i < (prefs!.getStringList("chats") ?? []).length;
|
|
||||||
i++) {
|
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] == jsonDecode(item)["uuid"]) {
|
|
||||||
List<String> tmp = prefs!.getStringList("chats")!;
|
|
||||||
tmp.removeAt(i);
|
|
||||||
prefs!.setStringList("chats", tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (chatUuid ==
|
|
||||||
jsonDecode(item)["uuid"]) {
|
|
||||||
messages = [];
|
|
||||||
chatUuid = null;
|
|
||||||
if (!desktopLayoutRequired(context)) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons
|
icon: const Icon(Icons
|
||||||
.delete_forever_rounded),
|
.delete_forever_rounded),
|
||||||
|
@ -793,48 +646,10 @@ class _MainAppState extends State<MainApp> {
|
||||||
? DismissDirection.startToEnd
|
? DismissDirection.startToEnd
|
||||||
: DismissDirection.none,
|
: DismissDirection.none,
|
||||||
confirmDismiss: (direction) async {
|
confirmDismiss: (direction) async {
|
||||||
bool returnValue = false;
|
if (!chatAllowed && chatUuid == jsonDecode(item)["uuid"])
|
||||||
if (!chatAllowed) return false;
|
return false;
|
||||||
|
return await deleteChatDialog(context, setState,
|
||||||
if (prefs!.getBool("askBeforeDeletion") ?? false) {
|
takeAction: false);
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context, setLocalState) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(AppLocalizations.of(context)!
|
|
||||||
.deleteDialogTitle),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(context)!
|
|
||||||
.deleteDialogDescription),
|
|
||||||
]),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
selectionHaptic();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
returnValue = false;
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(context)!
|
|
||||||
.deleteDialogCancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
selectionHaptic();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
returnValue = true;
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(context)!
|
|
||||||
.deleteDialogDelete))
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
returnValue = true;
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
},
|
},
|
||||||
onDismissed: (direction) {
|
onDismissed: (direction) {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
|
@ -937,15 +752,18 @@ class _MainAppState extends State<MainApp> {
|
||||||
resetSystemNavigation(context);
|
resetSystemNavigation(context);
|
||||||
|
|
||||||
Widget selector = InkWell(
|
Widget selector = InkWell(
|
||||||
onTap: () {
|
onTap: !useModel
|
||||||
if (host == null) {
|
? () {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
if (host == null) {
|
||||||
content: Text(AppLocalizations.of(context)!.noHostSelected),
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
showCloseIcon: true));
|
content:
|
||||||
return;
|
Text(AppLocalizations.of(context)!.noHostSelected),
|
||||||
}
|
showCloseIcon: true));
|
||||||
setModel(context, setState);
|
return;
|
||||||
},
|
}
|
||||||
|
setModel(context, setState);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
splashFactory: NoSplash.splashFactory,
|
splashFactory: NoSplash.splashFactory,
|
||||||
highlightColor: Colors.transparent,
|
highlightColor: Colors.transparent,
|
||||||
enableFeedback: false,
|
enableFeedback: false,
|
||||||
|
@ -1053,90 +871,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
if (!chatAllowed) return;
|
if (!chatAllowed) return;
|
||||||
|
deleteChatDialog(context, setState,
|
||||||
if (prefs!.getBool("askBeforeDeletion") ??
|
additionalCondition: messages.isNotEmpty);
|
||||||
// ignore: dead_code
|
|
||||||
false && messages.isNotEmpty) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context, setLocalState) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.deleteDialogTitle),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(context)!
|
|
||||||
.deleteDialogDescription),
|
|
||||||
]),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
selectionHaptic();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.deleteDialogCancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
selectionHaptic();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
for (var i = 0;
|
|
||||||
i <
|
|
||||||
(prefs!.getStringList(
|
|
||||||
"chats") ??
|
|
||||||
[])
|
|
||||||
.length;
|
|
||||||
i++) {
|
|
||||||
if (jsonDecode((prefs!
|
|
||||||
.getStringList(
|
|
||||||
"chats") ??
|
|
||||||
[])[i])["uuid"] ==
|
|
||||||
chatUuid) {
|
|
||||||
List<String> tmp = prefs!
|
|
||||||
.getStringList(
|
|
||||||
"chats")!;
|
|
||||||
tmp.removeAt(i);
|
|
||||||
prefs!.setStringList(
|
|
||||||
"chats", tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages = [];
|
|
||||||
chatUuid = null;
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.deleteDialogDelete))
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
for (var i = 0;
|
|
||||||
i <
|
|
||||||
(prefs!.getStringList("chats") ?? [])
|
|
||||||
.length;
|
|
||||||
i++) {
|
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ??
|
|
||||||
[])[i])["uuid"] ==
|
|
||||||
chatUuid) {
|
|
||||||
List<String> tmp =
|
|
||||||
prefs!.getStringList("chats")!;
|
|
||||||
tmp.removeAt(i);
|
|
||||||
prefs!.setStringList("chats", tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages = [];
|
|
||||||
chatUuid = null;
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.restart_alt_rounded))
|
icon: const Icon(Icons.restart_alt_rounded))
|
||||||
: const SizedBox.shrink()
|
: const SizedBox.shrink()
|
||||||
|
@ -1533,189 +1269,198 @@ class _MainAppState extends State<MainApp> {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
decoration: (Theme.of(context)
|
||||||
|
.brightness ==
|
||||||
|
Brightness.dark)
|
||||||
|
? BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
Colors.white),
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.only(
|
||||||
|
topLeft: Radius
|
||||||
|
.circular(
|
||||||
|
26),
|
||||||
|
topRight:
|
||||||
|
Radius.circular(
|
||||||
|
26)))
|
||||||
|
: null,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
top: 16),
|
top: 16),
|
||||||
child: Column(
|
child:
|
||||||
mainAxisSize:
|
Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
MainAxisSize.min,
|
(prefs?.getBool(
|
||||||
children: [
|
"voiceModeEnabled") ??
|
||||||
(prefs?.getBool(
|
false)
|
||||||
"voiceModeEnabled") ??
|
? SizedBox(
|
||||||
false)
|
width:
|
||||||
? SizedBox(
|
double.infinity,
|
||||||
width: double
|
child: OutlinedButton
|
||||||
.infinity,
|
.icon(
|
||||||
child: OutlinedButton
|
onPressed:
|
||||||
.icon(
|
() async {
|
||||||
onPressed:
|
selectionHaptic();
|
||||||
() async {
|
Navigator.of(
|
||||||
selectionHaptic();
|
context)
|
||||||
Navigator.of(context)
|
.pop();
|
||||||
.pop();
|
setMainState =
|
||||||
setMainState =
|
setState;
|
||||||
setState;
|
settingsOpen =
|
||||||
settingsOpen =
|
true;
|
||||||
true;
|
logoVisible =
|
||||||
logoVisible =
|
false;
|
||||||
false;
|
Navigator.of(
|
||||||
Navigator.of(context).push(MaterialPageRoute(
|
context)
|
||||||
|
.push(MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
const ScreenVoice()));
|
const ScreenVoice()));
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
Icons
|
|
||||||
.headphones_rounded),
|
|
||||||
label: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsTitleVoice)))
|
|
||||||
: const SizedBox
|
|
||||||
.shrink(),
|
|
||||||
(prefs?.getBool(
|
|
||||||
"voiceModeEnabled") ??
|
|
||||||
false)
|
|
||||||
? const SizedBox(
|
|
||||||
height: 8)
|
|
||||||
: const SizedBox
|
|
||||||
.shrink(),
|
|
||||||
SizedBox(
|
|
||||||
width:
|
|
||||||
double.infinity,
|
|
||||||
child: OutlinedButton
|
|
||||||
.icon(
|
|
||||||
onPressed:
|
|
||||||
() async {
|
|
||||||
selectionHaptic();
|
|
||||||
|
|
||||||
Navigator.of(
|
|
||||||
context)
|
|
||||||
.pop();
|
|
||||||
final result =
|
|
||||||
await ImagePicker()
|
|
||||||
.pickImage(
|
|
||||||
source: ImageSource
|
|
||||||
.camera,
|
|
||||||
);
|
|
||||||
if (result ==
|
|
||||||
null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final bytes =
|
|
||||||
await result
|
|
||||||
.readAsBytes();
|
|
||||||
final image =
|
|
||||||
await decodeImageFromList(
|
|
||||||
bytes);
|
|
||||||
|
|
||||||
final message =
|
|
||||||
types
|
|
||||||
.ImageMessage(
|
|
||||||
author:
|
|
||||||
user,
|
|
||||||
createdAt:
|
|
||||||
DateTime.now()
|
|
||||||
.millisecondsSinceEpoch,
|
|
||||||
height: image
|
|
||||||
.height
|
|
||||||
.toDouble(),
|
|
||||||
id: const Uuid()
|
|
||||||
.v4(),
|
|
||||||
name: result
|
|
||||||
.name,
|
|
||||||
size: bytes
|
|
||||||
.length,
|
|
||||||
uri: result
|
|
||||||
.path,
|
|
||||||
width: image
|
|
||||||
.width
|
|
||||||
.toDouble(),
|
|
||||||
);
|
|
||||||
|
|
||||||
messages.insert(
|
|
||||||
0,
|
|
||||||
message);
|
|
||||||
setState(
|
|
||||||
() {});
|
|
||||||
selectionHaptic();
|
|
||||||
},
|
},
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons
|
Icons
|
||||||
.photo_camera_rounded),
|
.headphones_rounded),
|
||||||
label: Text(AppLocalizations.of(
|
label: Text(AppLocalizations.of(
|
||||||
context)!
|
context)!
|
||||||
|
.settingsTitleVoice)))
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
(prefs?.getBool(
|
||||||
|
"voiceModeEnabled") ??
|
||||||
|
false)
|
||||||
|
? const SizedBox(
|
||||||
|
height: 8)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child:
|
||||||
|
OutlinedButton.icon(
|
||||||
|
onPressed:
|
||||||
|
() async {
|
||||||
|
selectionHaptic();
|
||||||
|
|
||||||
|
Navigator.of(
|
||||||
|
context)
|
||||||
|
.pop();
|
||||||
|
final result =
|
||||||
|
await ImagePicker()
|
||||||
|
.pickImage(
|
||||||
|
source:
|
||||||
|
ImageSource
|
||||||
|
.camera,
|
||||||
|
);
|
||||||
|
if (result ==
|
||||||
|
null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final bytes =
|
||||||
|
await result
|
||||||
|
.readAsBytes();
|
||||||
|
final image =
|
||||||
|
await decodeImageFromList(
|
||||||
|
bytes);
|
||||||
|
|
||||||
|
final message =
|
||||||
|
types
|
||||||
|
.ImageMessage(
|
||||||
|
author: user,
|
||||||
|
createdAt: DateTime
|
||||||
|
.now()
|
||||||
|
.millisecondsSinceEpoch,
|
||||||
|
height: image
|
||||||
|
.height
|
||||||
|
.toDouble(),
|
||||||
|
id: const Uuid()
|
||||||
|
.v4(),
|
||||||
|
name: result
|
||||||
|
.name,
|
||||||
|
size: bytes
|
||||||
|
.length,
|
||||||
|
uri: result
|
||||||
|
.path,
|
||||||
|
width: image
|
||||||
|
.width
|
||||||
|
.toDouble(),
|
||||||
|
);
|
||||||
|
|
||||||
|
messages.insert(
|
||||||
|
0, message);
|
||||||
|
setState(() {});
|
||||||
|
selectionHaptic();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons
|
||||||
|
.photo_camera_rounded),
|
||||||
|
label: Text(
|
||||||
|
AppLocalizations.of(
|
||||||
|
context)!
|
||||||
.takeImage))),
|
.takeImage))),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width:
|
width: double.infinity,
|
||||||
double.infinity,
|
child:
|
||||||
child: OutlinedButton
|
OutlinedButton.icon(
|
||||||
.icon(
|
onPressed:
|
||||||
onPressed:
|
() async {
|
||||||
() async {
|
selectionHaptic();
|
||||||
selectionHaptic();
|
|
||||||
|
|
||||||
Navigator.of(
|
Navigator.of(
|
||||||
context)
|
context)
|
||||||
.pop();
|
.pop();
|
||||||
final result =
|
final result =
|
||||||
await ImagePicker()
|
await ImagePicker()
|
||||||
.pickImage(
|
.pickImage(
|
||||||
source: ImageSource
|
source:
|
||||||
|
ImageSource
|
||||||
.gallery,
|
.gallery,
|
||||||
);
|
);
|
||||||
if (result ==
|
if (result ==
|
||||||
null) {
|
null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final bytes =
|
final bytes =
|
||||||
await result
|
await result
|
||||||
.readAsBytes();
|
.readAsBytes();
|
||||||
final image =
|
final image =
|
||||||
await decodeImageFromList(
|
await decodeImageFromList(
|
||||||
bytes);
|
bytes);
|
||||||
|
|
||||||
final message =
|
final message =
|
||||||
types
|
types
|
||||||
.ImageMessage(
|
.ImageMessage(
|
||||||
author:
|
author: user,
|
||||||
user,
|
createdAt: DateTime
|
||||||
createdAt:
|
.now()
|
||||||
DateTime.now()
|
.millisecondsSinceEpoch,
|
||||||
.millisecondsSinceEpoch,
|
height: image
|
||||||
height: image
|
.height
|
||||||
.height
|
.toDouble(),
|
||||||
.toDouble(),
|
id: const Uuid()
|
||||||
id: const Uuid()
|
.v4(),
|
||||||
.v4(),
|
name: result
|
||||||
name: result
|
.name,
|
||||||
.name,
|
size: bytes
|
||||||
size: bytes
|
.length,
|
||||||
.length,
|
uri: result
|
||||||
uri: result
|
.path,
|
||||||
.path,
|
width: image
|
||||||
width: image
|
.width
|
||||||
.width
|
.toDouble(),
|
||||||
.toDouble(),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
messages.insert(
|
messages.insert(
|
||||||
0,
|
0, message);
|
||||||
message);
|
setState(() {});
|
||||||
setState(
|
selectionHaptic();
|
||||||
() {});
|
},
|
||||||
selectionHaptic();
|
icon: const Icon(Icons
|
||||||
},
|
.image_rounded),
|
||||||
icon: const Icon(
|
label: Text(
|
||||||
Icons
|
AppLocalizations.of(
|
||||||
.image_rounded),
|
|
||||||
label: Text(AppLocalizations.of(
|
|
||||||
context)!
|
context)!
|
||||||
.uploadImage)))
|
.uploadImage)))
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
l10n: ChatL10nEn(
|
l10n: ChatL10nEn(
|
||||||
|
|
|
@ -125,14 +125,15 @@ Widget title(String text, {double top = 16, double bottom = 16}) {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||||
child: Text(text)),
|
child: Text(text)),
|
||||||
const Expanded(child: Divider())
|
const Expanded(child: Divider(height: 1))
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget titleDivider({double? top, double? bottom, BuildContext? context}) {
|
Widget titleDivider({double? top, double? bottom, BuildContext? context}) {
|
||||||
top ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
top ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
||||||
bottom ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
bottom ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
||||||
return Padding(
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
padding: EdgeInsets.only(left: 8, right: 8, top: top, bottom: bottom),
|
padding: EdgeInsets.only(left: 8, right: 8, top: top, bottom: bottom),
|
||||||
child: const Row(
|
child: const Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
@ -143,13 +144,12 @@ Widget verticalTitleDivider(
|
||||||
{double? left, double? right, BuildContext? context}) {
|
{double? left, double? right, BuildContext? context}) {
|
||||||
left ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
left ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
||||||
right ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
right ??= (context != null && desktopLayoutNotRequired(context)) ? 32 : 16;
|
||||||
return Padding(
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
padding: EdgeInsets.only(left: left, right: right, top: 8, bottom: 8),
|
padding: EdgeInsets.only(left: left, right: right, top: 8, bottom: 8),
|
||||||
child: const Row(mainAxisSize: MainAxisSize.max, children: [
|
child: const Row(
|
||||||
// Expanded(child:
|
mainAxisSize: MainAxisSize.max,
|
||||||
VerticalDivider()
|
children: [VerticalDivider(width: 1)]));
|
||||||
// ),
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget button(String text, IconData? icon, void Function()? onPressed,
|
Widget button(String text, IconData? icon, void Function()? onPressed,
|
||||||
|
@ -359,6 +359,7 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
||||||
TextField(
|
TextField(
|
||||||
controller: hostInputController,
|
controller: hostInputController,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
|
autofillHints: const [AutofillHints.url],
|
||||||
readOnly: useHost,
|
readOnly: useHost,
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
|
|
|
@ -42,123 +42,130 @@ class _ScreenSettingsAboutState extends State<ScreenSettingsAbout> {
|
||||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||||
]),
|
]),
|
||||||
actions: desktopControlsActions(context)),
|
actions: desktopControlsActions(context)),
|
||||||
body: Padding(
|
body: Center(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
child: Container(
|
||||||
child: Column(children: [
|
constraints: const BoxConstraints(maxWidth: 1000),
|
||||||
Expanded(
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child: ListView(children: [
|
child: Column(children: [
|
||||||
// const SizedBox(height: 8),
|
Expanded(
|
||||||
button(
|
child: ListView(children: [
|
||||||
AppLocalizations.of(context)!
|
// const SizedBox(height: 8),
|
||||||
.settingsVersion(currentVersion ?? ""),
|
button(
|
||||||
Icons.verified_rounded,
|
AppLocalizations.of(context)!
|
||||||
null),
|
.settingsVersion(currentVersion ?? ""),
|
||||||
(updateStatus == "notAvailable")
|
Icons.verified_rounded,
|
||||||
? const SizedBox.shrink()
|
null),
|
||||||
: button(
|
(updateStatus == "notAvailable")
|
||||||
(!updateChecked
|
? const SizedBox.shrink()
|
||||||
? AppLocalizations.of(context)!
|
: button(
|
||||||
.settingsUpdateCheck
|
(!updateChecked
|
||||||
: updateLoading
|
? AppLocalizations.of(context)!
|
||||||
? AppLocalizations.of(context)!
|
.settingsUpdateCheck
|
||||||
.settingsUpdateChecking
|
: updateLoading
|
||||||
: (updateStatus == "rateLimit")
|
? AppLocalizations.of(context)!
|
||||||
? AppLocalizations.of(context)!
|
.settingsUpdateChecking
|
||||||
.settingsUpdateRateLimit
|
: (updateStatus == "rateLimit")
|
||||||
: (updateStatus != "ok")
|
? AppLocalizations.of(context)!
|
||||||
? AppLocalizations.of(context)!
|
.settingsUpdateRateLimit
|
||||||
.settingsUpdateIssue
|
: (updateStatus != "ok")
|
||||||
: (Version.parse(latestVersion ??
|
? AppLocalizations.of(context)!
|
||||||
"1.0.0") >
|
.settingsUpdateIssue
|
||||||
Version.parse(
|
: (Version.parse(latestVersion ??
|
||||||
currentVersion ??
|
"1.0.0") >
|
||||||
"2.0.0"))
|
Version.parse(
|
||||||
? AppLocalizations.of(context)!
|
currentVersion ??
|
||||||
.settingsUpdateAvailable(
|
"2.0.0"))
|
||||||
latestVersion!)
|
? AppLocalizations.of(
|
||||||
: AppLocalizations.of(context)!
|
context)!
|
||||||
.settingsUpdateLatest),
|
.settingsUpdateAvailable(
|
||||||
((updateStatus != "ok")
|
latestVersion!)
|
||||||
? Icons.warning_rounded
|
: AppLocalizations.of(
|
||||||
: (Version.parse(latestVersion ?? "1.0.0") >
|
context)!
|
||||||
Version.parse(
|
.settingsUpdateLatest),
|
||||||
currentVersion ?? "2.0.0"))
|
((updateStatus != "ok")
|
||||||
? Icons.info_outline_rounded
|
? Icons.warning_rounded
|
||||||
: Icons.update_rounded), () {
|
: (Version.parse(latestVersion ?? "1.0.0") >
|
||||||
if (updateLoading) return;
|
Version.parse(
|
||||||
selectionHaptic();
|
currentVersion ?? "2.0.0"))
|
||||||
if ((Version.parse(latestVersion ?? "1.0.0") >
|
? Icons.info_outline_rounded
|
||||||
Version.parse(currentVersion ?? "2.0.0")) &&
|
: Icons.update_rounded), () {
|
||||||
(updateStatus == "ok")) {
|
if (updateLoading) return;
|
||||||
updateDialog(context, title);
|
selectionHaptic();
|
||||||
} else {
|
if ((Version.parse(latestVersion ?? "1.0.0") >
|
||||||
checkUpdate(setState);
|
Version.parse(
|
||||||
return;
|
currentVersion ?? "2.0.0")) &&
|
||||||
}
|
(updateStatus == "ok")) {
|
||||||
}),
|
updateDialog(context, title);
|
||||||
(updateStatus == "notAvailable")
|
} else {
|
||||||
? const SizedBox.shrink()
|
checkUpdate(setState);
|
||||||
: toggle(
|
return;
|
||||||
context,
|
}
|
||||||
AppLocalizations.of(context)!
|
}),
|
||||||
.settingsCheckForUpdates,
|
(updateStatus == "notAvailable")
|
||||||
(prefs!.getBool("checkUpdateOnSettingsOpen") ??
|
? const SizedBox.shrink()
|
||||||
false), (value) {
|
: toggle(
|
||||||
selectionHaptic();
|
context,
|
||||||
prefs!.setBool("checkUpdateOnSettingsOpen", value);
|
AppLocalizations.of(context)!
|
||||||
setState(() {});
|
.settingsCheckForUpdates,
|
||||||
}),
|
(prefs!.getBool("checkUpdateOnSettingsOpen") ??
|
||||||
titleDivider(context: context),
|
false), (value) {
|
||||||
button(AppLocalizations.of(context)!.settingsGithub,
|
selectionHaptic();
|
||||||
SimpleIcons.github, () {
|
prefs!
|
||||||
selectionHaptic();
|
.setBool("checkUpdateOnSettingsOpen", value);
|
||||||
launchUrl(
|
setState(() {});
|
||||||
mode: LaunchMode.inAppBrowserView,
|
}),
|
||||||
Uri.parse(repoUrl));
|
titleDivider(context: context),
|
||||||
}),
|
button(AppLocalizations.of(context)!.settingsGithub,
|
||||||
button(AppLocalizations.of(context)!.settingsReportIssue,
|
SimpleIcons.github, () {
|
||||||
Icons.report_rounded, () {
|
selectionHaptic();
|
||||||
selectionHaptic();
|
launchUrl(
|
||||||
launchUrl(
|
mode: LaunchMode.inAppBrowserView,
|
||||||
mode: LaunchMode.inAppBrowserView,
|
Uri.parse(repoUrl));
|
||||||
Uri.parse("$repoUrl/issues"));
|
}),
|
||||||
}),
|
button(AppLocalizations.of(context)!.settingsReportIssue,
|
||||||
button(AppLocalizations.of(context)!.settingsLicenses,
|
Icons.report_rounded, () {
|
||||||
Icons.gavel_rounded, () {
|
selectionHaptic();
|
||||||
selectionHaptic();
|
launchUrl(
|
||||||
String legal = "Copyright 2024 JHubi1";
|
mode: LaunchMode.inAppBrowserView,
|
||||||
Widget icon = const Padding(
|
Uri.parse("$repoUrl/issues"));
|
||||||
padding: EdgeInsets.all(16),
|
}),
|
||||||
child: ImageIcon(AssetImage("assets/logo512.png"),
|
button(AppLocalizations.of(context)!.settingsLicenses,
|
||||||
size: 48),
|
Icons.gavel_rounded, () {
|
||||||
);
|
selectionHaptic();
|
||||||
if (desktopFeature()) {
|
String legal = "Copyright 2024 JHubi1";
|
||||||
showDialog(
|
Widget icon = const Padding(
|
||||||
context: context,
|
padding: EdgeInsets.all(16),
|
||||||
builder: (context) {
|
child: ImageIcon(AssetImage("assets/logo512.png"),
|
||||||
return Dialog(
|
size: 48),
|
||||||
child: ClipRRect(
|
);
|
||||||
borderRadius: BorderRadius.circular(28),
|
if (desktopFeature()) {
|
||||||
child: LicensePage(
|
showDialog(
|
||||||
applicationName: "Ollama App",
|
context: context,
|
||||||
applicationVersion: currentVersion,
|
builder: (context) {
|
||||||
applicationIcon: icon,
|
return Dialog(
|
||||||
applicationLegalese: legal),
|
child: ClipRRect(
|
||||||
));
|
borderRadius: BorderRadius.circular(28),
|
||||||
});
|
child: LicensePage(
|
||||||
} else {
|
applicationName: "Ollama App",
|
||||||
showLicensePage(
|
applicationVersion: currentVersion,
|
||||||
context: context,
|
applicationIcon: icon,
|
||||||
applicationName: "Ollama App",
|
applicationLegalese: legal),
|
||||||
applicationVersion: currentVersion,
|
));
|
||||||
applicationIcon: icon,
|
});
|
||||||
applicationLegalese: legal);
|
} else {
|
||||||
}
|
showLicensePage(
|
||||||
}),
|
context: context,
|
||||||
const SizedBox(height: 16)
|
applicationName: "Ollama App",
|
||||||
]),
|
applicationVersion: currentVersion,
|
||||||
)
|
applicationIcon: icon,
|
||||||
]))),
|
applicationLegalese: legal);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 16)
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
])),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,71 +37,75 @@ class _ScreenSettingsBehaviorState extends State<ScreenSettingsBehavior> {
|
||||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||||
]),
|
]),
|
||||||
actions: desktopControlsActions(context)),
|
actions: desktopControlsActions(context)),
|
||||||
body: Padding(
|
body: Center(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
child: Container(
|
||||||
child: Column(children: [
|
constraints: const BoxConstraints(maxWidth: 1000),
|
||||||
Expanded(
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child: ListView(children: [
|
child: Column(children: [
|
||||||
const SizedBox(height: 8),
|
Expanded(
|
||||||
TextField(
|
child: ListView(children: [
|
||||||
controller: systemInputController,
|
const SizedBox(height: 8),
|
||||||
keyboardType: TextInputType.multiline,
|
TextField(
|
||||||
maxLines: desktopLayoutNotRequired(context) ? 5 : 2,
|
controller: systemInputController,
|
||||||
decoration: InputDecoration(
|
keyboardType: TextInputType.multiline,
|
||||||
labelText: AppLocalizations.of(context)!
|
maxLines: desktopLayoutNotRequired(context) ? 5 : 2,
|
||||||
.settingsSystemMessage,
|
decoration: InputDecoration(
|
||||||
hintText: "You are a helpful assistant",
|
labelText: AppLocalizations.of(context)!
|
||||||
suffixIcon: IconButton(
|
.settingsSystemMessage,
|
||||||
tooltip:
|
alignLabelWithHint: true,
|
||||||
AppLocalizations.of(context)!.tooltipSave,
|
hintText: "You are a helpful assistant",
|
||||||
onPressed: () {
|
suffixIcon: IconButton(
|
||||||
selectionHaptic();
|
tooltip:
|
||||||
prefs?.setString(
|
AppLocalizations.of(context)!.tooltipSave,
|
||||||
"system",
|
onPressed: () {
|
||||||
(systemInputController.text.isNotEmpty)
|
selectionHaptic();
|
||||||
? systemInputController.text
|
prefs?.setString(
|
||||||
: "You are a helpful assistant");
|
"system",
|
||||||
},
|
(systemInputController.text.isNotEmpty)
|
||||||
icon: const Icon(Icons.save_rounded),
|
? systemInputController.text
|
||||||
),
|
: "You are a helpful assistant");
|
||||||
border: const OutlineInputBorder())),
|
},
|
||||||
const SizedBox(height: 16),
|
icon: const Icon(Icons.save_rounded),
|
||||||
toggle(
|
),
|
||||||
context,
|
border: const OutlineInputBorder())),
|
||||||
AppLocalizations.of(context)!.settingsUseSystem,
|
const SizedBox(height: 16),
|
||||||
(prefs!.getBool("useSystem") ?? true),
|
toggle(
|
||||||
(value) {
|
context,
|
||||||
selectionHaptic();
|
AppLocalizations.of(context)!.settingsUseSystem,
|
||||||
prefs!.setBool("useSystem", value);
|
(prefs!.getBool("useSystem") ?? true),
|
||||||
setState(() {});
|
(value) {
|
||||||
},
|
selectionHaptic();
|
||||||
icon: const Icon(Icons.info_outline_rounded),
|
prefs!.setBool("useSystem", value);
|
||||||
onLongTap: () {
|
setState(() {});
|
||||||
selectionHaptic();
|
},
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
icon: const Icon(Icons.info_outline_rounded),
|
||||||
content: Text(AppLocalizations.of(context)!
|
onLongTap: () {
|
||||||
.settingsUseSystemDescription),
|
selectionHaptic();
|
||||||
showCloseIcon: true));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
}),
|
content: Text(AppLocalizations.of(context)!
|
||||||
toggle(
|
.settingsUseSystemDescription),
|
||||||
context,
|
showCloseIcon: true));
|
||||||
AppLocalizations.of(context)!.settingsDisableMarkdown,
|
}),
|
||||||
(prefs!.getBool("noMarkdown") ?? false), (value) {
|
toggle(
|
||||||
selectionHaptic();
|
context,
|
||||||
prefs!.setBool("noMarkdown", value);
|
AppLocalizations.of(context)!.settingsDisableMarkdown,
|
||||||
setState(() {});
|
(prefs!.getBool("noMarkdown") ?? false), (value) {
|
||||||
})
|
selectionHaptic();
|
||||||
]),
|
prefs!.setBool("noMarkdown", value);
|
||||||
),
|
setState(() {});
|
||||||
const SizedBox(height: 8),
|
})
|
||||||
button(
|
]),
|
||||||
AppLocalizations.of(context)!
|
),
|
||||||
.settingsBehaviorNotUpdatedForOlderChats,
|
const SizedBox(height: 8),
|
||||||
Icons.info_rounded,
|
button(
|
||||||
null,
|
AppLocalizations.of(context)!
|
||||||
color: Colors.grey
|
.settingsBehaviorNotUpdatedForOlderChats,
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.primary))
|
Icons.info_rounded,
|
||||||
]))),
|
null,
|
||||||
|
color: Colors.grey
|
||||||
|
.harmonizeWith(Theme.of(context).colorScheme.primary))
|
||||||
|
])),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,192 +36,204 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||||
]),
|
]),
|
||||||
actions: desktopControlsActions(context)),
|
actions: desktopControlsActions(context)),
|
||||||
body: Padding(
|
body: Center(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
child: Container(
|
||||||
child: Column(children: [
|
constraints: const BoxConstraints(maxWidth: 1000),
|
||||||
Expanded(
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child: ListView(children: [
|
child: Column(children: [
|
||||||
// const SizedBox(height: 8),
|
Expanded(
|
||||||
button(AppLocalizations.of(context)!.settingsExportChats,
|
child: ListView(children: [
|
||||||
Icons.upload_rounded, () async {
|
// const SizedBox(height: 8),
|
||||||
selectionHaptic();
|
button(AppLocalizations.of(context)!.settingsExportChats,
|
||||||
var name =
|
Icons.upload_rounded, () async {
|
||||||
"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();
|
selectionHaptic();
|
||||||
if (path == null) return;
|
var name =
|
||||||
if (desktopFeature()) {
|
"ollama-export-${DateFormat('yyyy-MM-dd-H-m-s').format(DateTime.now())}.json";
|
||||||
File(path).writeAsString(content);
|
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
|
||||||
// ignore: use_build_context_synchronously
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
// ignore: use_build_context_synchronously
|
||||||
// ignore: use_build_context_synchronously
|
content: Text(AppLocalizations.of(context)!
|
||||||
content: Text(AppLocalizations.of(context)!
|
.settingsExportChatsSuccess),
|
||||||
.settingsExportChatsSuccess),
|
showCloseIcon: true));
|
||||||
showCloseIcon: true));
|
}),
|
||||||
}),
|
allowMultipleChats
|
||||||
allowMultipleChats
|
? button(
|
||||||
? button(
|
AppLocalizations.of(context)!.settingsImportChats,
|
||||||
AppLocalizations.of(context)!.settingsImportChats,
|
Icons.download_rounded, () {
|
||||||
Icons.download_rounded, () {
|
selectionHaptic();
|
||||||
selectionHaptic();
|
showDialog(
|
||||||
showDialog(
|
context: context,
|
||||||
context: context,
|
builder: (context) {
|
||||||
builder: (context) {
|
return AlertDialog(
|
||||||
return AlertDialog(
|
surfaceTintColor:
|
||||||
title: Text(AppLocalizations.of(context)!
|
(Theme.of(context).brightness ==
|
||||||
.settingsImportChatsTitle),
|
Brightness.dark)
|
||||||
content: Text(
|
? Colors.grey[800]
|
||||||
AppLocalizations.of(context)!
|
: null,
|
||||||
.settingsImportChatsDescription),
|
title: Text(
|
||||||
actions: [
|
AppLocalizations.of(context)!
|
||||||
TextButton(
|
.settingsImportChatsTitle),
|
||||||
onPressed: () {
|
content: Text(AppLocalizations.of(
|
||||||
selectionHaptic();
|
context)!
|
||||||
Navigator.of(context).pop();
|
.settingsImportChatsDescription),
|
||||||
},
|
actions: [
|
||||||
child: Text(AppLocalizations.of(
|
TextButton(
|
||||||
context)!
|
onPressed: () {
|
||||||
.settingsImportChatsCancel)),
|
selectionHaptic();
|
||||||
TextButton(
|
Navigator.of(context).pop();
|
||||||
onPressed: () async {
|
},
|
||||||
selectionHaptic();
|
child: Text(AppLocalizations.of(
|
||||||
String content;
|
context)!
|
||||||
try {
|
.settingsImportChatsCancel)),
|
||||||
if (kIsWeb) {
|
TextButton(
|
||||||
throw Exception(
|
onPressed: () async {
|
||||||
"web must use file picker");
|
selectionHaptic();
|
||||||
}
|
String content;
|
||||||
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 {
|
try {
|
||||||
File file = File(result
|
if (kIsWeb) {
|
||||||
.files.single.path!);
|
throw Exception(
|
||||||
content =
|
"web must use file picker");
|
||||||
await file.readAsString();
|
}
|
||||||
|
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 (_) {
|
} catch (_) {
|
||||||
// web fallback
|
FilePickerResult? result =
|
||||||
content = utf8.decode(result
|
await FilePicker.platform
|
||||||
.files
|
.pickFiles(
|
||||||
.single
|
type: FileType
|
||||||
.bytes as List<int>);
|
.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 =
|
||||||
List<dynamic> tmpHistory =
|
jsonDecode(content);
|
||||||
jsonDecode(content);
|
List<String> history = [];
|
||||||
List<String> history = [];
|
|
||||||
|
|
||||||
for (var i = 0;
|
for (var i = 0;
|
||||||
i < tmpHistory.length;
|
i < tmpHistory.length;
|
||||||
i++) {
|
i++) {
|
||||||
history.add(tmpHistory[i]);
|
history.add(tmpHistory[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs!.setStringList(
|
prefs!.setStringList(
|
||||||
"chats", history);
|
"chats", history);
|
||||||
|
|
||||||
messages = [];
|
messages = [];
|
||||||
chatUuid = null;
|
chatUuid = null;
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
ScaffoldMessenger.of(context)
|
ScaffoldMessenger.of(context)
|
||||||
.showSnackBar(SnackBar(
|
.showSnackBar(SnackBar(
|
||||||
content: Text(AppLocalizations
|
content: Text(AppLocalizations
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
.of(context)!
|
.of(context)!
|
||||||
.settingsImportChatsSuccess),
|
.settingsImportChatsSuccess),
|
||||||
showCloseIcon: true));
|
showCloseIcon: true));
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(AppLocalizations.of(
|
||||||
AppLocalizations.of(context)!
|
context)!
|
||||||
.settingsImportChatsImport))
|
.settingsImportChatsImport))
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
: const SizedBox.shrink()
|
: const SizedBox.shrink()
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
button(AppLocalizations.of(context)!.settingsExportInfo,
|
button(AppLocalizations.of(context)!.settingsExportInfo,
|
||||||
Icons.info_rounded, null,
|
Icons.info_rounded, null,
|
||||||
color: Colors.grey
|
color: Colors.grey.harmonizeWith(
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.primary)),
|
Theme.of(context).colorScheme.primary)),
|
||||||
button(AppLocalizations.of(context)!.settingsExportWarning,
|
button(AppLocalizations.of(context)!.settingsExportWarning,
|
||||||
Icons.warning_rounded, null,
|
Icons.warning_rounded, null,
|
||||||
color: Colors.orange
|
color: Colors.orange
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.primary))
|
.harmonizeWith(Theme.of(context).colorScheme.primary))
|
||||||
]))),
|
])),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,460 +36,487 @@ class _ScreenSettingsInterfaceState extends State<ScreenSettingsInterface> {
|
||||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||||
]),
|
]),
|
||||||
actions: desktopControlsActions(context)),
|
actions: desktopControlsActions(context)),
|
||||||
body: Padding(
|
body: Center(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
child: Container(
|
||||||
child: Column(children: [
|
constraints: const BoxConstraints(maxWidth: 1000),
|
||||||
Expanded(
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child: ListView(children: [
|
child: Column(children: [
|
||||||
// const SizedBox(height: 8),
|
Expanded(
|
||||||
toggle(
|
child: ListView(children: [
|
||||||
context,
|
// const SizedBox(height: 8),
|
||||||
AppLocalizations.of(context)!.settingsShowModelTags,
|
toggle(
|
||||||
(prefs!.getBool("modelTags") ?? false), (value) {
|
context,
|
||||||
selectionHaptic();
|
AppLocalizations.of(context)!.settingsShowModelTags,
|
||||||
prefs!.setBool("modelTags", value);
|
(prefs!.getBool("modelTags") ?? false), (value) {
|
||||||
setState(() {});
|
selectionHaptic();
|
||||||
}),
|
prefs!.setBool("modelTags", value);
|
||||||
toggle(
|
setState(() {});
|
||||||
context,
|
}),
|
||||||
AppLocalizations.of(context)!.settingsPreloadModels,
|
toggle(
|
||||||
(prefs!.getBool("preloadModel") ?? true), (value) {
|
context,
|
||||||
selectionHaptic();
|
AppLocalizations.of(context)!.settingsPreloadModels,
|
||||||
prefs!.setBool("preloadModel", value);
|
(prefs!.getBool("preloadModel") ?? true), (value) {
|
||||||
setState(() {});
|
selectionHaptic();
|
||||||
}),
|
prefs!.setBool("preloadModel", value);
|
||||||
toggle(
|
setState(() {});
|
||||||
context,
|
}),
|
||||||
AppLocalizations.of(context)!
|
toggle(
|
||||||
.settingsResetOnModelChange,
|
context,
|
||||||
(prefs!.getBool("resetOnModelSelect") ?? true),
|
AppLocalizations.of(context)!
|
||||||
(value) {
|
.settingsResetOnModelChange,
|
||||||
selectionHaptic();
|
(prefs!.getBool("resetOnModelSelect") ?? true),
|
||||||
prefs!.setBool("resetOnModelSelect", value);
|
(value) {
|
||||||
setState(() {});
|
selectionHaptic();
|
||||||
}),
|
prefs!.setBool("resetOnModelSelect", value);
|
||||||
titleDivider(
|
setState(() {});
|
||||||
bottom: desktopLayoutNotRequired(context) ? 38 : 20,
|
}),
|
||||||
context: context),
|
titleDivider(
|
||||||
SegmentedButton(
|
bottom: desktopLayoutNotRequired(context) ? 38 : 20,
|
||||||
segments: [
|
context: context),
|
||||||
ButtonSegment(
|
SegmentedButton(
|
||||||
value: "stream",
|
segments: [
|
||||||
label: Text(AppLocalizations.of(context)!
|
ButtonSegment(
|
||||||
.settingsRequestTypeStream),
|
value: "stream",
|
||||||
icon: const Icon(Icons.stream_rounded)),
|
label: Text(AppLocalizations.of(context)!
|
||||||
ButtonSegment(
|
.settingsRequestTypeStream),
|
||||||
value: "request",
|
icon: const Icon(Icons.stream_rounded)),
|
||||||
label: Text(AppLocalizations.of(context)!
|
ButtonSegment(
|
||||||
.settingsRequestTypeRequest),
|
value: "request",
|
||||||
icon: const Icon(Icons.send_rounded))
|
label: Text(AppLocalizations.of(context)!
|
||||||
],
|
.settingsRequestTypeRequest),
|
||||||
selected: {
|
icon: const Icon(Icons.send_rounded))
|
||||||
prefs!.getString("requestType") ?? "stream"
|
],
|
||||||
},
|
selected: {
|
||||||
onSelectionChanged: (p0) {
|
prefs!.getString("requestType") ?? "stream"
|
||||||
selectionHaptic();
|
},
|
||||||
setState(() {
|
onSelectionChanged: (p0) {
|
||||||
prefs!.setString("requestType", p0.elementAt(0));
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!.settingsGenerateTitles,
|
|
||||||
(prefs!.getBool("generateTitles") ?? true), (value) {
|
|
||||||
selectionHaptic();
|
|
||||||
prefs!.setBool("generateTitles", value);
|
|
||||||
setState(() {});
|
|
||||||
}),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!.settingsEnableEditing,
|
|
||||||
(prefs!.getBool("enableEditing") ?? true), (value) {
|
|
||||||
selectionHaptic();
|
|
||||||
prefs!.setBool("enableEditing", value);
|
|
||||||
setState(() {});
|
|
||||||
}),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!.settingsAskBeforeDelete,
|
|
||||||
(prefs!.getBool("askBeforeDeletion") ?? false),
|
|
||||||
(value) {
|
|
||||||
selectionHaptic();
|
|
||||||
prefs!.setBool("askBeforeDeletion", value);
|
|
||||||
setState(() {});
|
|
||||||
}),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!.settingsShowTips,
|
|
||||||
(prefs!.getBool("tips") ?? true), (value) {
|
|
||||||
selectionHaptic();
|
|
||||||
prefs!.setBool("tips", value);
|
|
||||||
setState(() {});
|
|
||||||
}),
|
|
||||||
titleDivider(context: context),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsKeepModelLoadedAlways,
|
|
||||||
int.parse(prefs!.getString("keepAlive") ?? "300") == -1,
|
|
||||||
(value) {
|
|
||||||
selectionHaptic();
|
|
||||||
setState(() {
|
|
||||||
if (value) {
|
|
||||||
prefs!.setString("keepAlive", "-1");
|
|
||||||
} else {
|
|
||||||
prefs!.setString("keepAlive", "300");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsKeepModelLoadedNever,
|
|
||||||
int.parse(prefs!.getString("keepAlive") ?? "300") == 0,
|
|
||||||
(value) {
|
|
||||||
selectionHaptic();
|
|
||||||
setState(() {
|
|
||||||
if (value) {
|
|
||||||
prefs!.setString("keepAlive", "0");
|
|
||||||
} else {
|
|
||||||
prefs!.setString("keepAlive", "300");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
button(
|
|
||||||
(int.parse(prefs!.getString("keepAlive") ?? "300") > 0)
|
|
||||||
? AppLocalizations.of(context)!
|
|
||||||
.settingsKeepModelLoadedSet((int.parse(
|
|
||||||
prefs!.getString("keepAlive") ??
|
|
||||||
"300") ~/
|
|
||||||
60)
|
|
||||||
.toString())
|
|
||||||
: AppLocalizations.of(context)!
|
|
||||||
.settingsKeepModelLoadedFor,
|
|
||||||
Icons.snooze_rounded, () async {
|
|
||||||
selectionHaptic();
|
|
||||||
bool loaded = false;
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return Dialog(
|
|
||||||
alignment: desktopLayout(context)
|
|
||||||
? null
|
|
||||||
: Alignment.bottomRight,
|
|
||||||
child: StatefulBuilder(
|
|
||||||
builder: (context, setLocalState) {
|
|
||||||
if (int.parse(prefs!.getString("keepAlive") ??
|
|
||||||
"0") <=
|
|
||||||
0 &&
|
|
||||||
loaded == false) {
|
|
||||||
prefs!.setString("keepAlive", "0");
|
|
||||||
WidgetsBinding.instance
|
|
||||||
.addPostFrameCallback((timeStamp) {
|
|
||||||
setLocalState(() {});
|
|
||||||
void load() async {
|
|
||||||
try {
|
|
||||||
while (int.parse(prefs!
|
|
||||||
.getString("keepAlive")!) <
|
|
||||||
300) {
|
|
||||||
await Future.delayed(const Duration(
|
|
||||||
milliseconds: 5));
|
|
||||||
prefs!.setString(
|
|
||||||
"keepAlive",
|
|
||||||
(int.parse(prefs!.getString(
|
|
||||||
"keepAlive")!) +
|
|
||||||
30)
|
|
||||||
.toString());
|
|
||||||
setLocalState(() {});
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
prefs!.setString("keepAlive", "300");
|
|
||||||
loaded = true;
|
|
||||||
} catch (_) {
|
|
||||||
prefs!.setString("keepAlive", "300");
|
|
||||||
loaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
load();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
loaded = true;
|
|
||||||
}
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Theme(
|
|
||||||
data: (prefs?.getBool("useDeviceTheme") ??
|
|
||||||
false)
|
|
||||||
? Theme.of(context)
|
|
||||||
: ThemeData.from(
|
|
||||||
colorScheme: ColorScheme.fromSeed(
|
|
||||||
seedColor: Colors.black)),
|
|
||||||
child: DurationPicker(
|
|
||||||
duration: Duration(
|
|
||||||
seconds: int.parse(prefs!
|
|
||||||
.getString("keepAlive") ??
|
|
||||||
"300")),
|
|
||||||
baseUnit: BaseUnit.minute,
|
|
||||||
lowerBound:
|
|
||||||
const Duration(minutes: 1),
|
|
||||||
upperBound:
|
|
||||||
const Duration(minutes: 60),
|
|
||||||
onChange: (value) {
|
|
||||||
if (!loaded) return;
|
|
||||||
if (value.inSeconds == 0) return;
|
|
||||||
prefs!.setString("keepAlive",
|
|
||||||
value.inSeconds.toString());
|
|
||||||
setLocalState(() {});
|
|
||||||
setState(() {});
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
titleDivider(context: context),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsEnableHapticFeedback,
|
|
||||||
(prefs!.getBool("enableHaptic") ?? true), (value) {
|
|
||||||
prefs!.setBool("enableHaptic", value);
|
|
||||||
selectionHaptic();
|
|
||||||
setState(() {});
|
|
||||||
}),
|
|
||||||
desktopFeature()
|
|
||||||
? toggle(
|
|
||||||
context,
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsMaximizeOnStart,
|
|
||||||
(prefs!.getBool("maximizeOnStart") ?? false),
|
|
||||||
(value) {
|
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
prefs!.setBool("maximizeOnStart", value);
|
setState(() {
|
||||||
setState(() {});
|
prefs!.setString("requestType", p0.elementAt(0));
|
||||||
})
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
SegmentedButton(
|
|
||||||
segments: [
|
|
||||||
ButtonSegment(
|
|
||||||
value: "dark",
|
|
||||||
label: Text(AppLocalizations.of(context)!
|
|
||||||
.settingsBrightnessDark),
|
|
||||||
icon: const Icon(Icons.brightness_4_rounded)),
|
|
||||||
ButtonSegment(
|
|
||||||
value: "system",
|
|
||||||
label: Text(AppLocalizations.of(context)!
|
|
||||||
.settingsBrightnessSystem),
|
|
||||||
icon: const Icon(Icons.brightness_auto_rounded)),
|
|
||||||
ButtonSegment(
|
|
||||||
value: "light",
|
|
||||||
label: Text(AppLocalizations.of(context)!
|
|
||||||
.settingsBrightnessLight),
|
|
||||||
icon: const Icon(Icons.brightness_high_rounded))
|
|
||||||
],
|
|
||||||
selected: {
|
|
||||||
prefs!.getString("brightness") ?? "system"
|
|
||||||
},
|
|
||||||
onSelectionChanged: (p0) {
|
|
||||||
selectionHaptic();
|
|
||||||
var tmp = prefs!.getString("brightness") ?? "system";
|
|
||||||
prefs!.setString("brightness", p0.elementAt(0));
|
|
||||||
setState(() {});
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context, setLocalState) {
|
|
||||||
return PopScope(
|
|
||||||
onPopInvoked: (didPop) {
|
|
||||||
prefs!.setString("brightness", tmp);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: AlertDialog(
|
|
||||||
title: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsBrightnessRestartTitle),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsBrightnessRestartDescription),
|
|
||||||
]),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
selectionHaptic();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsBrightnessRestartCancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
selectionHaptic();
|
|
||||||
await prefs!.setString(
|
|
||||||
"brightness",
|
|
||||||
p0.elementAt(0));
|
|
||||||
if (desktopFeature()) {
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
Restart.restartApp();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsBrightnessRestartRestart))
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
!kIsWeb
|
|
||||||
? SegmentedButton(
|
|
||||||
segments: [
|
|
||||||
ButtonSegment(
|
|
||||||
value: "device",
|
|
||||||
label: Text(AppLocalizations.of(context)!
|
|
||||||
.settingsThemeDevice),
|
|
||||||
icon: const Icon(Icons.devices_rounded)),
|
|
||||||
ButtonSegment(
|
|
||||||
value: "ollama",
|
|
||||||
label: Text(AppLocalizations.of(context)!
|
|
||||||
.settingsThemeOllama),
|
|
||||||
icon: const ImageIcon(
|
|
||||||
AssetImage("assets/logo512.png")))
|
|
||||||
],
|
|
||||||
selected: {
|
|
||||||
(prefs?.getBool("useDeviceTheme") ?? false)
|
|
||||||
? "device"
|
|
||||||
: "ollama"
|
|
||||||
},
|
|
||||||
onSelectionChanged: (p0) {
|
|
||||||
selectionHaptic();
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context, setLocalState) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsThemeRestartTitle),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsThemeRestartDescription),
|
|
||||||
]),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
selectionHaptic();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsThemeRestartCancel)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
selectionHaptic();
|
|
||||||
await prefs!.setBool(
|
|
||||||
"useDeviceTheme",
|
|
||||||
p0.elementAt(0) ==
|
|
||||||
"device");
|
|
||||||
if (desktopFeature()) {
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
Restart.restartApp();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(AppLocalizations.of(
|
|
||||||
context)!
|
|
||||||
.settingsThemeRestartRestart))
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
titleDivider(),
|
|
||||||
button(AppLocalizations.of(context)!.settingsTemporaryFixes,
|
|
||||||
Icons.fast_forward_rounded, () {
|
|
||||||
selectionHaptic();
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return StatefulBuilder(
|
|
||||||
builder: (context, setState) {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
left: 16,
|
|
||||||
right: 16,
|
|
||||||
top: 16,
|
|
||||||
bottom: desktopLayout(context) ? 16 : 0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
button(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsTemporaryFixesDescription,
|
|
||||||
Icons.info_rounded,
|
|
||||||
null,
|
|
||||||
color: Colors.grey.harmonizeWith(
|
|
||||||
Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary)),
|
|
||||||
button(
|
|
||||||
AppLocalizations.of(context)!
|
|
||||||
.settingsTemporaryFixesInstructions,
|
|
||||||
Icons.warning_rounded,
|
|
||||||
null,
|
|
||||||
color: Colors.orange.harmonizeWith(
|
|
||||||
Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary)),
|
|
||||||
titleDivider(),
|
|
||||||
// Text(
|
|
||||||
// AppLocalizations.of(context)!
|
|
||||||
// .settingsTemporaryFixesNoFixes,
|
|
||||||
// style: const TextStyle(
|
|
||||||
// color: Colors.grey)),
|
|
||||||
toggle(
|
|
||||||
context,
|
|
||||||
"Fixing code block not scrollable",
|
|
||||||
(prefs!.getBool(
|
|
||||||
"fixCodeblockScroll") ??
|
|
||||||
false), (value) {
|
|
||||||
selectionHaptic();
|
|
||||||
prefs!.setBool(
|
|
||||||
"fixCodeblockScroll", value);
|
|
||||||
if ((prefs!.getBool(
|
|
||||||
"fixCodeblockScroll") ??
|
|
||||||
false) ==
|
|
||||||
false) {
|
|
||||||
prefs!.remove("fixCodeblockScroll");
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}, onLongTap: () {
|
|
||||||
selectionHaptic();
|
|
||||||
launchUrl(Uri.parse(
|
|
||||||
"https://github.com/JHubi1/ollama-app/issues/26"));
|
|
||||||
}),
|
|
||||||
const SizedBox(height: 16)
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
}),
|
const SizedBox(height: 16),
|
||||||
const SizedBox(height: 16)
|
toggle(
|
||||||
]),
|
context,
|
||||||
)
|
AppLocalizations.of(context)!.settingsGenerateTitles,
|
||||||
]))),
|
(prefs!.getBool("generateTitles") ?? true), (value) {
|
||||||
|
selectionHaptic();
|
||||||
|
prefs!.setBool("generateTitles", value);
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!.settingsEnableEditing,
|
||||||
|
(prefs!.getBool("enableEditing") ?? true), (value) {
|
||||||
|
selectionHaptic();
|
||||||
|
prefs!.setBool("enableEditing", value);
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!.settingsAskBeforeDelete,
|
||||||
|
(prefs!.getBool("askBeforeDeletion") ?? false),
|
||||||
|
(value) {
|
||||||
|
selectionHaptic();
|
||||||
|
prefs!.setBool("askBeforeDeletion", value);
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!.settingsShowTips,
|
||||||
|
(prefs!.getBool("tips") ?? true), (value) {
|
||||||
|
selectionHaptic();
|
||||||
|
prefs!.setBool("tips", value);
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
titleDivider(context: context),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsKeepModelLoadedAlways,
|
||||||
|
int.parse(prefs!.getString("keepAlive") ?? "300") ==
|
||||||
|
-1, (value) {
|
||||||
|
selectionHaptic();
|
||||||
|
setState(() {
|
||||||
|
if (value) {
|
||||||
|
prefs!.setString("keepAlive", "-1");
|
||||||
|
} else {
|
||||||
|
prefs!.setString("keepAlive", "300");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsKeepModelLoadedNever,
|
||||||
|
int.parse(prefs!.getString("keepAlive") ?? "300") ==
|
||||||
|
0, (value) {
|
||||||
|
selectionHaptic();
|
||||||
|
setState(() {
|
||||||
|
if (value) {
|
||||||
|
prefs!.setString("keepAlive", "0");
|
||||||
|
} else {
|
||||||
|
prefs!.setString("keepAlive", "300");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
button(
|
||||||
|
(int.parse(prefs!.getString("keepAlive") ?? "300") >
|
||||||
|
0)
|
||||||
|
? AppLocalizations.of(context)!
|
||||||
|
.settingsKeepModelLoadedSet((int.parse(
|
||||||
|
prefs!.getString("keepAlive") ??
|
||||||
|
"300") ~/
|
||||||
|
60)
|
||||||
|
.toString())
|
||||||
|
: AppLocalizations.of(context)!
|
||||||
|
.settingsKeepModelLoadedFor,
|
||||||
|
Icons.snooze_rounded, () async {
|
||||||
|
selectionHaptic();
|
||||||
|
bool loaded = false;
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return Dialog(
|
||||||
|
surfaceTintColor: (Theme.of(context).brightness == Brightness.dark) ? Colors.grey[800] : null,
|
||||||
|
alignment: desktopLayout(context)
|
||||||
|
? null
|
||||||
|
: Alignment.bottomRight,
|
||||||
|
child: StatefulBuilder(
|
||||||
|
builder: (context, setLocalState) {
|
||||||
|
if (int.parse(
|
||||||
|
prefs!.getString("keepAlive") ??
|
||||||
|
"0") <=
|
||||||
|
0 &&
|
||||||
|
loaded == false) {
|
||||||
|
prefs!.setString("keepAlive", "0");
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((timeStamp) {
|
||||||
|
setLocalState(() {});
|
||||||
|
void load() async {
|
||||||
|
try {
|
||||||
|
while (int.parse(prefs!
|
||||||
|
.getString("keepAlive")!) <
|
||||||
|
300) {
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(
|
||||||
|
milliseconds: 5));
|
||||||
|
prefs!.setString(
|
||||||
|
"keepAlive",
|
||||||
|
(int.parse(prefs!.getString(
|
||||||
|
"keepAlive")!) +
|
||||||
|
30)
|
||||||
|
.toString());
|
||||||
|
setLocalState(() {});
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
prefs!
|
||||||
|
.setString("keepAlive", "300");
|
||||||
|
loaded = true;
|
||||||
|
} catch (_) {
|
||||||
|
prefs!
|
||||||
|
.setString("keepAlive", "300");
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Theme(
|
||||||
|
data:
|
||||||
|
(prefs?.getBool("useDeviceTheme") ??
|
||||||
|
false)
|
||||||
|
? Theme.of(context)
|
||||||
|
: ThemeData.from(
|
||||||
|
colorScheme:
|
||||||
|
ColorScheme.fromSeed(
|
||||||
|
seedColor:
|
||||||
|
Colors.black)),
|
||||||
|
child: DurationPicker(
|
||||||
|
duration: Duration(
|
||||||
|
seconds: int.parse(prefs!
|
||||||
|
.getString(
|
||||||
|
"keepAlive") ??
|
||||||
|
"300")),
|
||||||
|
baseUnit: BaseUnit.minute,
|
||||||
|
lowerBound:
|
||||||
|
const Duration(minutes: 1),
|
||||||
|
upperBound:
|
||||||
|
const Duration(minutes: 60),
|
||||||
|
onChange: (value) {
|
||||||
|
if (!loaded) return;
|
||||||
|
if (value.inSeconds == 0) return;
|
||||||
|
prefs!.setString("keepAlive",
|
||||||
|
value.inSeconds.toString());
|
||||||
|
setLocalState(() {});
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
titleDivider(context: context),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsEnableHapticFeedback,
|
||||||
|
(prefs!.getBool("enableHaptic") ?? true), (value) {
|
||||||
|
prefs!.setBool("enableHaptic", value);
|
||||||
|
selectionHaptic();
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
desktopFeature()
|
||||||
|
? toggle(
|
||||||
|
context,
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsMaximizeOnStart,
|
||||||
|
(prefs!.getBool("maximizeOnStart") ?? false),
|
||||||
|
(value) {
|
||||||
|
selectionHaptic();
|
||||||
|
prefs!.setBool("maximizeOnStart", value);
|
||||||
|
setState(() {});
|
||||||
|
})
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SegmentedButton(
|
||||||
|
segments: [
|
||||||
|
ButtonSegment(
|
||||||
|
value: "dark",
|
||||||
|
label: Text(AppLocalizations.of(context)!
|
||||||
|
.settingsBrightnessDark),
|
||||||
|
icon: const Icon(Icons.brightness_4_rounded)),
|
||||||
|
ButtonSegment(
|
||||||
|
value: "system",
|
||||||
|
label: Text(AppLocalizations.of(context)!
|
||||||
|
.settingsBrightnessSystem),
|
||||||
|
icon:
|
||||||
|
const Icon(Icons.brightness_auto_rounded)),
|
||||||
|
ButtonSegment(
|
||||||
|
value: "light",
|
||||||
|
label: Text(AppLocalizations.of(context)!
|
||||||
|
.settingsBrightnessLight),
|
||||||
|
icon: const Icon(Icons.brightness_high_rounded))
|
||||||
|
],
|
||||||
|
selected: {
|
||||||
|
prefs!.getString("brightness") ?? "system"
|
||||||
|
},
|
||||||
|
onSelectionChanged: (p0) {
|
||||||
|
selectionHaptic();
|
||||||
|
var tmp =
|
||||||
|
prefs!.getString("brightness") ?? "system";
|
||||||
|
prefs!.setString("brightness", p0.elementAt(0));
|
||||||
|
setState(() {});
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setLocalState) {
|
||||||
|
return PopScope(
|
||||||
|
onPopInvoked: (didPop) {
|
||||||
|
prefs!.setString("brightness", tmp);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: AlertDialog(
|
||||||
|
surfaceTintColor: (Theme.of(context).brightness == Brightness.dark) ? Colors.grey[800] : null,
|
||||||
|
title: Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.settingsBrightnessRestartTitle),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.settingsBrightnessRestartDescription),
|
||||||
|
]),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
selectionHaptic();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations
|
||||||
|
.of(context)!
|
||||||
|
.settingsBrightnessRestartCancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
selectionHaptic();
|
||||||
|
await prefs!.setString(
|
||||||
|
"brightness",
|
||||||
|
p0.elementAt(0));
|
||||||
|
if (desktopFeature()) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Restart.restartApp();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations
|
||||||
|
.of(context)!
|
||||||
|
.settingsBrightnessRestartRestart))
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
!kIsWeb
|
||||||
|
? SegmentedButton(
|
||||||
|
segments: [
|
||||||
|
ButtonSegment(
|
||||||
|
value: "device",
|
||||||
|
label: Text(AppLocalizations.of(context)!
|
||||||
|
.settingsThemeDevice),
|
||||||
|
icon: const Icon(Icons.devices_rounded)),
|
||||||
|
ButtonSegment(
|
||||||
|
value: "ollama",
|
||||||
|
label: Text(AppLocalizations.of(context)!
|
||||||
|
.settingsThemeOllama),
|
||||||
|
icon: const ImageIcon(
|
||||||
|
AssetImage("assets/logo512.png")))
|
||||||
|
],
|
||||||
|
selected: {
|
||||||
|
(prefs?.getBool("useDeviceTheme") ?? false)
|
||||||
|
? "device"
|
||||||
|
: "ollama"
|
||||||
|
},
|
||||||
|
onSelectionChanged: (p0) {
|
||||||
|
selectionHaptic();
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setLocalState) {
|
||||||
|
return AlertDialog(
|
||||||
|
surfaceTintColor: (Theme.of(context).brightness == Brightness.dark) ? Colors.grey[800] : null,
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsThemeRestartTitle),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.settingsThemeRestartDescription),
|
||||||
|
]),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
selectionHaptic();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations
|
||||||
|
.of(context)!
|
||||||
|
.settingsThemeRestartCancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
selectionHaptic();
|
||||||
|
await prefs!.setBool(
|
||||||
|
"useDeviceTheme",
|
||||||
|
p0.elementAt(0) ==
|
||||||
|
"device");
|
||||||
|
if (desktopFeature()) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
Restart.restartApp();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations
|
||||||
|
.of(context)!
|
||||||
|
.settingsThemeRestartRestart))
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
titleDivider(),
|
||||||
|
button(
|
||||||
|
AppLocalizations.of(context)!.settingsTemporaryFixes,
|
||||||
|
Icons.fast_forward_rounded, () {
|
||||||
|
selectionHaptic();
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: (Theme.of(context).brightness ==
|
||||||
|
Brightness.dark)
|
||||||
|
? BoxDecoration(
|
||||||
|
border:
|
||||||
|
Border.all(color: Colors.white),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(26),
|
||||||
|
topRight: Radius.circular(26)))
|
||||||
|
: null,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
top: 16,
|
||||||
|
bottom: desktopLayout(context) ? 16 : 0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
button(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsTemporaryFixesDescription,
|
||||||
|
Icons.info_rounded,
|
||||||
|
null,
|
||||||
|
color: Colors.grey.harmonizeWith(
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary)),
|
||||||
|
button(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
|
.settingsTemporaryFixesInstructions,
|
||||||
|
Icons.warning_rounded,
|
||||||
|
null,
|
||||||
|
color: Colors.orange.harmonizeWith(
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary)),
|
||||||
|
titleDivider(),
|
||||||
|
// Text(
|
||||||
|
// AppLocalizations.of(context)!
|
||||||
|
// .settingsTemporaryFixesNoFixes,
|
||||||
|
// style: const TextStyle(
|
||||||
|
// color: Colors.grey)),
|
||||||
|
toggle(
|
||||||
|
context,
|
||||||
|
"Fixing code block not scrollable",
|
||||||
|
(prefs!.getBool(
|
||||||
|
"fixCodeblockScroll") ??
|
||||||
|
false), (value) {
|
||||||
|
selectionHaptic();
|
||||||
|
prefs!.setBool(
|
||||||
|
"fixCodeblockScroll", value);
|
||||||
|
if ((prefs!.getBool(
|
||||||
|
"fixCodeblockScroll") ??
|
||||||
|
false) ==
|
||||||
|
false) {
|
||||||
|
prefs!.remove("fixCodeblockScroll");
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}, onLongTap: () {
|
||||||
|
selectionHaptic();
|
||||||
|
launchUrl(Uri.parse(
|
||||||
|
"https://github.com/JHubi1/ollama-app/issues/26"));
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 16)
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 16)
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
])),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,6 +216,21 @@ class _ScreenSettingsVoiceState extends State<ScreenSettingsVoice> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
decoration: (Theme.of(context)
|
||||||
|
.brightness ==
|
||||||
|
Brightness.dark)
|
||||||
|
? BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white),
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.only(
|
||||||
|
topLeft:
|
||||||
|
Radius.circular(
|
||||||
|
26),
|
||||||
|
topRight:
|
||||||
|
Radius.circular(
|
||||||
|
26)))
|
||||||
|
: null,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
|
|
|
@ -80,7 +80,6 @@ List getHistoryString([String? uuid]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getTitleAi(List history) async {
|
Future<String> getTitleAi(List history) async {
|
||||||
print(history);
|
|
||||||
final generated = await (llama.OllamaClient(
|
final generated = await (llama.OllamaClient(
|
||||||
headers: (jsonDecode(prefs!.getString("hostHeaders") ?? "{}") as Map)
|
headers: (jsonDecode(prefs!.getString("hostHeaders") ?? "{}") as Map)
|
||||||
.cast<String, String>(),
|
.cast<String, String>(),
|
||||||
|
@ -92,7 +91,7 @@ Future<String> getTitleAi(List history) async {
|
||||||
const llama.Message(
|
const llama.Message(
|
||||||
role: llama.MessageRole.system,
|
role: llama.MessageRole.system,
|
||||||
content:
|
content:
|
||||||
"Generate a three to six word title for the conversation provided by the user. If an object or person is very important in the conversation, put it in the title as well; keep the focus on the main subject. You must not put the assistant in the focus and you must not put the word 'assistant' in the title! Do preferably use title case. Use a formal tone, don't use dramatic words, like 'mystery' Use spaces between words, do not use camel case! You must not use markdown or any other formatting language! You must not use emojis or any other symbols! You must not use general clauses like 'assistance', 'help' or 'session' in your title! \n\n~~User Introduces Themselves~~ -> User Introduction\n~~User Asks for Help with a Problem~~ -> Problem Help\n~~User has a _**big**_ Problem~~ -> Big Problem\n~~Conversation~~ -> Conversation about Fireflies"),
|
"Generate a three to six word title for the conversation provided by the user. If an object or person is very important in the conversation, put it in the title as well; keep the focus on the main subject. You must not put the assistant in the focus and you must not put the word 'assistant' in the title! Do preferably use title case. Use a formal tone, don't use dramatic words, like 'mystery' Use spaces between words, do not use camel case! You must not use markdown or any other formatting language! You must not use emojis or any other symbols! You must not use general clauses like 'assistance', 'help' or 'session' in your title! \n\n~~User Introduces Themselves~~ -> User Introduction\n~~User Asks for Help with a Problem~~ -> Problem Help\n~~User has a _**big**_ Problem~~ -> Big Problem"),
|
||||||
llama.Message(
|
llama.Message(
|
||||||
role: llama.MessageRole.user,
|
role: llama.MessageRole.user,
|
||||||
content: "```\n${jsonEncode(history)}\n```")
|
content: "```\n${jsonEncode(history)}\n```")
|
||||||
|
|
|
@ -278,6 +278,10 @@ void setModel(BuildContext context, Function setState) {
|
||||||
? const Offset(289, 0)
|
? const Offset(289, 0)
|
||||||
: const Offset(0, 0),
|
: const Offset(0, 0),
|
||||||
child: Dialog(
|
child: Dialog(
|
||||||
|
surfaceTintColor:
|
||||||
|
(Theme.of(context).brightness == Brightness.dark)
|
||||||
|
? Colors.grey[800]
|
||||||
|
: null,
|
||||||
alignment: desktopLayoutRequired(context)
|
alignment: desktopLayoutRequired(context)
|
||||||
? Alignment.topLeft
|
? Alignment.topLeft
|
||||||
: Alignment.topCenter,
|
: Alignment.topCenter,
|
||||||
|
@ -285,7 +289,17 @@ void setModel(BuildContext context, Function setState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
showModalBottomSheet(context: context, builder: (context) => content);
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => Container(
|
||||||
|
decoration: (Theme.of(context).brightness == Brightness.dark)
|
||||||
|
? BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.white),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(26),
|
||||||
|
topRight: Radius.circular(26)))
|
||||||
|
: null,
|
||||||
|
child: content));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +420,79 @@ void loadChat(String uuid, Function setState) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> deleteChatDialog(BuildContext context, Function setState,
|
||||||
|
{bool takeAction = true,
|
||||||
|
bool? additionalCondition,
|
||||||
|
String? uuid,
|
||||||
|
bool popSidebar = false}) async {
|
||||||
|
additionalCondition ??= true;
|
||||||
|
uuid ??= chatUuid;
|
||||||
|
|
||||||
|
bool returnValue = false;
|
||||||
|
void delete() {
|
||||||
|
returnValue = true;
|
||||||
|
if (takeAction) {
|
||||||
|
for (var i = 0; i < (prefs!.getStringList("chats") ?? []).length; i++) {
|
||||||
|
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] ==
|
||||||
|
uuid) {
|
||||||
|
List<String> tmp = prefs!.getStringList("chats")!;
|
||||||
|
tmp.removeAt(i);
|
||||||
|
prefs!.setStringList("chats", tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chatUuid == uuid) {
|
||||||
|
messages = [];
|
||||||
|
chatUuid = null;
|
||||||
|
if (!desktopLayoutRequired(context) &&
|
||||||
|
Navigator.of(context).canPop() &&
|
||||||
|
popSidebar) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((prefs!.getBool("askBeforeDeletion") ?? false) && additionalCondition) {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(builder: (context, setLocalState) {
|
||||||
|
return AlertDialog(
|
||||||
|
surfaceTintColor:
|
||||||
|
(Theme.of(context).brightness == Brightness.dark)
|
||||||
|
? Colors.grey[800]
|
||||||
|
: null,
|
||||||
|
title: Text(AppLocalizations.of(context)!.deleteDialogTitle),
|
||||||
|
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
Text(AppLocalizations.of(context)!.deleteDialogDescription),
|
||||||
|
]),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
selectionHaptic();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.deleteDialogCancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
selectionHaptic();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
delete();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.deleteDialogDelete))
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
delete();
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
Future<String> prompt(BuildContext context,
|
Future<String> prompt(BuildContext context,
|
||||||
{String description = "",
|
{String description = "",
|
||||||
String value = "",
|
String value = "",
|
||||||
|
@ -431,6 +518,13 @@ Future<String> prompt(BuildContext context,
|
||||||
return StatefulBuilder(builder: (context, setLocalState) {
|
return StatefulBuilder(builder: (context, setLocalState) {
|
||||||
return PopScope(
|
return PopScope(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
decoration: (Theme.of(context).brightness == Brightness.dark)
|
||||||
|
? BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.white),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(26),
|
||||||
|
topRight: Radius.circular(26)))
|
||||||
|
: null,
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
|
|
|
@ -125,6 +125,7 @@ void updateDialog(BuildContext context, Function title) {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
|
surfaceTintColor: (Theme.of(context).brightness == Brightness.dark) ? Colors.grey[800] : null,
|
||||||
title:
|
title:
|
||||||
Text(AppLocalizations.of(context)!.settingsUpdateDialogTitle),
|
Text(AppLocalizations.of(context)!.settingsUpdateDialogTitle),
|
||||||
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
@ -133,9 +134,12 @@ void updateDialog(BuildContext context, Function title) {
|
||||||
title(AppLocalizations.of(context)!.settingsUpdateChangeLog),
|
title(AppLocalizations.of(context)!.settingsUpdateChangeLog),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: MarkdownBody(
|
child: Container(
|
||||||
data: updateChangeLog ?? "No changelog given.",
|
constraints: const BoxConstraints(maxWidth: 1000),
|
||||||
shrinkWrap: true)))
|
child: MarkdownBody(
|
||||||
|
data: updateChangeLog ?? "No changelog given.",
|
||||||
|
shrinkWrap: true),
|
||||||
|
)))
|
||||||
]),
|
]),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|
Loading…
Reference in New Issue