Skip to content

Commit 196b4a8

Browse files
authored
Merge pull request #105 from terwer/dev
fix: 添加浮窗 loading 提升用户体验
2 parents ef39d06 + 7690aaf commit 196b4a8

File tree

7 files changed

+179
-34
lines changed

7 files changed

+179
-34
lines changed

layouts/default.vue

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
<script lang="ts" setup>
22
import Header from "~/components/default/Header.vue"
33
import Footer from "~/components/default/Footer.vue"
4+
import { createAppLogger } from "~/common/appLogger"
5+
import { sendMessageToParent } from "~/utils/innerIframeEvent"
6+
7+
const logger = createAppLogger("blog-app-layout")
8+
9+
// datas
10+
11+
onMounted(() => {
12+
if (window.self !== window.top) {
13+
logger.info("Current window is inside an iframe")
14+
15+
const isParentWindowLoaded = window.parent.document.readyState === "complete"
16+
if (isParentWindowLoaded) {
17+
nextTick(() => {
18+
const height = document.body.scrollHeight + 10
19+
logger.info(`Sending message to parent window with height: ${height}`)
20+
sendMessageToParent("updateHeight", height)
21+
})
22+
} else {
23+
logger.info("Parent window has not finished loading yet.")
24+
}
25+
26+
window.addEventListener("load", function () {
27+
logger.info("IFrame has finished loading.")
28+
})
29+
}
30+
})
431
</script>
532

633
<template>

nuxt.config.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const generateDynamicV = () => {
99
}
1010

1111
const isDev = process.env.NODE_ENV === "development"
12-
const appBase = "/"
12+
const appBase = "/plugins/siyuan-blog/"
1313
const staticV = generateDynamicV()
1414

