chore: revamp mobile url editor (#4602)

* chore: revamp mobile url editor

* chore: add i18n

* chore: use shared url launcher

* chore: translation bump

* chore: add a toast notification after URL is copied to clipboard

* chore: listen to onchanged

* chore: improve text field editing experience

* chore: disable quick action buttons if url cell data is empty

* chore: apply suggestions from code review

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>

* chore: provide the bloc and watch changes

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
This commit is contained in:
Richard Shiue 2024-03-14 09:36:29 +08:00 committed by GitHub
parent 48cac4c5ac
commit 25d4b4f718
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 192 additions and 140 deletions

View File

@ -52,7 +52,7 @@ class _MobileBottomSheetRenameWidgetState
height: 42.0,
child: FlowyTextField(
controller: controller,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
onSubmitted: (text) => widget.onRename(text),
),
),

View File

@ -115,7 +115,7 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory>
Widget build(BuildContext context) {
if (widget.cellDataNotifier.value.isNotEmpty) {
return FlowyTooltip(
message: LocaleKeys.tooltip_urlCopyAccessory.tr(),
message: LocaleKeys.grid_url_copy.tr(),
preferBelow: false,
child: _URLAccessoryIconContainer(
child: FlowySvg(
@ -161,7 +161,7 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory>
Widget build(BuildContext context) {
if (widget.cellDataNotifier.value.isNotEmpty) {
return FlowyTooltip(
message: LocaleKeys.tooltip_urlLaunchAccessory.tr(),
message: LocaleKeys.grid_url_launch.tr(),
preferBelow: false,
child: _URLAccessoryIconContainer(
child: FlowySvg(

View File

@ -1,5 +1,8 @@
import 'dart:async';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
@ -8,8 +11,13 @@ import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.d
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:go_router/go_router.dart';
import '../desktop_grid/desktop_grid_url_cell.dart';
import '../desktop_row_detail/desktop_row_detail_url_cell.dart';
@ -106,7 +114,8 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
child: BlocListener<URLCellBloc, URLCellState>(
listenWhen: (previous, current) => previous.content != current.content,
listener: (context, state) {
_textEditingController.text = state.content;
_textEditingController.value =
_textEditingController.value.copyWith(text: state.content);
widget._cellDataNotifier.value = state.content;
},
child: widget.skin.build(
@ -135,6 +144,74 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
String? onCopy() => cellBloc.state.content;
}
class MobileURLEditor extends StatelessWidget {
const MobileURLEditor({
super.key,
required this.textEditingController,
});
final TextEditingController textEditingController;
@override
Widget build(BuildContext context) {
return Column(
children: [
const VSpace(4.0),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: FlowyTextField(
controller: textEditingController,
hintStyle: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Theme.of(context).hintColor),
hintText: LocaleKeys.grid_url_textFieldHint.tr(),
textStyle: Theme.of(context).textTheme.bodyMedium,
keyboardType: TextInputType.url,
hintTextConstraints: const BoxConstraints(maxHeight: 52),
onChanged: (_) {
if (textEditingController.value.composing.isCollapsed) {
context
.read<URLCellBloc>()
.add(URLCellEvent.updateURL(textEditingController.text));
}
},
onSubmitted: (text) =>
context.read<URLCellBloc>().add(URLCellEvent.updateURL(text)),
),
),
const VSpace(8.0),
MobileQuickActionButton(
enable: context.watch<URLCellBloc>().state.content.isNotEmpty,
onTap: () {
openUrlCellLink(textEditingController.text);
context.pop();
},
icon: FlowySvgs.url_s,
text: LocaleKeys.grid_url_launch.tr(),
),
const Divider(height: 8.5, thickness: 0.5),
MobileQuickActionButton(
enable: context.watch<URLCellBloc>().state.content.isNotEmpty,
onTap: () {
Clipboard.setData(
ClipboardData(text: textEditingController.text),
);
Fluttertoast.showToast(
msg: LocaleKeys.grid_url_copiedNotification.tr(),
gravity: ToastGravity.BOTTOM,
);
context.pop();
},
icon: FlowySvgs.copy_s,
text: LocaleKeys.grid_url_copy.tr(),
),
const Divider(height: 8.5, thickness: 0.5),
],
);
}
}
void openUrlCellLink(String content) {
if (RegExp(regexUrl).hasMatch(content)) {
const linkPrefix = [

View File

@ -1,14 +1,9 @@
import 'package:flutter/material.dart';
import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../editable_cell_skeleton/url.dart';
@ -25,53 +20,9 @@ class MobileGridURLCellSkin extends IEditableURLCellSkin {
return BlocSelector<URLCellBloc, URLCellState, String>(
selector: (state) => state.content,
builder: (context, content) {
if (content.isEmpty) {
return TextField(
focusNode: focusNode,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 14,
vertical: 12,
),
isCollapsed: true,
),
onTapOutside: (event) =>
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: (value) => bloc.add(URLCellEvent.updateURL(value)),
);
}
return GestureDetector(
onTap: () {
if (content.isEmpty) {
return;
}
final shouldAddScheme = !['http', 'https']
.any((pattern) => content.startsWith(pattern));
final url = shouldAddScheme ? 'http://$content' : content;
afLaunchUrlString(url);
},
onLongPress: () => showMobileBottomSheet(
context,
title: LocaleKeys.board_mobile_editURL.tr(),
showHeader: true,
showCloseButton: true,
builder: (_) {
final controller = TextEditingController(text: content);
return TextField(
controller: controller,
autofocus: true,
keyboardType: TextInputType.url,
onEditingComplete: () {
bloc.add(URLCellEvent.updateURL(controller.text));
context.pop();
},
);
},
),
onTap: () => _showURLEditor(context, bloc, textEditingController),
behavior: HitTestBehavior.opaque,
child: Container(
alignment: AlignmentDirectional.centerStart,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
@ -92,6 +43,23 @@ class MobileGridURLCellSkin extends IEditableURLCellSkin {
);
}
void _showURLEditor(
BuildContext context,
URLCellBloc bloc,
TextEditingController textEditingController,
) {
showMobileBottomSheet(
context,
showDragHandle: true,
builder: (context) => BlocProvider.value(
value: bloc,
child: MobileURLEditor(
textEditingController: textEditingController,
),
),
);
}
@override
List<GridCellAccessoryBuilder<State<StatefulWidget>>> accessoryBuilder(
GridCellAccessoryBuildContext context,

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
@ -8,7 +7,6 @@ import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.d
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../editable_cell_skeleton/url.dart';
@ -27,17 +25,18 @@ class MobileRowDetailURLCellSkin extends IEditableURLCellSkin {
builder: (context, content) {
return InkWell(
borderRadius: const BorderRadius.all(Radius.circular(14)),
onTap: () {
if (content.isEmpty) {
_showURLEditor(context, bloc, content);
return;
}
final shouldAddScheme = !['http', 'https']
.any((pattern) => content.startsWith(pattern));
final url = shouldAddScheme ? 'http://$content' : content;
afLaunchUrlString(url);
},
onLongPress: () => _showURLEditor(context, bloc, content),
onTap: () => showMobileBottomSheet(
context,
showDragHandle: true,
builder: (_) {
return BlocProvider.value(
value: bloc,
child: MobileURLEditor(
textEditingController: textEditingController,
),
);
},
),
child: Container(
constraints: const BoxConstraints(
minHeight: 48,
@ -77,25 +76,4 @@ class MobileRowDetailURLCellSkin extends IEditableURLCellSkin {
URLCellDataNotifier cellDataNotifier,
) =>
const [];
void _showURLEditor(BuildContext context, URLCellBloc bloc, String content) {
final controller = TextEditingController(text: content);
showMobileBottomSheet(
context,
title: LocaleKeys.board_mobile_editURL.tr(),
showHeader: true,
showCloseButton: true,
builder: (_) {
return TextField(
controller: controller,
autofocus: true,
keyboardType: TextInputType.url,
onEditingComplete: () {
bloc.add(URLCellEvent.updateURL(controller.text));
context.pop();
},
);
},
).then((_) => controller.dispose());
}
}

View File

@ -30,7 +30,7 @@ class FlowyTextField extends StatefulWidget {
final TextStyle? hintStyle;
final InputDecoration? decoration;
final TextAlignVertical? textAlignVertical;
final TextInputAction? textInputAction;
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
const FlowyTextField({
@ -60,7 +60,7 @@ class FlowyTextField extends StatefulWidget {
this.hintStyle,
this.decoration,
this.textAlignVertical,
this.textInputAction,
this.keyboardType = TextInputType.multiline,
this.inputFormatters,
});
@ -145,7 +145,6 @@ class FlowyTextFieldState extends State<FlowyTextField> {
_onChanged(text);
}
},
textInputAction: widget.textInputAction,
onSubmitted: (text) => _onSubmitted(text),
onEditingComplete: widget.onEditingComplete,
minLines: 1,
@ -154,7 +153,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds,
style: widget.textStyle ?? Theme.of(context).textTheme.bodySmall,
textAlignVertical: widget.textAlignVertical ?? TextAlignVertical.center,
keyboardType: TextInputType.multiline,
keyboardType: widget.keyboardType,
inputFormatters: widget.inputFormatters,
decoration: widget.decoration ??
InputDecoration(

View File

@ -178,9 +178,7 @@
"dragRow": "اضغط مطولاً لإعادة ترتيب الصف",
"viewDataBase": "عرض قاعدة البيانات",
"referencePage": "تمت الإشارة إلى هذا {name}",
"addBlockBelow": "إضافة كتلة أدناه",
"urlLaunchAccessory": "فتح في المتصفح",
"urlCopyAccessory": "إنسخ الرابط"
"addBlockBelow": "إضافة كتلة أدناه"
},
"sideBar": {
"closeSidebar": "إغلاق الشريط الجانبي",
@ -625,6 +623,10 @@
"hideComplete": "إخفاء المهام المكتملة",
"showComplete": "إظهار كافة المهام"
},
"url": {
"launch": "فتح في المتصفح",
"copy": "إنسخ الرابط"
},
"menuName": "شبكة",
"referencedGridPrefix": "نظرا ل"
},

View File

@ -176,9 +176,7 @@
"dragRow": "Premeu llargament per reordenar la fila",
"viewDataBase": "Veure base de dades",
"referencePage": "Es fa referència a aquest {nom}",
"addBlockBelow": "Afegeix un bloc a continuació",
"urlLaunchAccessory": "Oberta al navegador",
"urlCopyAccessory": "Copia l'URL"
"addBlockBelow": "Afegeix un bloc a continuació"
},
"sideBar": {
"closeSidebar": "Close sidebar",
@ -573,6 +571,10 @@
"addNew": "Afegeix un element",
"submitNewTask": "Crear"
},
"url": {
"launch": "Oberta al navegador",
"copy": "Copia l'URL"
},
"menuName": "Quadrícula",
"referencedGridPrefix": "Vista de"
},

View File

@ -177,9 +177,7 @@
"dragRow": "Gedrückt halten, um die Zeile neu anzuordnen",
"viewDataBase": "Datenbank ansehen",
"referencePage": "Auf diesen {Name} wird verwiesen",
"addBlockBelow": "Einen Block unten hinzufügen",
"urlLaunchAccessory": "Im Browser öffnen",
"urlCopyAccessory": "Webadresse kopieren."
"addBlockBelow": "Einen Block unten hinzufügen"
},
"sideBar": {
"closeSidebar": "Seitenleiste schließen",
@ -639,6 +637,10 @@
"hideComplete": "Blende abgeschlossene Aufgaben aus",
"showComplete": "Zeige alle Aufgaben"
},
"url": {
"launch": "Im Browser öffnen",
"copy": "Webadresse kopieren"
},
"menuName": "Raster",
"referencedGridPrefix": "Sicht von"
},

View File

@ -198,9 +198,7 @@
"dragRow": "Long press to reorder the row",
"viewDataBase": "View database",
"referencePage": "This {name} is referenced",
"addBlockBelow": "Add a block below",
"urlLaunchAccessory": "Open in browser",
"urlCopyAccessory": "Copy URL"
"addBlockBelow": "Add a block below"
},
"sideBar": {
"closeSidebar": "Close side bar",
@ -732,6 +730,12 @@
"hideComplete": "Hide completed tasks",
"showComplete": "Show all tasks"
},
"url": {
"launch": "Open link in browser",
"copy": "Copy link to clipboard",
"textFieldHint": "Enter a URL",
"copiedNotification": "Copied to clipboard!"
},
"relation": {
"relatedDatabasePlaceLabel": "Related Database",
"relatedDatabasePlaceholder": "None",

View File

@ -180,9 +180,7 @@
"dragRow": "Pulsación larga para reordenar la fila",
"viewDataBase": "Ver base de datos",
"referencePage": "Se hace referencia a este {nombre}",
"addBlockBelow": "Añadir un bloque a continuación",
"urlLaunchAccessory": "Abrir en el navegador",
"urlCopyAccessory": "Copiar URL"
"addBlockBelow": "Añadir un bloque a continuación"
},
"sideBar": {
"closeSidebar": "Cerrar panel lateral",
@ -627,6 +625,10 @@
"hideComplete": "Ocultar tareas completadas",
"showComplete": "Mostrar todas las tareas"
},
"url": {
"launch": "Abrir en el navegador",
"copy": "Copiar URL"
},
"menuName": "Cuadrícula",
"referencedGridPrefix": "Vista de"
},

View File

@ -182,9 +182,7 @@
"dragRow": "Appuyez longuement pour réorganiser la ligne",
"viewDataBase": "Voir la base de données",
"referencePage": "Ce {nom} est référencé",
"addBlockBelow": "Ajouter un bloc ci-dessous",
"urlLaunchAccessory": "Ouvrir dans le navigateur",
"urlCopyAccessory": "Copier l'URL"
"addBlockBelow": "Ajouter un bloc ci-dessous"
},
"sideBar": {
"closeSidebar": "Fermer le menu latéral",
@ -659,6 +657,10 @@
"hideComplete": "Cacher les tâches terminées",
"showComplete": "Afficher toutes les tâches"
},
"url": {
"launch": "Ouvrir dans le navigateur",
"copy": "Copier l'URL"
},
"menuName": "Grille",
"referencedGridPrefix": "Vue",
"calculate": "Calculer",
@ -1264,4 +1266,4 @@
"userIcon": "Icône utilisateur"
},
"noLogFiles": "Il n'y a pas de log"
}
}

View File

@ -187,9 +187,7 @@
"dragRow": "Appuyez longuement pour réorganiser la ligne",
"viewDataBase": "Voir la base de données",
"referencePage": "Ce {nom} est référencé",
"addBlockBelow": "Ajouter un bloc ci-dessous",
"urlLaunchAccessory": "Ouvrir dans le navigateur",
"urlCopyAccessory": "Copier l'URL"
"addBlockBelow": "Ajouter un bloc ci-dessous"
},
"sideBar": {
"closeSidebar": "Fermer le menu latéral",
@ -703,6 +701,10 @@
"hideComplete": "Cacher les tâches terminées",
"showComplete": "Afficher toutes les tâches"
},
"url": {
"launch": "Ouvrir dans le navigateur",
"copy": "Copier l'URL"
},
"relation": {
"inRelatedDatabase": "Dans",
"emptySearchResult": "Aucun enregistrement trouvé"

View File

@ -183,9 +183,7 @@
"dragRow": "Premere a lungo per riordinare la riga",
"viewDataBase": "Visualizza banca dati",
"referencePage": "Questo {nome} è referenziato",
"addBlockBelow": "Aggiungi un blocco qui sotto",
"urlLaunchAccessory": "Apri nel browser",
"urlCopyAccessory": "Copia l'URL"
"addBlockBelow": "Aggiungi un blocco qui sotto"
},
"sideBar": {
"closeSidebar": "Close sidebar",
@ -665,6 +663,10 @@
"hideComplete": "Nascondi le attività completate",
"showComplete": "Mostra tutte le attività"
},
"url": {
"launch": "Apri nel browser",
"copy": "Copia l'URL"
},
"menuName": "Griglia",
"referencedGridPrefix": "Vista di",
"calculate": "Calcolare",

View File

@ -180,9 +180,7 @@
"dragRow": "Pressione e segure para reordenar a linha",
"viewDataBase": "Visualizar banco de dados",
"referencePage": "Esta {name} é uma referência",
"addBlockBelow": "Adicione um bloco abaixo",
"urlLaunchAccessory": "Abrir com o navegador",
"urlCopyAccessory": "Copiar URL"
"addBlockBelow": "Adicione um bloco abaixo"
},
"sideBar": {
"closeSidebar": "Fechar barra lateral",
@ -648,6 +646,10 @@
"hideComplete": "Ocultar tarefas concluídas",
"showComplete": "Mostrar todas as tarefas"
},
"url": {
"launch": "Abrir com o navegador",
"copy": "Copiar URL"
},
"menuName": "Grade",
"referencedGridPrefix": "Vista de"
},

View File

@ -187,9 +187,7 @@
"dragRow": "Перетащите для изменения порядка строк",
"viewDataBase": "Просмотр базы данных",
"referencePage": "Ссылки на {name}",
"addBlockBelow": "Добавьте блок ниже",
"urlLaunchAccessory": "Открыть в браузере",
"urlCopyAccessory": "Скопировать URL"
"addBlockBelow": "Добавьте блок ниже"
},
"sideBar": {
"closeSidebar": "Закрыть боковое меню",
@ -689,6 +687,10 @@
"hideComplete": "Скрыть выполненные задачи",
"showComplete": "Показать все задачи"
},
"url": {
"launch": "Открыть в браузере",
"copy": "Скопировать URL"
},
"relation": {
"relatedDatabasePlaceLabel": "Связанная база данных",
"relatedDatabasePlaceholder": "Пусто",

View File

@ -177,9 +177,7 @@
"dragRow": "กดค้างเพื่อเรียงลำดับแถวใหม่",
"viewDataBase": "ดูฐานข้อมูล",
"referencePage": "{name} ถูกอ้างอิงถึง",
"addBlockBelow": "เพิ่มบล็อกด้านล่าง",
"urlLaunchAccessory": "เปิดในเบราว์เซอร์",
"urlCopyAccessory": "คัดลอก URL"
"addBlockBelow": "เพิ่มบล็อกด้านล่าง"
},
"sideBar": {
"closeSidebar": "ปิดแถบด้านข้าง",
@ -557,7 +555,7 @@
"showHiddenFields": {
"one": "แสดงฟิลด์ที่ซ่อนอยู่ {count} ฟิลด์",
"many": "แสดงฟิลด์ที่ซ่อนอยู่ {count} ฟิลด์",
"other": "แสดงฟิลด์ที่ซ่อนอยู่ {count} ฟิลด์"
"other": "แสดงฟิลด์ที่ซ่อนอยู่ {count} ฟิลด์"
},
"hideHiddenFields": {
"one": "ซ่อนฟิลด์ที่ซ่อนอยู่ {count} ฟิลด์",
@ -614,6 +612,10 @@
"hideComplete": "ซ่อนงานเสร็จ",
"showComplete": "แสดงงานทั้งหมด"
},
"url": {
"launch": "เปิดในเบราว์เซอร์",
"copy": "คัดลอก URL"
},
"menuName": "ตาราง",
"referencedGridPrefix": "มุมมองของ"
},
@ -630,11 +632,11 @@
},
"grid": {
"selectAGridToLinkTo": "เลือกตารางเพื่อเชื่อมโยง",
"createANewGrid": "สร้างตารางใหม่"
"createANewGrid": "สร้างตารางใหม่"
},
"calendar": {
"selectACalendarToLinkTo": "เลือกปฏิทินเพื่อเชื่อมโยง",
"createANewCalendar": "สร้างปฏิทินใหม่"
"createANewCalendar": "สร้างปฏิทินใหม่"
},
"document": {
"selectADocumentToLinkTo": "เลือกเอกสารเพื่อเชื่อมโยง"
@ -764,7 +766,7 @@
},
"stability_ai": {
"label": "สร้างรูปภาพจาก Stability AI",
"placeholder": "โปรดระบุคำขอใช้ Stability AI สร้างรูปภาพ"
"placeholder": "โปรดระบุคำขอใช้ Stability AI สร้างรูปภาพ"
},
"support": "ขนาดรูปภาพจำกัดอยู่ที่ 5MB รูปแบบที่รองรับ: JPEG, PNG, GIF, SVG",
"error": {

View File

@ -175,8 +175,7 @@
"openMenu": "Bấm để mở menu",
"dragRow": "Nhấn và giữ để sắp xếp lại hàng",
"viewDataBase": "Xem cơ sở dữ liệu",
"referencePage": "{name} này được tham chiếu",
"urlCopyAccessory": "Sao chép URL"
"referencePage": "{name} này được tham chiếu"
},
"sideBar": {
"closeSidebar": "Đóng thanh bên",
@ -612,6 +611,9 @@
"searchOrCreateOption": "Tìm kiếm hoặc tạo một tùy chọn...",
"tagName": "Tên thẻ"
},
"url": {
"copy": "Sao chép URL"
},
"menuName": "Lưới"
},
"document": {

View File

@ -187,9 +187,7 @@
"dragRow": "长按重新排序该行",
"viewDataBase": "查看数据库",
"referencePage": "这个 {name} 已被引用",
"addBlockBelow": "在下面添加一个块",
"urlLaunchAccessory": "在浏览器中打开",
"urlCopyAccessory": "复制链接"
"addBlockBelow": "在下面添加一个块"
},
"sideBar": {
"closeSidebar": "关闭侧边栏",
@ -691,6 +689,10 @@
"hideComplete": "隐藏已完成的任务",
"showComplete": "显示所有任务"
},
"url": {
"launch": "在浏览器中打开",
"copy": "复制链接"
},
"relation": {
"emptySearchResult": "无结果"
},

View File

@ -181,9 +181,7 @@
"dragRow": "長按以重新排序列",
"viewDataBase": "檢視資料庫",
"referencePage": "這個 {name} 已被引用",
"addBlockBelow": "在下方新增一個區塊",
"urlLaunchAccessory": "在瀏覽器中開啟",
"urlCopyAccessory": "複製網址"
"addBlockBelow": "在下方新增一個區塊"
},
"sideBar": {
"closeSidebar": "關閉側欄",
@ -650,6 +648,10 @@
"hideComplete": "隱藏已完成任務",
"showComplete": "顯示所有任務"
},
"url": {
"launch": "在瀏覽器中開啟",
"copy": "複製網址"
},
"menuName": "網格",
"referencedGridPrefix": "檢視",
"calculate": "計算",