feat: last publish name (#6766)

* chore: wip update client api

* feat: add unpublished information

* feat: keep the last publish name

* test: add re-publish test

* test: add empty name test

* chore: update cloud version to 0.7.6

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Zack 2024-11-13 20:36:35 +08:00 committed by GitHub
parent cd3be696dc
commit 3b304747f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 166 additions and 50 deletions

View File

@ -22,7 +22,6 @@ on:
env:
FLUTTER_VERSION: "3.22.0"
RUST_TOOLCHAIN: "1.80.1"
CLOUD_VERSION: 0.6.51
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}

View File

@ -20,7 +20,7 @@ on:
env:
CARGO_TERM_COLOR: always
CLOUD_VERSION: 0.6.51-amd64
CLOUD_VERSION: 0.7.6-amd64
RUST_TOOLCHAIN: "1.77.2"
jobs:

View File

@ -106,9 +106,21 @@ void main() {
await tester.pumpUntilFound(errorToast2);
await tester.pumpUntilNotFound(errorToast2);
// rename with empty name
await tester.tap(inputField);
await tester.enterText(inputField, '');
await tester.tapButton(find.text(LocaleKeys.button_save.tr()));
await tester.pumpAndSettle();
// expect to see the toast with error message
final errorToast3 = find.text(
LocaleKeys.settings_sites_error_publishNameCannotBeEmpty.tr(),
);
await tester.pumpUntilFound(errorToast3);
await tester.pumpUntilNotFound(errorToast3);
// input the new path name
await tester.tap(inputField);
await tester.enterText(inputField, 'new-path-name');
// click save button
await tester.tapButton(find.text(LocaleKeys.button_save.tr()));
@ -137,5 +149,72 @@ void main() {
isTrue,
);
});
testWidgets('re-publish the document', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,
);
await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
const pageName = 'Document';
await tester.createNewPageInSpace(
spaceName: Constants.generalSpaceName,
layout: ViewLayoutPB.Document,
pageName: pageName,
);
// open the publish menu
await tester.openPublishMenu();
// publish the document
final publishButton = find.byType(PublishButton);
await tester.tapButton(publishButton);
// rename the path name
final inputField = find.descendant(
of: find.byType(ShareMenu),
matching: find.byType(TextField),
);
// input the new path name
const newName = 'new-path-name';
await tester.enterText(inputField, newName);
// click save button
await tester.tapButton(find.text(LocaleKeys.button_save.tr()));
await tester.pumpAndSettle();
// expect to see the toast with success message
final successToast = find.text(
LocaleKeys.settings_sites_success_updatePathNameSuccess.tr(),
);
await tester.pumpUntilNotFound(successToast);
// unpublish the document
final unpublishButton = find.byType(UnPublishButton);
await tester.tapButton(unpublishButton);
final unpublishSuccessToast = find.text(
LocaleKeys.publish_unpublishSuccessfully.tr(),
);
await tester.pumpUntilNotFound(unpublishSuccessToast);
// re-publish the document
await tester.tapButton(publishButton);
// expect to see the toast with success message
final rePublishSuccessToast = find.text(
LocaleKeys.publish_publishSuccessfully.tr(),
);
await tester.pumpUntilNotFound(rePublishSuccessToast);
// check the clipboard has the link
final content = await Clipboard.getData(Clipboard.kTextPlain);
expect(
content?.text?.contains(newName),
isTrue,
);
});
});
}

View File

