Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom share url, publish url and copy link to share #7061

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void main() {
);

final shareValues = plainText!
.replaceAll('https://${ShareConstants.shareBaseUrl}/', '')
.replaceAll('https://${ShareConstants.baseWebDomain}/app/', '')
.split('/');
final workspaceId = shareValues[0];
expect(workspaceId, isNotEmpty);
Expand Down
1 change: 1 addition & 0 deletions frontend/appflowy_flutter/lib/core/config/kv_keys.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class KVKeys {

static const String kCloudType = 'kCloudType';
static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL';
static const String kAppFlowyBaseShareDomain = 'kAppFlowyBaseShareDomain';
static const String kAppFlowyEnableSyncTrace = 'kAppFlowyEnableSyncTrace';

/// The key for saving the text scale factor.
Expand Down
11 changes: 11 additions & 0 deletions frontend/appflowy_flutter/lib/env/backend_env.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// ignore_for_file: non_constant_identifier_names

import 'package:appflowy/plugins/shared/share/constants.dart';
import 'package:json_annotation/json_annotation.dart';

part 'backend_env.g.dart';

@JsonSerializable()
Expand Down Expand Up @@ -40,6 +42,7 @@ class AppFlowyCloudConfiguration {
required this.ws_base_url,
required this.gotrue_url,
required this.enable_sync_trace,
required this.base_web_domain,
});

