fix: the name is too long to publish

This commit is contained in:
Lucas.Xu
2024-06-28 10:24:03 +08:00
parent e51b0f90de
commit 2f9f2c1ba7
5 changed files with 75 additions and 32 deletions

View File

@ -116,11 +116,11 @@
{ {
"name": "AF-desktop: Debug Rust", "name": "AF-desktop: Debug Rust",
"type": "lldb", "type": "lldb",
"request": "attach", // "request": "attach",
"pid": "${command:pickMyProcess}" // "pid": "${command:pickMyProcess}"
// To launch the application directly, use the following configuration: // To launch the application directly, use the following configuration:
// "request": "launch", "request": "launch",
// "program": "[YOUR_APPLICATION_PATH]", "program": "/Users/lucas.xu/Desktop/development/AppFlowy/frontend/appflowy_flutter/build/macos/Build/Products/Debug/AppFlowy.app",
}, },
{ {
// https://tauri.app/v1/guides/debugging/vs-code // https://tauri.app/v1/guides/debugging/vs-code

View File

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:appflowy/workspace/application/export/document_exporter.dart'; import 'package:appflowy/workspace/application/export/document_exporter.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
@ -12,7 +13,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'document_share_bloc.freezed.dart'; part 'document_share_bloc.freezed.dart';
const _url = 'https://test.appflowy.io'; const _url = 'https://test.appflowy.com';
class DocumentShareBloc extends Bloc<DocumentShareEvent, DocumentShareState> { class DocumentShareBloc extends Bloc<DocumentShareEvent, DocumentShareState> {
DocumentShareBloc({ DocumentShareBloc({
@ -21,16 +22,30 @@ class DocumentShareBloc extends Bloc<DocumentShareEvent, DocumentShareState> {
on<DocumentShareEvent>((event, emit) async { on<DocumentShareEvent>((event, emit) async {
await event.when( await event.when(
initial: () async { initial: () async {
viewListener = ViewListener(viewId: view.id)
..start(
onViewUpdated: (value) {
add(DocumentShareEvent.updateViewName(value.name));
},
);
final publishInfo = await ViewBackendService.getPublishInfo(view); final publishInfo = await ViewBackendService.getPublishInfo(view);
publishInfo.fold((s) { publishInfo.fold((s) {
emit( emit(
state.copyWith( state.copyWith(
isPublished: true, isPublished: true,
url: '$_url/${s.namespace}/${s.publishName}', url: '$_url/${s.namespace}/${s.publishName}',
viewName: view.name,
), ),
); );
}, (f) { }, (f) {
emit(state.copyWith(isPublished: false, url: '')); emit(
state.copyWith(
isPublished: false,
url: '',
viewName: view.name,
),
);
}); });
}, },
share: (type, path) async { share: (type, path) async {
@ -101,14 +116,24 @@ class DocumentShareBloc extends Bloc<DocumentShareEvent, DocumentShareState> {
), ),
); );
}, },
updateViewName: (viewName) async {
emit(state.copyWith(viewName: viewName));
},
); );
}); });
} }
final ViewPB view; final ViewPB view;
late final ViewListener viewListener;
late final exporter = DocumentExporter(view); late final exporter = DocumentExporter(view);
@override
Future<void> close() async {
await viewListener.stop();
return super.close();
}
Future<FlowyResult<ExportDataPB, FlowyError>> _export( Future<FlowyResult<ExportDataPB, FlowyError>> _export(
DocumentShareType type, DocumentShareType type,
String? path, String? path,
@ -181,6 +206,8 @@ class DocumentShareEvent with _$DocumentShareEvent {
String pageId, String pageId,
) = _Publish; ) = _Publish;
const factory DocumentShareEvent.unPublish() = _UnPublish; const factory DocumentShareEvent.unPublish() = _UnPublish;
const factory DocumentShareEvent.updateViewName(String name) =
_UpdateViewName;
} }
@freezed @freezed
@ -191,11 +218,13 @@ class DocumentShareState with _$DocumentShareState {
required bool isPublished, required bool isPublished,
FlowyResult<void, FlowyError>? publishResult, FlowyResult<void, FlowyError>? publishResult,
required String url, required String url,
required String viewName,
}) = _DocumentShareState; }) = _DocumentShareState;
factory DocumentShareState.initial() => const DocumentShareState( factory DocumentShareState.initial() => const DocumentShareState(
isLoading: false, isLoading: false,
isPublished: false, isPublished: false,
url: '', url: '',
viewName: '',
); );
} }