1515
// https://nuxt.com/docs/api/configuration/nuxt-config
@@ -24,7 +24,14 @@ export default defineNuxtConfig({
2424
},
2525

2626
// build modules
27-
modules: ["@vueuse/nuxt", "@nuxtjs/i18n", "@element-plus/nuxt", "@nuxtjs/color-mode", "@pinia/nuxt", "@nuxt/image"],
27+
modules: [
28+
"@vueuse/nuxt",
29+
"@nuxtjs/i18n-edge",
30+
"@element-plus/nuxt",
31+
"@nuxtjs/color-mode",
32+
"@pinia/nuxt",
33+
"@nuxt/image",
34+
],
2835

2936
// vueuse
3037
vueuse: {
@@ -57,6 +64,14 @@ export default defineNuxtConfig({
5764
themes: ["dark"],
5865
},
5966

67+
// https://nuxt.com/docs/guide/going-further/custom-routing#hash-mode-spa
68+
ssr: false,
69+
router: {
70+
options: {
71+
hashMode: true,
72+
},
73+
},
74+
6075
css: ["~/assets/siyuan/style.styl", "~/assets/siyuan/index.styl"],
6176

6277
app: {
@@ -93,11 +108,15 @@ export default defineNuxtConfig({
93108

94109
// 环境变量
95110
runtimeConfig: {
96-
siyuanAuthToken: process.env.NUXT_SIYUAN_AUTH_TOKEN,
111+
// siyuanAuthToken: process.env.NUXT_SIYUAN_AUTH_TOKEN,
112+
siyuanAuthToken: "",
97113
public: {
98-
defaultType: process.env.NUXT_PUBLIC_DEFAULT_TYPE,
99-
siyuanApiUrl: process.env.NUXT_PUBLIC_SIYUAN_API_URL,
100-
waitTime: process.env.NUXT_PUBLIC_WAIT_TIME,
114+
// defaultType: process.env.NUXT_PUBLIC_DEFAULT_TYPE,
115+
defaultType: "siyuan",
116+
// siyuanApiUrl: process.env.NUXT_PUBLIC_SIYUAN_API_URL,
117+
siyuanApiUrl: "",
118+
// waitTime: process.env.NUXT_PUBLIC_WAIT_TIME,
119+
waitTime: "0",
101120
},
102121
},
103122
})

siyuan/iframeEvent.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,21 @@
2525

2626
import { popContentIframeId } from "~/siyuan/siyuanConstants"
2727
import { createAppLogger } from "~/common/appLogger"
28+
import SiyuanBlog from "~/siyuan/index"
2829

2930
const logger = createAppLogger("iframe-events")
3031
let added = false
31-
export const adjustIframeHeight = (iframeId: string, customHeight?: number) => {
32+
const adjustIframeHeight = (iframeId: string, customHeight?: number) => {
3233
const iframe = document.getElementById(iframeId) as HTMLIFrameElement
3334
let counter = 0
3435
let lastHeight = "0px" // 将初始高度设为 "0px"
36+
const defaultHeight = 350
3537

3638
// 注册定时器
3739
const interval = setInterval(() => {
3840
// 获取id为__nuxt的元素高度
3941
const iframeBody = iframe?.contentWindow?.document.getElementById("__nuxt") as HTMLElement
40-
let height = `${customHeight ?? iframeBody.scrollHeight}px`
42+
let height = `${customHeight ?? iframeBody?.scrollHeight ?? defaultHeight}px`
4143
if (height === lastHeight) {
4244
if (!added) {
4345
height = height + 10
@@ -65,25 +67,38 @@ export const adjustIframeHeight = (iframeId: string, customHeight?: number) => {
6567
}
6668
}
6769

68-
// 监听 message 事件
69-
window.addEventListener("message", (event) => {
70-
const iframe = document.getElementById(popContentIframeId) as HTMLIFrameElement
70+
/**
71+
* 注册 iframe 事件
72+
*/
73+
export const registerIframeEvent = (pluginInstance: SiyuanBlog) => {
74+
// 监听 message 事件
75+
window.addEventListener("message", (event) => {
76+
const iframe = document.getElementById(popContentIframeId) as HTMLIFrameElement
77+
78+
// 判断是否是来自指定 iframe 的消息
79+
if (event.source === iframe.contentWindow) {
80+
const data = event.data
81+
// 判断消息类型
82+
if (data.type === "updateHeight") {
83+
logger.info(`Try to cancel loading`)
84+
pluginInstance.popView.cancelLoading()
7185

72-
// 判断是否是来自指定 iframe 的消息
73-
if (event.source === iframe.contentWindow) {
74-
const data = event.data
75-
// 判断消息类型
76-
if (data.type === "updateHeight") {
77-
logger.info(`Received update height message from iframe`)
78-
const height = data.height
79-
// 更新 iframe 高度
80-
if (height) {
81-
iframe.height = `${height}px`
82-
logger.info(`Updated iframe height to ${height}px`)
86+
logger.info(`Received update height message from iframe`)
87+
const height = data.height
88+
// 更新 iframe 高度
89+
if (height) {
90+
iframe.height = `${height}px`
91+
logger.info(`Updated iframe height to ${height}px`)
92+
} else {
93+
adjustIframeHeight(popContentIframeId)
94+
logger.info(`Auto adjust iframe height to ${height}px`)
95+
}
8396
} else {
84-
adjustIframeHeight(popContentIframeId)
85-
logger.info(`Auto adjust iframe height to ${height}px`)
97+
logger.warn(`Unknown message type, ignore`)
8698
}
99+
} else {
100+
logger.warn(`message is not from contentWindow, ignore`)
87101
}
88-
}
89-
})
102+
})
103+
logger.info("iframe event registered in plugin main")
104+
}

siyuan/index.styl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,53 @@
5454
margin 0
5555
padding 0
5656
overflow hidden
57+
58+
.loading-spinner {
59+
width: 64px;
60+
height: 64px;
61+
position: absolute;
62+
top: 50%;
63+
left: 50%;
64+
transform: translate(-50%, -50%);
65+
border-radius: 5px;
66+
}
67+
68+
.loading-spinner > div {
69+
box-sizing: border-box;
70+
display: block;
71+
position: absolute;
72+
top: 50%;
73+
left: 50%;
74+
width: 20px;
75+
height: 20px;
76+
margin: -10px 0 0 -10px;
77+
border-radius: 50%;
78+
border: 2px solid #3498db;
79+
border-bottom-color: transparent;
80+
animation: loading-spinner-animation 0.75s 0s linear infinite;
81+
}
82+
83+
.loading-spinner > div:nth-child(1) {
84+
animation-delay: 0s;
85+
}
86+
87+
.loading-spinner > div:nth-child(2) {
88+
animation-delay: -0.6s;
89+
}
90+
91+
.loading-spinner > div:nth-child(3) {
92+
animation-delay: -0.4s;
93+
}
94+
95+
.loading-spinner > div:nth-child(4) {
96+
animation-delay: -0.2s;
97+
}
98+
99+
@keyframes loading-spinner-animation {
100+
0% {
101+
transform: rotate(0deg);
102+
}
103+
100% {
104+
transform: rotate(360deg);
105+
}
106+
}

siyuan/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ import { isDev } from "~/common/Constants"
2929

3030
import "./index.styl"
3131
import { icons } from "~/siyuan/utils/svg"
32+
import { registerIframeEvent } from "~/siyuan/iframeEvent"
3233

3334
export default class SiyuanBlog extends Plugin {
3435
public isMobile
3536
public logger
37+
public popView: any
38+
3639
constructor(options: { app: App; id: string; name: string; i18n: IObject }) {
3740
super(options)
3841

@@ -44,7 +47,10 @@ export default class SiyuanBlog extends Plugin {
4447
onload() {
4548
// 注册图标
4649
this.addIcons(icons.iconShare)
50+
// 初始化顶部栏以及图标
4751
initTopbar(this)
52+
// 注册 iframe 事件
53+
registerIframeEvent(this)
4854
}
4955

5056
openSetting() {

siyuan/topbar.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ import { Dialog, Menu } from "siyuan"
2828
import PageUtil from "~/siyuan/utils/pageUtil"
2929
import { getAvailableOrigin } from "~/siyuan/utils/utils"
3030
import { contentElement, contentHtml } from "~/siyuan/customElement"
31-
import { adjustIframeHeight } from "~/siyuan/iframeEvent"
32-
import { popContentIframeId } from "~/siyuan/siyuanConstants"
3331

3432
/**
3533
* 顶栏按钮
@@ -50,8 +48,10 @@ export function initTopbar(pluginInstance: SiyuanBlog) {
5048
topBarElement.addEventListener("click", async () => {
5149
const sharePage =
5250
"/plugins/siyuan-blog/#/share?id=" + PageUtil.getPageId() + "&origin=" + getAvailableOrigin() + "&isSsr=false"
53-
showPopView(pluginInstance, topBarElement, sharePage)
54-
adjustIframeHeight(popContentIframeId)
51+
const popView = showPopView(pluginInstance, topBarElement, sharePage, {
52+
showLoading: true,
53+
})
54+
pluginInstance.popView = popView
5555
})
5656

5757
// topBarElement.addEventListener("click", async () => {
@@ -139,8 +139,14 @@ const showPage = (pluginInstance: SiyuanBlog, pageUrl: string, title?: string) =
139139
* @param pluginInstance 插件实例对象
140140
* @param boxElement 被点击的 box 元素
141141
* @param pageUrl 当前页面的 URL
142+
* @param options
142143
*/
143-
const showPopView = (pluginInstance: SiyuanBlog, boxElement: HTMLElement, pageUrl: string) => {
144+
const showPopView = (
145+
pluginInstance: SiyuanBlog,
146+
boxElement: HTMLElement,
147+
pageUrl: string,
148+
options: { showLoading?: boolean; cancelLoading?: any } = {}
149+
) => {
144150
const popContentId = "pop-content"
145151
let popContent = document.getElementById(popContentId)
146152

@@ -170,8 +176,28 @@ const showPopView = (pluginInstance: SiyuanBlog, boxElement: HTMLElement, pageUr
170176
popContent.style.opacity = "0"
171177
popContent.style.transition = "opacity 0.3s ease-in-out"
172178

179+
// 添加loading效果
180+
if (options.showLoading) {
181+
const loadingElement = document.createElement("div")
182+
loadingElement.className = "loading-spinner"
183+
for (let i = 0; i < 4; i++) {
184+
const divElement = document.createElement("div")
185+
loadingElement.appendChild(divElement)
186+
}
187+
popContent.appendChild(loadingElement)
188+
189+
// 定义cancelLoading函数,用于取消loading
190+
const cancelLoading = () => {
191+
popContent?.removeChild(loadingElement)
192+
}
193+
194+
// 在option中添加cancelLoading函数
195+
options.cancelLoading = cancelLoading
196+
}
197+
173198
// 填充内容
174-
popContent.innerHTML = contentElement(pageUrl).innerHTML
199+
const content = contentElement(pageUrl)
200+
popContent.appendChild(content)
175201

176202
document.body.appendChild(popContent)
177203

@@ -204,4 +230,6 @@ const showPopView = (pluginInstance: SiyuanBlog, boxElement: HTMLElement, pageUr
204230
}
205231
}, 300) // 等待 300ms,即过渡时间,然后再删除浮动框
206232
}
233+
234+
return options
207235
}

utils/innerIframeEvent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { createAppLogger } from "~/common/appLogger"
2727

2828
const logger = createAppLogger("inner-iframe-event")
2929

30-
export const sendMessageToParent = (type: string) => {
30+
export const sendMessageToParent = (type: string, height?: number) => {
3131
const win = window.self as any
3232
if (!win.parent.siyuan) {
3333
logger.info(`Not in siyuan-note plugin iframe environment, ignore message sending`)
@@ -38,6 +38,6 @@ export const sendMessageToParent = (type: string) => {
3838
const iframeWindow = window.self
3939

4040
// 向父窗口发送消息
41-
iframeWindow.parent.postMessage({ type: type }, "*")
41+
iframeWindow.parent.postMessage({ type: type, height: height }, "*")
4242
logger.info(`Sends a message to the parent window, type => ${type}`)
4343
}

0 commit comments

Comments
 (0)