factory AppFlowyCloudConfiguration.fromJson(Map<String, dynamic> json) =>
Expand All @@ -50,6 +53,13 @@ class AppFlowyCloudConfiguration {
final String gotrue_url;
final bool enable_sync_trace;

/// The base domain is used in
///
/// - Share URL
/// - Publish URL
/// - Copy Link To Block
final String base_web_domain;

Map<String, dynamic> toJson() => _$AppFlowyCloudConfigurationToJson(this);

static AppFlowyCloudConfiguration defaultConfig() {
Expand All @@ -58,6 +68,7 @@ class AppFlowyCloudConfiguration {
ws_base_url: '',
gotrue_url: '',
enable_sync_trace: false,
base_web_domain: ShareConstants.baseWebDomain,
);
}

Expand Down
28 changes: 27 additions & 1 deletion frontend/appflowy_flutter/lib/env/cloud_env.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/env/backend_env.dart';
import 'package:appflowy/env/env.dart';
import 'package:appflowy/plugins/shared/share/constants.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy_backend/log.dart';

Expand Down Expand Up @@ -155,6 +156,13 @@ Future<void> _setAppFlowyCloudUrl(String? url) async {
await getIt<KeyValueStorage>().set(KVKeys.kAppflowyCloudBaseURL, url ?? '');
}

Future<void> useBaseWebDomain(String? url) async {
await getIt<KeyValueStorage>().set(
KVKeys.kAppFlowyBaseShareDomain,
url ?? ShareConstants.baseWebDomain,
);
}

Future<void> useSelfHostedAppFlowyCloudWithURL(String url) async {
await _setAuthenticatorType(AuthenticatorType.appflowyCloudSelfHost);
await _setAppFlowyCloudUrl(url);
Expand Down Expand Up @@ -213,6 +221,7 @@ class AppFlowyCloudSharedEnv {
ws_base_url: await _getAppFlowyCloudWSUrl(Env.afCloudUrl),
gotrue_url: await _getAppFlowyCloudGotrueUrl(Env.afCloudUrl),
enable_sync_trace: false,
base_web_domain: Env.baseWebDomain,
);

return AppFlowyCloudSharedEnv(
Expand All @@ -233,6 +242,7 @@ Future<AppFlowyCloudConfiguration> configurationFromUri(
Uri baseUri,
String baseUrl,
AuthenticatorType authenticatorType,
String baseShareDomain,
) async {
// In development mode, the app is configured to access the AppFlowy cloud server directly through specific ports.
// This setup bypasses the need for Nginx, meaning that the AppFlowy cloud should be running without an Nginx server
Expand All @@ -243,13 +253,17 @@ Future<AppFlowyCloudConfiguration> configurationFromUri(
ws_base_url: "ws://${baseUri.host}:8000/ws/v1",
gotrue_url: "$baseUrl:9999",
enable_sync_trace: true,
base_web_domain: ShareConstants.testBaseWebDomain,
);
} else {
return AppFlowyCloudConfiguration(
base_url: baseUrl,
ws_base_url: await _getAppFlowyCloudWSUrl(baseUrl),
gotrue_url: await _getAppFlowyCloudGotrueUrl(baseUrl),
enable_sync_trace: await getSyncLogEnabled(),
base_web_domain: authenticatorType == AuthenticatorType.appflowyCloud
? ShareConstants.baseWebDomain
: baseShareDomain,
);
}
}
Expand All @@ -258,10 +272,16 @@ Future<AppFlowyCloudConfiguration> getAppFlowyCloudConfig(
AuthenticatorType authenticatorType,
) async {
final baseURL = await getAppFlowyCloudUrl();
final baseShareDomain = await getBaseShareDomain();

try {
final uri = Uri.parse(baseURL);
return await configurationFromUri(uri, baseURL, authenticatorType);
return await configurationFromUri(
uri,
baseURL,
authenticatorType,
baseShareDomain,
);
} catch (e) {
Log.error("Failed to parse AppFlowy Cloud URL: $e");
return AppFlowyCloudConfiguration.defaultConfig();
Expand All @@ -274,6 +294,12 @@ Future<String> getAppFlowyCloudUrl() async {
return result ?? kAppflowyCloudUrl;
}

Future<String> getBaseShareDomain() async {
final result =
await getIt<KeyValueStorage>().get(KVKeys.kAppFlowyBaseShareDomain);
return result ?? ShareConstants.baseWebDomain;
}

Future<bool> getSyncLogEnabled() async {
final result =
await getIt<KeyValueStorage>().get(KVKeys.kAppFlowyEnableSyncTrace);
Expand Down
8 changes: 8 additions & 0 deletions frontend/appflowy_flutter/lib/env/env.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// lib/env/env.dart
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/plugins/shared/share/constants.dart';
import 'package:envied/envied.dart';

part 'env.g.dart';
Expand Down Expand Up @@ -43,4 +44,11 @@ abstract class Env {
defaultValue: '',
)
static const String sentryDsn = _Env.sentryDsn;

@EnviedField(
obfuscate: false,
varName: 'BASE_WEB_DOMAIN',
defaultValue: ShareConstants.baseWebDomain,
)
static const String baseWebDomain = _Env.baseWebDomain;
}
36 changes: 30 additions & 6 deletions frontend/appflowy_flutter/lib/plugins/shared/share/constants.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/startup/startup.dart';

class ShareConstants {
static const String publishBaseUrl = 'appflowy.com';
static const String shareBaseUrl = 'appflowy.com/app';
static const String baseWebDomain = 'appflowy.com';
static const String testBaseWebDomain = 'test.appflowy.com';

static String buildPublishUrl({
required String nameSpace,
required String publishName,
}) {
return 'https://$publishBaseUrl/$nameSpace/$publishName';
final baseShareDomain =
getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.base_web_domain;
final url = '$baseShareDomain/$nameSpace/$publishName'.addSchemaIfNeeded();
return url;
}

static String buildNamespaceUrl({
required String nameSpace,
bool withHttps = false,
}) {
final url = withHttps ? 'https://$publishBaseUrl' : publishBaseUrl;
return '$url/$nameSpace';
final baseShareDomain =
getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.base_web_domain;
String url = baseShareDomain.addSchemaIfNeeded();
if (!withHttps) {
url = url.replaceFirst('https://', '');
}
return '$url/app/$nameSpace';
}

static String buildShareUrl({
required String workspaceId,
required String viewId,
String? blockId,
}) {
final url = 'https://$shareBaseUrl/$workspaceId/$viewId';
final baseShareDomain =
getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.base_web_domain;
final url = '$baseShareDomain/app/$workspaceId/$viewId'.addSchemaIfNeeded();
if (blockId == null || blockId.isEmpty) {
return url;
}
return '$url?blockId=$blockId';
}
}

extension on String {
String addSchemaIfNeeded() {
final schema = Uri.parse(this).scheme;
// if the schema is empty, add https schema by default
if (schema.isEmpty) {
return 'https://$this';
}
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ class AppFlowyCloudURLsBloc
),
);
},
updateBaseWebDomain: (url) {
emit(
state.copyWith(
updatedBaseWebDomain: url,
urlError: null,
showRestartHint: url.isNotEmpty,
),
);
},
confirmUpdate: () async {
if (state.updatedServerUrl.isEmpty) {
emit(
Expand All @@ -35,13 +44,27 @@ class AppFlowyCloudURLsBloc
),
);
} else {
validateUrl(state.updatedServerUrl).fold(
bool isSuccess = false;

await validateUrl(state.updatedServerUrl).fold(
(url) async {
await useSelfHostedAppFlowyCloudWithURL(url);
add(const AppFlowyCloudURLsEvent.didSaveConfig());
isSuccess = true;
},
(err) async => emit(state.copyWith(urlError: err)),
);

await validateUrl(state.updatedBaseWebDomain).fold(
(url) async {
await useBaseWebDomain(url);
isSuccess = true;
},
(err) => emit(state.copyWith(urlError: err)),
(err) async => emit(state.copyWith(urlError: err)),
);

if (isSuccess) {
add(const AppFlowyCloudURLsEvent.didSaveConfig());
}
}
},
didSaveConfig: () {
Expand All @@ -62,6 +85,8 @@ class AppFlowyCloudURLsEvent with _$AppFlowyCloudURLsEvent {
const factory AppFlowyCloudURLsEvent.initial() = _Initial;
const factory AppFlowyCloudURLsEvent.updateServerUrl(String text) =
_ServerUrl;
const factory AppFlowyCloudURLsEvent.updateBaseWebDomain(String text) =
_UpdateBaseWebDomain;
const factory AppFlowyCloudURLsEvent.confirmUpdate() = _UpdateConfig;
const factory AppFlowyCloudURLsEvent.didSaveConfig() = _DidSaveConfig;
}
Expand All @@ -71,6 +96,7 @@ class AppFlowyCloudURLsState with _$AppFlowyCloudURLsState {
const factory AppFlowyCloudURLsState({
required AppFlowyCloudConfiguration config,
required String updatedServerUrl,
required String updatedBaseWebDomain,
required String? urlError,
required bool restartApp,
required bool showRestartHint,
Expand All @@ -81,6 +107,8 @@ class AppFlowyCloudURLsState with _$AppFlowyCloudURLsState {
urlError: null,
updatedServerUrl:
getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.base_url,
updatedBaseWebDomain:
getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.base_web_domain,
showRestartHint: getIt<AppFlowyCloudSharedEnv>()
.appflowyCloudConfig
.base_url
Expand Down
Loading
Loading