View File

@ -0,0 +1,20 @@
import 'dart:math';
import 'package:flowy_infra/uuid.dart';
final _regExp = RegExp(r'[^\w\-\.@:/]');
Future<String> generateNameSpace() async {
const workspaceName = '';
final id = uuid().substring(0, 8);
return '$workspaceName$id'.replaceAll(_regExp, '-');
}
// The backend limits the publish name to a maximum of 50 characters.
// If the combined length of the ID and the name exceeds 50 characters,
// we will truncate the name to ensure the final result is within the limit.
// The name should only contain alphanumeric characters and hyphens.
Future<String> generatePublishName(String id, String name) async {
final result = '${name.substring(0, min(49 - id.length, name.length))}-$id';
return result.replaceAll(_regExp, '-');
}

View File

@ -1,17 +1,15 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_share_bloc.dart'; import 'package:appflowy/plugins/document/application/document_share_bloc.dart';
import 'package:appflowy/plugins/document/presentation/share/publish_name_generator.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/rounded_button.dart'; import 'package:flowy_infra_ui/widget/rounded_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
final _regExp = RegExp(r'[^\w\-\.@:/]');
class PublishTab extends StatelessWidget { class PublishTab extends StatelessWidget {
const PublishTab({super.key}); const PublishTab({super.key});
@ -40,8 +38,12 @@ class PublishTab extends StatelessWidget {
) )
: _UnPublishWidget( : _UnPublishWidget(
onPublish: () async { onPublish: () async {
final publishName = await _generatePublishName(context); final id = context.read<DocumentShareBloc>().view.id;
final nameSpace = await _generateNameSpace(); final publishName = await generatePublishName(
id,
state.viewName,
);
final nameSpace = await generateNameSpace();
if (context.mounted) { if (context.mounted) {
context.read<DocumentShareBloc>().add( context.read<DocumentShareBloc>().add(
DocumentShareEvent.publish(nameSpace, publishName), DocumentShareEvent.publish(nameSpace, publishName),
@ -52,18 +54,6 @@ class PublishTab extends StatelessWidget {
}, },
); );
} }
Future<String> _generateNameSpace() async {
const workspaceName = '';
final id = uuid().substring(0, 8);
return '$workspaceName$id'.replaceAll(_regExp, '_');
}
Future<String> _generatePublishName(BuildContext context) async {
final publishName = context.read<DocumentShareBloc>().view.name;
final id = uuid().substring(0, 8);
return '$publishName$id'.replaceAll(_regExp, '');
}
} }
class _PublishedWidget extends StatefulWidget { class _PublishedWidget extends StatefulWidget {

View File

@ -1,20 +1,24 @@
use crate::entities::ViewPB; use crate::entities::ViewPB;
use flowy_folder_pub::entities::PublishViewInfo; use flowy_folder_pub::entities::PublishViewInfo;
use regex::Regex; use regex::Regex;
use tracing::trace;
fn replace_invalid_url_chars(input: &str) -> String { fn replace_invalid_url_chars(input: &str) -> String {
let re = Regex::new(r"[^\w-]").unwrap(); let regex = Regex::new(r"[^\w-]").unwrap();
regex.replace_all(input, "-").to_string()
}
let replaced = re.replace_all(input, "_").to_string();
if replaced.len() > 20 {
replaced[..20].to_string()
} else {
replaced
}
}
pub fn generate_publish_name(id: &str, name: &str) -> String { pub fn generate_publish_name(id: &str, name: &str) -> String {
let id_len = id.len();
let name = replace_invalid_url_chars(name); let name = replace_invalid_url_chars(name);
format!("{}-{}", name, id) let name_len = name.len();
// The backend limits the publish name to a maximum of 50 characters.
// If the combined length of the ID and the name exceeds 50 characters,
// we will truncate the name to ensure the final result is within the limit.
// The name should only contain alphanumeric characters and hyphens.
let result = format!("{}-{}", &name[..std::cmp::min(49 - id_len, name_len)], id);
trace!("generate_publish_name: {}", result);
result
} }
pub fn view_pb_to_publish_view(view: &ViewPB) -> PublishViewInfo { pub fn view_pb_to_publish_view(view: &ViewPB) -> PublishViewInfo {