diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx
index a671c6ef43..d454f7b9f0 100644
--- a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx
+++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx
@@ -100,7 +100,7 @@ export const DatabaseBlock = memo(
>
{notFound ? (
<>
-
{
if (disableClick) return;
try {
diff --git a/frontend/appflowy_web_app/src/components/publish/header/PublishViewHeader.tsx b/frontend/appflowy_web_app/src/components/publish/header/PublishViewHeader.tsx
index 2d3187f27c..4d3221249b 100644
--- a/frontend/appflowy_web_app/src/components/publish/header/PublishViewHeader.tsx
+++ b/frontend/appflowy_web_app/src/components/publish/header/PublishViewHeader.tsx
@@ -1,6 +1,8 @@
import { usePublishContext } from '@/application/publish';
+import { openOrDownload } from '@/components/publish/header/utils';
import React, { useMemo } from 'react';
import Breadcrumb from './Breadcrumb';
+import { ReactComponent as Logo } from '@/assets/logo.svg';
export function PublishViewHeader() {
const viewMeta = usePublishContext()?.viewMeta;
@@ -24,9 +26,12 @@ export function PublishViewHeader() {
}, [viewMeta]);
return (
-
+
);
diff --git a/frontend/appflowy_web_app/src/components/publish/header/utils.ts b/frontend/appflowy_web_app/src/components/publish/header/utils.ts
new file mode 100644
index 0000000000..05cf87e9cc
--- /dev/null
+++ b/frontend/appflowy_web_app/src/components/publish/header/utils.ts
@@ -0,0 +1,99 @@
+export function openOrDownload() {
+ const getDeviceType = () => {
+ const ua = navigator.userAgent;
+
+ if (/(iPad|iPhone|iPod)/g.test(ua)) {
+ return 'iOS';
+ } else if (/Android/g.test(ua)) {
+ return 'Android';
+ } else {
+ return 'Desktop';
+ }
+ };
+
+ const deviceType = getDeviceType();
+ const isMobile = deviceType !== 'Desktop';
+ const getFallbackLink = () => {
+ if (deviceType === 'iOS') {
+ return 'https://testflight.apple.com/join/6CexvkDz';
+ } else if (deviceType === 'Android') {
+ return 'https://play.google.com/store/apps/details?id=io.appflowy.appflowy';
+ } else {
+ return 'https://appflowy.io/download/#pop';
+ }
+ };
+
+ const getDuration = () => {
+ switch (deviceType) {
+ case 'iOS':
+ return 250;
+ default:
+ return 1500;
+ }
+ };
+
+ const APPFLOWY_SCHEME = 'appflowy-flutter://';
+
+ const iframe = document.createElement('iframe');
+
+ iframe.style.display = 'none';
+ iframe.src = APPFLOWY_SCHEME;
+
+ const openSchema = () => {
+ if (isMobile) return (window.location.href = APPFLOWY_SCHEME);
+ document.body.appendChild(iframe);
+ setTimeout(() => {
+ document.body.removeChild(iframe);
+ }, 1000);
+ };
+
+ const openAppFlowy = () => {
+ openSchema();
+
+ const initialTime = Date.now();
+ let interactTime = initialTime;
+ let waitTime = 0;
+ const duration = getDuration();
+
+ const updateInteractTime = () => {
+ interactTime = Date.now();
+ };
+
+ document.removeEventListener('mousemove', updateInteractTime);
+ document.removeEventListener('mouseenter', updateInteractTime);
+
+ const checkOpen = setInterval(() => {
+ waitTime = Date.now() - initialTime;
+
+ if (waitTime > duration) {
+ clearInterval(checkOpen);
+ if (isMobile || Date.now() - interactTime < duration) {
+ window.open(getFallbackLink(), '_current');
+ }
+ }
+ }, 20);
+
+ if (!isMobile) {
+ document.addEventListener('mouseenter', updateInteractTime);
+ document.addEventListener('mousemove', updateInteractTime);
+ }
+
+ document.addEventListener('visibilitychange', () => {
+ const isHidden = document.hidden;
+
+ if (isHidden) {
+ clearInterval(checkOpen);
+ }
+ });
+
+ window.onpagehide = () => {
+ clearInterval(checkOpen);
+ };
+
+ window.onbeforeunload = () => {
+ clearInterval(checkOpen);
+ };
+ };
+
+ openAppFlowy();
+}