@ -9,6 +9,7 @@ import 'package:appflowy/plugins/shared/share/constants.dart';
import 'package:appflowy/plugins/shared/share/publish_name_generator.dart';
import 'package:appflowy/plugins/shared/share/share_bloc.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/string_extension.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/view/prelude.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
@ -97,9 +98,12 @@ class MobileViewPageMoreBottomSheet extends StatelessWidget {
Future<void> _publish(BuildContext context) async {
final id = context.read<ShareBloc>().view.id;
final publishName = await generatePublishName(
id,
view.name,
final lastPublishName = context.read<ShareBloc>().state.pathName;
final publishName = lastPublishName.orDefault(
await generatePublishName(
id,
view.name,
),
);
if (context.mounted) {
context.read<ShareBloc>().add(

View File

@ -9,6 +9,7 @@ import 'package:appflowy/plugins/shared/share/publish_name_generator.dart';
import 'package:appflowy/plugins/shared/share/share_bloc.dart';
import 'package:appflowy/shared/error_code/error_code_map.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/string_extension.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/log.dart';
@ -50,9 +51,12 @@ class PublishTab extends StatelessWidget {
return _PublishWidget(
onPublish: (selectedViews) async {
final id = context.read<ShareBloc>().view.id;
final publishName = await generatePublishName(
id,
viewName,
final lastPublishName = context.read<ShareBloc>().state.pathName;
final publishName = lastPublishName.orDefault(
await generatePublishName(
id,
viewName,
),
);
if (selectedViews.isNotEmpty) {

View File

@ -7,6 +7,7 @@ import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
@ -188,39 +189,47 @@ class ShareBloc extends Bloc<ShareEvent, ShareState> {
(v) => v.authenticator == AuthenticatorPB.AppFlowyCloud,
(p) => false,
);
Log.info(
'get publish info: $publishInfo for view: ${view.name}(${view.id})',
);
String workspaceId = state.workspaceId;
if (workspaceId.isEmpty) {
workspaceId = await UserBackendService.getCurrentWorkspace()
.fold((s) => s.id, (f) => '');
workspaceId = await UserBackendService.getCurrentWorkspace().fold(
(s) => s.id,
(f) => '',
);
}
publishInfo.fold((s) {
emit(
state.copyWith(
isPublished: true,
namespace: s.namespace,
pathName: s.publishName,
url: ShareConstants.buildPublishUrl(
final (isPublished, namespace, pathName, url) = publishInfo.fold(
(s) {
return (
// if the unpublishedAtTimestampSec is not set, it means the view is not unpublished.
!s.hasUnpublishedAtTimestampSec(),
s.namespace,
s.publishName,
ShareConstants.buildPublishUrl(
nameSpace: s.namespace,
publishName: s.publishName,
),
viewName: view.name,
enablePublish: enablePublish,
workspaceId: workspaceId,
viewId: view.id,
),
);
}, (f) {
emit(
state.copyWith(
isPublished: false,
url: '',
viewName: view.name,
enablePublish: enablePublish,
workspaceId: workspaceId,
viewId: view.id,
),
);
});
);
},
(f) => (false, '', '', ''),
);
emit(
state.copyWith(
isPublished: isPublished,
namespace: namespace,
pathName: pathName,
url: url,
viewName: view.name,
enablePublish: enablePublish,
workspaceId: workspaceId,
viewId: view.id,
),
);
}
Future<void> _updatePathName(
@ -232,6 +241,21 @@ class ShareBloc extends Bloc<ShareEvent, ShareState> {
updatePathNameResult: null,
),
);
if (pathName.isEmpty) {
emit(
state.copyWith(
updatePathNameResult: FlowyResult.failure(
FlowyError(
code: ErrorCode.ViewNameInvalid,
msg: 'Path name is invalid',
),
),
),
);
return;
}
final request = SetPublishNamePB()
..viewId = view.id
..newName = pathName;

View File

@ -14,6 +14,8 @@ extension PublishNameErrorCodeMap on ErrorCode {
LocaleKeys.settings_sites_error_publishNameTooLong.tr(),
ErrorCode.UserUnauthorized =>
LocaleKeys.settings_sites_error_publishPermissionDenied.tr(),
ErrorCode.ViewNameInvalid =>
LocaleKeys.settings_sites_error_publishNameCannotBeEmpty.tr(),
_ => null,
};
}

View File

@ -487,7 +487,8 @@
"publishNameTooLong": "The path name is too long, please try another one",
"publishNameAlreadyInUse": "The path name is already in use, please try another one",
"namespaceContainsInvalidCharacters": "The namespace contains invalid character(s), please try another one",
"publishPermissionDenied": "Only the workspace owner or page publisher can manage the publish settings"
"publishPermissionDenied": "Only the workspace owner or page publisher can manage the publish settings",
"publishNameCannotBeEmpty": "The path name cannot be empty, please try another one"
},
"success": {
"namespaceUpdated": "Updated namespace successfully",

View File

@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"bincode",
@ -183,7 +183,7 @@ dependencies = [
[[package]]
name = "appflowy-ai-client"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"bytes",
@ -780,7 +780,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"again",
"anyhow",
@ -835,7 +835,7 @@ dependencies = [
[[package]]
name = "client-api-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"collab-entity",
"collab-rt-entity",
@ -848,7 +848,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"futures-channel",
"futures-util",
@ -1117,7 +1117,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"bincode",
@ -1142,7 +1142,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"async-trait",
@ -1537,7 +1537,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"app-error",
@ -2979,7 +2979,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"futures-util",
@ -2996,7 +2996,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"app-error",
@ -3358,7 +3358,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"bytes",
@ -5663,7 +5663,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a#e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ed4c14d53aa77496a68df0875f5f668f278a21bf#ed4c14d53aa77496a68df0875f5f668f278a21bf"
dependencies = [
"anyhow",
"app-error",

View File

@ -106,8 +106,8 @@ dashmap = "6.0.1"
# Run the script.add_workspace_members:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a" }
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "e31e541d07b8ef5e3f33b0b3dd54ebc22a79f86a" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ed4c14d53aa77496a68df0875f5f668f278a21bf" }
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ed4c14d53aa77496a68df0875f5f668f278a21bf" }
[profile.dev]
opt-level = 0

View File

@ -74,6 +74,8 @@ pub struct PublishInfoResponsePB {
pub publisher_email: String,
#[pb(index = 5)]
pub publish_timestamp_sec: i64,
#[pb(index = 6, one_of)]
pub unpublished_at_timestamp_sec: Option<i64>,
}
impl From<PublishInfo> for PublishInfoResponsePB {
@ -84,6 +86,7 @@ impl From<PublishInfo> for PublishInfoResponsePB {
namespace: Some(info.namespace),
publisher_email: info.publisher_email,
publish_timestamp_sec: info.publish_timestamp.timestamp(),
unpublished_at_timestamp_sec: info.unpublished_timestamp.map(|t| t.timestamp()),
}
}
}