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 a6e8836 commit dc8664f
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 139 deletions.
4 changes: 0 additions & 4 deletions lib/data/res/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,4 @@ 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;
}
1 change: 1 addition & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"fileTooLarge": "File too large: {size}",
"fontSize": "Font size",
"fontSizeSettingTip": "Applies only to code blocks",
"freeCopy": "Free copy",
"genChatTitle": "Chat title generator",
"help": "Help",
"helpTip": "Before you open an issue, please read the following:\n1. Paste the **entire log** (long press the title of home page) in the issue template.\n2. Make sure the issue is caused by this app.\n3. All valid and positive feedbacks are welcome. Subjective feedbacks (for example, you think another UI is better) may not be accepted.\n\nAfter you read the above, you can:\n- If you have **any questions or feature requests**, please open a [discussion]({discussion}). \n- If you have found **any bugs**, please open an [issue]({issue}).\n",
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"fileTooLarge": "文件过大:{size}",
"fontSize": "字体大小",
"fontSizeSettingTip": "仅对代码块生效",
"freeCopy": "自由复制",
"genChatTitle": "生成聊天标题",
"help": "帮助",
"helpTip": "吹水、参与开发、了解如何使用:\n- QQ群 **762870488**\n\n反馈前须知:\n1. 反馈问题请附带 log(长按首页标题),并以 bug 模版提交。\n2. 反馈问题前请检查是否是 本app 的问题。\n3. 欢迎所有有效、正面的反馈,主观(比如你觉得其他UI更好看)的反馈不一定会接受\n\n确认了解上述内容后:\n- 如果你有**任何问题或者功能请求**,请在 [讨论]({discussion}) 中交流。\n- 如果 app 有**任何 bug**,请在 [问题]({issue}) 中反馈。",
Expand Down
117 changes: 77 additions & 40 deletions lib/view/page/home/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,34 +135,27 @@ class _ChatPageState extends State<_ChatPage>
children: [
_buildChatItemTitle(chatItems, chatItem),
UIs.height13,
GestureDetector(
onLongPress: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => _MarkdownCopyPage(
text: chatItem.toMarkdown,
actions: _buildChatItemFuncs(chatItems, chatItem),
heroTag: chatItem.id,
),
));
},
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),
);
},
),
BlurOverlay(
popup: () => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _buildChatItemFuncs(chatItems, chatItem)
.joinWith(UIs.width13),
),
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 @@ -232,13 +225,53 @@ class _ChatPageState extends State<_ChatPage>
);
}

Widget _buildCircleBtn({
required String text,
required IconData icon,
required VoidCallback onTap,
}) {
return ClipOval(
child: Material(
color: const Color.fromARGB(239, 47, 47, 47),
child: InkWell(
onTap: () {
BlurOverlay.close?.call();
onTap();
},
child: SizedBox(
width: 77,
height: 77,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 20),
const SizedBox(height: 9),
Text(text),
],
),
),
),
),
);
}

List<Widget> _buildChatItemFuncs(
List<ChatHistoryItem> chatItems,
ChatHistoryItem chatItem,
) {
final replayEnabled =
chatItem.role == ChatRole.user && Stores.setting.replay.fetch();
return [
_buildCircleBtn(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => _MarkdownCopyPage(text: chatItem.toMarkdown),
),
),
text: l10n.freeCopy,
icon: BoxIcons.bxs_crop,
),
if (replayEnabled)
ListenableBuilder(
listenable: _sendBtnRN,
Expand All @@ -249,23 +282,25 @@ class _ChatPageState extends State<_ChatPage>
if (isImgChat) return UIs.placeholder;
final isWorking = _chatStreamSubs.containsKey(_curChatId);
if (isWorking) return UIs.placeholder;
return IconButton(
onPressed: () => _onTapReplay(
return _buildCircleBtn(
onTap: () => _onTapReplay(
context,
_curChatId,
chatItem,
),
icon: const Icon(Icons.refresh),
text: l10n.replay,
icon: Icons.refresh,
);
},
),
if (replayEnabled)
IconButton(
onPressed: () => _onTapEditMsg(context, chatItem),
icon: const Icon(Icons.edit),
_buildCircleBtn(
onTap: () => _onTapEditMsg(context, chatItem),
text: l10n.edit,
icon: Icons.edit,
),
IconButton(
onPressed: () async {
_buildCircleBtn(
onTap: () async {
BlurOverlay.close?.call();
final idx = chatItems.indexOf(chatItem) + 1;
final result = await context.showRoundDialog<bool>(
Expand All @@ -281,11 +316,13 @@ class _ChatPageState extends State<_ChatPage>
_historyRNMap[_curChatId]?.build();
_chatRN.build();
},
icon: const Icon(Icons.delete),
text: l10n.delete,
icon: Icons.delete,
),
IconButton(
onPressed: () => _onCopy(chatItem.toMarkdown),
icon: const Icon(MingCute.copy_2_fill),
_buildCircleBtn(
onTap: () => _onCopy(chatItem.toMarkdown),
text: l10n.copy,
icon: MingCute.copy_2_fill,
),
];
}
Expand Down
60 changes: 15 additions & 45 deletions lib/view/page/home/md_copy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,38 @@ 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,
actions: IconButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: text));
},
icon: const Icon(Icons.copy, size: 20),
).asList,
),
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),
),
return 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),
),
),
);
Expand Down
70 changes: 20 additions & 50 deletions lib/view/widget/overlay.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:gpt_box/core/ext/value_notifier.dart';
import 'package:gpt_box/data/res/ui.dart';

