Skip to content

Commit

Permalink
opt.: free copy
Browse files Browse the repository at this point in the history
  • Loading branch information
lollipopkit committed May 8, 2024
1 parent 398d45f commit 909b174
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 94 deletions.
17 changes: 10 additions & 7 deletions lib/core/route/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ class AppRoute<Ret, Arg> {
final ret = middlewares?.any((e) => !e((context: context, route: this)));
if (ret == true) return Future.value(null);

return Navigator.push<Ret>(
context,
MaterialPageRoute(
builder: (context) => page(key: key, args: args),
settings: RouteSettings(name: path),
),
);
final route = Stores.setting.cupertinoRoute.fetch()
? CupertinoPageRoute<Ret>(
builder: (_) => page(key: key, args: args),
settings: RouteSettings(name: path),
)
: MaterialPageRoute<Ret>(
builder: (_) => page(key: key, args: args),
settings: RouteSettings(name: path),
);
return Navigator.push<Ret>(context, route);
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/core/route/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ library route;

import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:gpt_box/core/analysis.dart';
import 'package:gpt_box/data/store/all.dart';
import 'package:gpt_box/view/page/about.dart';
import 'package:gpt_box/view/page/backup/view.dart';
import 'package:gpt_box/view/page/debug.dart';
Expand Down
4 changes: 4 additions & 0 deletions lib/data/res/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,8 @@ abstract final class UIs {
static const bgColor = DynColor(light: Colors.white, dark: Colors.black);

static const textColor = DynColor(light: Colors.black, dark: Colors.white);

/// Sizes
static const textSize = 13.0;
}
3 changes: 3 additions & 0 deletions lib/data/store/setting.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:gpt_box/core/store.dart';
import 'package:gpt_box/core/util/platform/base.dart';

class SettingStore extends Store {
SettingStore() : super('setting');
Expand Down Expand Up @@ -54,4 +55,6 @@ class SettingStore extends Store {
late final replay = property('replay', false);

late final countly = property('countly', true);

late final cupertinoRoute = property('cupertinoRoute', isIOS || isMacOS);
}
2 changes: 2 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,15 @@
"privacyTip": "This app does not collect any private data.\nThe data this app collects is used only for the purpose of improving the user experience.",
"profile": "Profile",
"promptsSettingsItem": "Prompts",
"raw": "Raw",
"rename": "Rename",
"replay": "Replay",
"res": "Resource",
"restore": "Restore",
"restoreOpenaiTip": "Document can be found [here]({url})",
"rmDuplication": "Remove duplication",
"rmDuplicationFmt": "Sure to delete [{count}] items?",
"route": "Route",
"save": "Save",
"search": "Search",
"secretKey": "Secret key",
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,15 @@
"privacyTip": "此 app 不搜集任何隐私信息。所搜集的信息只用于改进 app。",
"profile": "配置",
"promptsSettingsItem": "提示词",
"raw": "原始",
"rename": "重命名",
"replay": "重放",
"res": "资源",
"restore": "恢复",
"restoreOpenaiTip": "文档可以在 [这里]({url}) 找到",
"rmDuplication": "删除重复",
"rmDuplicationFmt": "确定要删除 [{count}]个重复项目吗?",
"route": "路由",
"save": "保存",
"search": "搜索",
"secretKey": "密钥",
Expand Down
158 changes: 71 additions & 87 deletions lib/view/page/home/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,47 +135,34 @@ class _ChatPageState extends State<_ChatPage>
children: [
_buildChatItemTitle(chatItems, chatItem),
UIs.height13,
BlurOverlay(
key: Key(chatItem.id),
bottom: () => _buildChatItemFuncs(chatItems, chatItem).card,
popup: () {
final child = Padding(
padding: const EdgeInsets.symmetric(horizontal: 11),
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 7),
child: SelectableText(
chatItem.toMarkdown,
showCursor: true,
style: TextStyle(
fontSize: 14,
color: UIs.textColor.fromBool(RNode.dark.value),
),
),
GestureDetector(
onLongPress: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => _MarkdownCopyPage(
text: chatItem.toMarkdown,
actions: _buildChatItemFuncs(chatItems, chatItem),
heroTag: chatItem.id,
),
);
return Material(
color: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(13),
),
child: child,
);
));
},
child: ListenableBuilder(
listenable: node,
builder: (_, __) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: chatItem.content
.map((e) => switch (e.type) {
ChatContentType.audio => _buildAudio(e),
ChatContentType.image => _buildImage(e),
_ => _buildText(e),
})
.joinWith(UIs.height13),
);
},
child: Hero(
tag: chatItem.id,
child: ListenableBuilder(
listenable: node,
builder: (_, __) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: chatItem.content
.map((e) => switch (e.type) {
ChatContentType.audio => _buildAudio(e),
ChatContentType.image => _buildImage(e),
_ => _buildText(e),
})
.joinWith(UIs.height13),
);
},
),
),
),
UIs.height13,
Expand Down Expand Up @@ -245,65 +232,62 @@ class _ChatPageState extends State<_ChatPage>
);
}