class BlurOverlay extends StatefulWidget {
final Widget child;
final Widget Function() popup;
final Widget Function()? bottom;

const BlurOverlay({
super.key,
required this.child,
this.bottom,
required this.popup,
});

Expand All @@ -28,11 +27,8 @@ class _BlurOverlayState extends State<BlurOverlay>
///
/// The animation controller is created when the overlay is shown.
AnimationController? _animeCtrl;
Animation<Offset>? _offsetAnime;
Animation<double>? _blurAnime;
Animation<double>? _fadeAnime;
Animation<Color?>? _colorAnime;

MediaQueryData? _media;

final _isShowingOverlay = false.vn;

Expand All @@ -41,12 +37,6 @@ class _BlurOverlayState extends State<BlurOverlay>
// super.initState();
// }

@override
void didChangeDependencies() {
super.didChangeDependencies();
_media = MediaQuery.of(context);
}

@override
void dispose() {
_animeCtrl?.dispose();
Expand All @@ -59,33 +49,20 @@ class _BlurOverlayState extends State<BlurOverlay>
/// this instance.
BlurOverlay.close = _removeOverlay;

final overlayState = Overlay.of(context);

/// Only create once (`??=`)
_animeCtrl ??= AnimationController(
vsync: this,
duration: Durations.medium1,
);

final renderBox = context.findRenderObject() as RenderBox;
var startOffset = renderBox.localToGlobal(Offset.zero);
final overlayState = Overlay.of(context);
final overlayBox = overlayState.context.findRenderObject() as RenderBox;
var endOffset = overlayBox.size.centerLeft(Offset.zero);
_offsetAnime = Tween(
begin: startOffset - endOffset,
end: Offset.zero,
).animate(
_blurAnime = Tween(begin: 0.0, end: 5.0).animate(
CurvedAnimation(parent: _animeCtrl!, curve: Curves.easeInOutCubic),
);

_fadeAnime = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _animeCtrl!, curve: Curves.easeInOutCubic),
);
_colorAnime = ColorTween(
begin: Colors.transparent,
end: Colors.black,
).animate(
CurvedAnimation(parent: _animeCtrl!, curve: Curves.easeInOutCubic),
);

_overlayEntry = _createOverlayEntry(context);
overlayState.insert(_overlayEntry!);
Expand All @@ -109,27 +86,20 @@ class _BlurOverlayState extends State<BlurOverlay>
child: AnimatedBuilder(
animation: _animeCtrl!,
builder: (_, __) {
return Container(
color: _colorAnime!.value,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 11),
child: FadeTransition(
opacity: _fadeAnime!,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Transform.translate(
offset: _offsetAnime!.value,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: (_media?.size.height ?? 700) * 0.6,
),
child: widget.popup(),
),
),
if (widget.bottom != null) UIs.height13,
if (widget.bottom != null) widget.bottom!(),
],
return BackdropFilter(
filter: ImageFilter.blur(
sigmaX: _blurAnime!.value,
sigmaY: _blurAnime!.value,
),
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 11),
child: FadeTransition(
opacity: _fadeAnime!,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [widget.popup()],
),
),
),
);
Expand Down

0 comments on commit dc8664f

Please sign in to comment.