Widget _buildChatItemFuncs(
List<Widget> _buildChatItemFuncs(
List<ChatHistoryItem> chatItems,
ChatHistoryItem chatItem,
) {
final replayEnabled =
chatItem.role == ChatRole.user && Stores.setting.replay.fetch();
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (replayEnabled)
ListenableBuilder(
listenable: _sendBtnRN,
builder: (_, __) {
/// TODO: Can't replay image message.
final isImgChat =
chatItem.content.any((e) => e.type == ChatContentType.image);
if (isImgChat) return UIs.placeholder;
final isWorking = _chatStreamSubs.containsKey(_curChatId);
if (isWorking) return UIs.placeholder;
return IconButton(
onPressed: () => _onTapReplay(
context,
_curChatId,
chatItem,
),
icon: const Icon(Icons.refresh, size: 17),
);
},
),
if (replayEnabled)
IconButton(
onPressed: () => _onTapEditMsg(context, chatItem),
icon: const Icon(Icons.edit, size: 17),
),
IconButton(
onPressed: () async {
BlurOverlay.close?.call();
final idx = chatItems.indexOf(chatItem) + 1;
final result = await context.showRoundDialog<bool>(
title: l10n.attention,
child: Text(
l10n.delFmt('${chatItem.role.localized}#$idx', l10n.chat),
return [
if (replayEnabled)
ListenableBuilder(
listenable: _sendBtnRN,
builder: (_, __) {
/// TODO: Can't replay image message.
final isImgChat =
chatItem.content.any((e) => e.type == ChatContentType.image);
if (isImgChat) return UIs.placeholder;
final isWorking = _chatStreamSubs.containsKey(_curChatId);
if (isWorking) return UIs.placeholder;
return IconButton(
onPressed: () => _onTapReplay(
context,
_curChatId,
chatItem,
),
actions: Btns.oks(onTap: () => context.pop(true), red: true),
icon: const Icon(Icons.refresh),
);
if (result != true) return;
chatItems.remove(chatItem);
_storeChat(_curChatId, context);
_historyRNMap[_curChatId]?.build();
_chatRN.build();
},
icon: const Icon(Icons.delete, size: 17),
),
if (replayEnabled)
IconButton(
onPressed: () => _onCopy(chatItem.toMarkdown),
icon: const Icon(Icons.copy, size: 15),
onPressed: () => _onTapEditMsg(context, chatItem),
icon: const Icon(Icons.edit),
),
],
);
IconButton(
onPressed: () async {
BlurOverlay.close?.call();
final idx = chatItems.indexOf(chatItem) + 1;
final result = await context.showRoundDialog<bool>(
title: l10n.attention,
child: Text(
l10n.delFmt('${chatItem.role.localized}#$idx', l10n.chat),
),
actions: Btns.oks(onTap: () => context.pop(true), red: true),
);
if (result != true) return;
chatItems.remove(chatItem);
_storeChat(_curChatId, context);
_historyRNMap[_curChatId]?.build();
_chatRN.build();
},
icon: const Icon(Icons.delete),
),
IconButton(
onPressed: () => _onCopy(chatItem.toMarkdown),
icon: const Icon(MingCute.copy_2_fill),
),
];
}

void _onCopy(String content) {
Expand Down
2 changes: 2 additions & 0 deletions lib/view/page/home/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:dart_openai/dart_openai.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
//import 'package:flutter_tiktoken/flutter_tiktoken.dart';
import 'package:gpt_box/core/build_mode.dart';
import 'package:gpt_box/core/ext/chat_history.dart';
Expand Down Expand Up @@ -71,6 +72,7 @@ part 'appbar.dart';
part 'bottom.dart';
part 'url_scheme.dart';
part 'req.dart';
part 'md_copy.dart';

class HomePage extends StatefulWidget {
const HomePage({super.key});
Expand Down
71 changes: 71 additions & 0 deletions lib/view/page/home/md_copy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
part of 'home.dart';

final class _MarkdownCopyPage extends StatelessWidget {
final String text;
final List<Widget> actions;
final String heroTag;

const _MarkdownCopyPage({
required this.text,
required this.actions,
required this.heroTag,
});

// @override
// State<_MarkdownCopyPage> createState() => _MarkdownCopyPageState();
// }

// final class _MarkdownCopyPageState extends State<_MarkdownCopyPage>
// with SingleTickerProviderStateMixin {
// late final _animeCtrl = AnimationController(
// vsync: this,
// duration: Durations.medium1,
// );

// @override
// void initState() {
// super.initState();
// _animeCtrl.forward();
// }

// @override
// Widget build(BuildContext context) {
// return PopScope(
// canPop: true,
// onPopInvoked: (_) async {
// await _animeCtrl.reverse();
// },
// child: _buildBody(),
// );
// }

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: Text(l10n.raw),
centerTitle: false,
actions: actions,
),
body: _buildBody(),
);
}

Widget _buildBody() {
return Hero(
tag: heroTag,
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 7, horizontal: 13),
child: SelectableText(
text,
autofocus: true,
showCursor: true,
style: TextStyle(
fontSize: 14,
color: UIs.textColor.fromBool(RNode.dark.value),
),
),
),
);
}
}
9 changes: 9 additions & 0 deletions lib/view/page/setting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ class _SettingPageState extends State<SettingPage> {
title: Text(l10n.more),
children: [
_buildCountly(),
_buildCupertinoRoute(),
],
);
}
Expand All @@ -716,4 +717,12 @@ class _SettingPageState extends State<SettingPage> {
trailing: StoreSwitch(prop: _store.countly),
);
}

Widget _buildCupertinoRoute() {
return ListTile(
leading: const Icon(MingCute.route_fill),
title: Text('Cupertino ${l10n.route}'),
trailing: StoreSwitch(prop: _store.cupertinoRoute),
);
}
}

0 comments on commit 909b174

Please sign in to comment.