nnbb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.local +2 -0
  3. package/.eslintrc.json +15 -0
  4. package/.github/stale.yml +16 -0
  5. package/.github/workflows/codeql-analysis.yml +73 -0
  6. package/.github/workflows/docker-ghcr.yaml +59 -0
  7. package/.prettierrc +9 -0
  8. package/.vscode/launch.json +28 -0
  9. package/.vscode/settings.json +6 -0
  10. package/Dockerfile +38 -0
  11. package/LICENSE +21 -0
  12. package/README.md +16 -0
  13. package/blog.config.js +454 -0
  14. package/components/Ackee.js +83 -0
  15. package/components/AdBlockDetect.js +40 -0
  16. package/components/AlgoliaSearchModal.js +250 -0
  17. package/components/Artalk.js +30 -0
  18. package/components/Busuanzi.js +26 -0
  19. package/components/ChatBase.js +19 -0
  20. package/components/Collapse.tsx +134 -0
  21. package/components/Comment.js +161 -0
  22. package/components/CommonHead.tsx +101 -0
  23. package/components/CommonScript.js +125 -0
  24. package/components/CusdisComponent.js +35 -0
  25. package/components/CustomContextMenu.js +221 -0
  26. package/components/DebugPanel.js +134 -0
  27. package/components/DisableCopy.js +21 -0
  28. package/components/Draggable.js +167 -0
  29. package/components/Equation.js +31 -0
  30. package/components/ExternalPlugins.js +75 -0
  31. package/components/ExternalScript.js +29 -0
  32. package/components/FacebookMessenger.js +255 -0
  33. package/components/FacebookPage.js +34 -0
  34. package/components/Fireworks.js +210 -0
  35. package/components/FlipCard.js +56 -0
  36. package/components/FlutteringRibbon.js +322 -0
  37. package/components/FullScreenButton.js +48 -0
  38. package/components/Giscus.js +33 -0
  39. package/components/Gitalk.js +42 -0
  40. package/components/GoogleAdsense.js +111 -0
  41. package/components/Gtag.js +18 -0
  42. package/components/HeroIcons.tsx +321 -0
  43. package/components/KatexReact.js +53 -0
  44. package/components/LazyImage.js +95 -0
  45. package/components/Live2D.js +52 -0
  46. package/components/Loading.js +20 -0
  47. package/components/Mark.js +28 -0
  48. package/components/NProgress.ts +8 -0
  49. package/components/Nest.js +124 -0
  50. package/components/NotionIcon.js +20 -0
  51. package/components/NotionPage.tsx +206 -0
  52. package/components/Player.js +54 -0
  53. package/components/PrismMac.js +236 -0
  54. package/components/QrCode.js +34 -0
  55. package/components/Ribbon.js +98 -0
  56. package/components/Sakura.js +192 -0
  57. package/components/Select.js +40 -0
  58. package/components/ShareBar.js +29 -0
  59. package/components/ShareButtons.js +403 -0
  60. package/components/SideBarDrawer.js +50 -0
  61. package/components/StarrySky.js +130 -0
  62. package/components/Tabs.js +64 -0
  63. package/components/ThemeSwitch.js +67 -0
  64. package/components/Twikoo.js +27 -0
  65. package/components/TwikooCommentCount.js +22 -0
  66. package/components/TwikooCommentCounter.js +78 -0
  67. package/components/TwikooRecentComments.js +11 -0
  68. package/components/Utterances.js +35 -0
  69. package/components/VConsole.js +76 -0
  70. package/components/ValineComponent.js +59 -0
  71. package/components/ValineCount.js +6 -0
  72. package/components/ValinePanel.js +3 -0
  73. package/components/Vercel.tsx +54 -0
  74. package/components/WWAds.js +18 -0
  75. package/components/WalineComponent.js +83 -0
  76. package/components/WebMention.js +173 -0
  77. package/components/Webwhiz.js +17 -0
  78. package/components/WordCount.js +73 -0
  79. package/hooks/useToggleClickOutSide.ts +32 -0
  80. package/hooks/useWindowSize.ts +30 -0
  81. package/lib/algolia.js +108 -0
  82. package/lib/busuanzi.js +99 -0
  83. package/lib/cache/cacheManager.ts +49 -0
  84. package/lib/cache/localFileCache.ts +56 -0
  85. package/lib/cache/memoryMache.ts +20 -0
  86. package/lib/cache/mongoDbCache.ts +70 -0
  87. package/lib/cache/types.ts +5 -0
  88. package/lib/font.js +46 -0
  89. package/lib/global.tsx +129 -0
  90. package/lib/gtag.js +17 -0
  91. package/lib/mailchimp.js +49 -0
  92. package/lib/memorize.js +0 -0
  93. package/lib/mhchem.js +1696 -0
  94. package/lib/notion/getAllCategories.ts +51 -0
  95. package/lib/notion/getAllPageIds.ts +51 -0
  96. package/lib/notion/getAllPosts.js +68 -0
  97. package/lib/notion/getAllTags.ts +43 -0
  98. package/lib/notion/getNotionData.ts +340 -0
  99. package/lib/notion/getPageInfoOfPostPage.ts +58 -0
  100. package/lib/notion/getPageProperties.ts +203 -0
  101. package/lib/notion/getPageTableOfContents.ts +107 -0
  102. package/lib/notion/getPostBlocks.ts +147 -0
  103. package/lib/notion/mapImage.ts +130 -0
  104. package/lib/notion/types.ts +125 -0
  105. package/lib/notion.js +2 -0
  106. package/lib/robots.txt.js +25 -0
  107. package/lib/rss.js +63 -0
  108. package/lib/sitemap.xml.js +67 -0
  109. package/lib/utils.js +212 -0
  110. package/next-env.d.ts +5 -0
  111. package/next-i18next.config.js +7 -0
  112. package/next-sitemap.config.js +11 -0
  113. package/next.config.js +124 -0
  114. package/package.json +92 -0
  115. package/pages/404.tsx +40 -0
  116. package/pages/[prefix]/[slug].tsx +123 -0
  117. package/pages/[prefix]/index.tsx +223 -0
  118. package/pages/_app.js +59 -0
  119. package/pages/_document.js +42 -0
  120. package/pages/api/subscribe.js +22 -0
  121. package/pages/archive/index.tsx +79 -0
  122. package/pages/category/[category]/index.tsx +87 -0
  123. package/pages/category/[category]/page/[page].tsx +103 -0
  124. package/pages/category/index.tsx +43 -0
  125. package/pages/index.tsx +88 -0
  126. package/pages/page/[page].tsx +93 -0
  127. package/pages/search/[keyword]/index.tsx +162 -0
  128. package/pages/search/[keyword]/page/[page].tsx +166 -0
  129. package/pages/search/index.tsx +69 -0
  130. package/pages/sitemap.xml.js +70 -0
  131. package/pages/tag/[tag]/index.tsx +73 -0
  132. package/pages/tag/[tag]/page/[page].tsx +87 -0
  133. package/pages/tag/index.tsx +42 -0
  134. package/postcss.config.js +6 -0
  135. package/public/ads.txt +1 -0
  136. package/public/avatar.png +0 -0
  137. package/public/avatar.svg +11 -0
  138. package/public/bg_image.jpg +0 -0
  139. package/public/css/all.min.css +9 -0
  140. package/public/css/custom.css +8 -0
  141. package/public/css/img-shadow.css +5 -0
  142. package/public/css/prism-mac-style.css +58 -0
  143. package/public/favicon.ico +0 -0
  144. package/public/favicon.svg +9 -0
  145. package/public/js/cusdis.es.js +107 -0
  146. package/public/js/custom.js +1 -0
  147. package/public/locales/en/common.json +44 -0
  148. package/public/locales/en/menu.json +9 -0
  149. package/public/locales/en/nav.json +11 -0
  150. package/public/locales/zh-CN/common.json +44 -0
  151. package/public/locales/zh-CN/menu.json +9 -0
  152. package/public/locales/zh-CN/nav.json +9 -0
  153. package/public/webfonts/fa-brands-400.ttf +0 -0
  154. package/public/webfonts/fa-brands-400.woff2 +0 -0
  155. package/public/webfonts/fa-regular-400.ttf +0 -0
  156. package/public/webfonts/fa-regular-400.woff2 +0 -0
  157. package/public/webfonts/fa-solid-900.ttf +0 -0
  158. package/public/webfonts/fa-solid-900.woff2 +0 -0
  159. package/public/webfonts/fa-v4compatibility.ttf +0 -0
  160. package/public/webfonts/fa-v4compatibility.woff2 +0 -0
  161. package/styles/animate.css +503 -0
  162. package/styles/globals.css +183 -0
  163. package/styles/notion.css +2064 -0
  164. package/styles/nprogress.css +84 -0
  165. package/styles/prism-theme.css +119 -0
  166. package/styles/utility-patterns.css +79 -0
  167. package/tailwind.config.js +37 -0
  168. package/theme/index.ts +6 -0
  169. package/theme/types/@theme-components.d.ts +29 -0
  170. package/theme/useLayout.ts +41 -0
  171. package/theme/utils.ts +108 -0
  172. package/themes/innocent/package.json +7 -0
  173. package/themes/innocent/theme.config.js +1 -0
  174. package/themes/nobelium/components/Announcement.tsx +27 -0
  175. package/themes/nobelium/components/ArticleFooter.tsx +39 -0
  176. package/themes/nobelium/components/ArticleInfo.tsx +58 -0
  177. package/themes/nobelium/components/ArticleLock.tsx +86 -0
  178. package/themes/nobelium/components/BlogArchiveItem.js +41 -0
  179. package/themes/nobelium/components/BlogListBar.js +39 -0
  180. package/themes/nobelium/components/BlogListPage.tsx +67 -0
  181. package/themes/nobelium/components/BlogListScroll.tsx +96 -0
  182. package/themes/nobelium/components/BlogPost.tsx +33 -0
  183. package/themes/nobelium/components/DarkModeButton.tsx +50 -0
  184. package/themes/nobelium/components/ExampleRecentComments.js +35 -0
  185. package/themes/nobelium/components/Footer.tsx +35 -0
  186. package/themes/nobelium/components/JumpToTopButton.tsx +39 -0
  187. package/themes/nobelium/components/LanguageSwitchButton.tsx +58 -0
  188. package/themes/nobelium/components/MenuItemCollapse.tsx +92 -0
  189. package/themes/nobelium/components/MenuItemDrop.tsx +83 -0
  190. package/themes/nobelium/components/Nav/Nav.module.css +50 -0
  191. package/themes/nobelium/components/Nav/Nav.tsx +187 -0
  192. package/themes/nobelium/components/RandomPostButton.tsx +31 -0
  193. package/themes/nobelium/components/SearchButton.tsx +31 -0
  194. package/themes/nobelium/components/SearchInput.tsx +94 -0
  195. package/themes/nobelium/components/SearchNavBar.js +19 -0
  196. package/themes/nobelium/components/SideBar.js +83 -0
  197. package/themes/nobelium/components/SvgIcon.js +29 -0
  198. package/themes/nobelium/components/TagItem.js +13 -0
  199. package/themes/nobelium/components/Tags.tsx +44 -0
  200. package/themes/nobelium/components/Title.js +19 -0
  201. package/themes/nobelium/index.tsx +28 -0
  202. package/themes/nobelium/layout/LayoutBase.tsx +79 -0
  203. package/themes/nobelium/pages/Archive.tsx +30 -0
  204. package/themes/nobelium/pages/Category.tsx +43 -0
  205. package/themes/nobelium/pages/Home.tsx +22 -0
  206. package/themes/nobelium/pages/PageNotFound.tsx +15 -0
  207. package/themes/nobelium/pages/Post.tsx +34 -0
  208. package/themes/nobelium/pages/PostList.tsx +74 -0
  209. package/themes/nobelium/pages/Search.tsx +65 -0
  210. package/themes/nobelium/pages/Tag.tsx +42 -0
  211. package/themes/nobelium/providers/index.tsx +60 -0
  212. package/themes/nobelium/stores/index.tsx +42 -0
  213. package/themes/nobelium/theme.config.ts +17 -0
  214. package/themes/nobelium/types/index.ts +10 -0
  215. package/tsconfig.json +29 -0
  216. package/types/index.ts +1 -0
  217. package/types/page.ts +102 -0
  218. package/vercel.json +5 -0
@@ -0,0 +1,167 @@
1
+ import { useRef, useEffect, useState } from 'react';
2
+ /**
3
+ * 可拖拽组件
4
+ */
5
+
6
+ export const Draggable = (props) => {
7
+ const { children } = props;
8
+ const draggableRef = useRef(null);
9
+ const rafRef = useRef(null);
10
+ const [moving, setMoving] = useState(false);
11
+ let currentObj, offsetX, offsetY;
12
+
13
+ useEffect(() => {
14
+ const draggableElements = document.getElementsByClassName('draggable');
15
+
16
+ // 标准化鼠标事件对象
17
+ function e(event) {
18
+ // 定义事件对象标准化函数
19
+ if (!event) {
20
+ // 兼容IE浏览器
21
+ event = window.event;
22
+ event.target = event.srcElement;
23
+ event.layerX = event.offsetX;
24
+ event.layerY = event.offsetY;
25
+ }
26
+ // 移动端
27
+ if (event.type === 'touchstart' || event.type === 'touchmove') {
28
+ event.clientX = event.touches[0].clientX;
29
+ event.clientY = event.touches[0].clientY;
30
+ }
31
+
32
+ event.mx = event.pageX || event.clientX + document.body.scrollLeft;
33
+ // 计算鼠标指针的x轴距离
34
+ event.my = event.pageY || event.clientY + document.body.scrollTop;
35
+ // 计算鼠标指针的y轴距离
36
+
37
+ return event; // 返回标准化的事件对象
38
+ }
39
+
40
+ // 定义鼠标事件处理函数
41
+ // document.pointerdown = start
42
+ document.onmousedown = start;
43
+ document.ontouchstart = start;
44
+
45
+ function start(event) {
46
+ // 按下鼠标时,初始化处理
47
+ if (!draggableElements) return;
48
+ event = e(event); // 获取标准事件对象
49
+
50
+ for (const drag of draggableElements) {
51
+ // 判断鼠标点击的区域是否是拖拽框内
52
+ if (inDragBox(event, drag)) {
53
+ currentObj = drag.firstElementChild;
54
+ }
55
+ }
56
+ if (currentObj) {
57
+ if (event.type === 'touchstart') {
58
+ event.preventDefault(); // 阻止默认的滚动行为
59
+ document.documentElement.style.overflow = 'hidden'; // 防止页面一起滚动
60
+ }
61
+
62
+ setMoving(true);
63
+ offsetX = event.mx - currentObj.offsetLeft;
64
+ offsetY = event.my - currentObj.offsetTop;
65
+
66
+ document.onmousemove = move; // 注册鼠标移动事件处理函数
67
+ document.ontouchmove = move;
68
+ document.onmouseup = stop; // 注册松开鼠标事件处理函数
69
+ document.ontouchend = stop;
70
+ }
71
+ }
72
+
73
+ function move(event) {
74
+ // 鼠标移动处理函数
75
+ event = e(event);
76
+ rafRef.current = requestAnimationFrame(() => updatePosition(event));
77
+ }
78
+
79
+ const stop = () => {
80
+ document.documentElement.style.overflow = 'auto'; // 恢复默认的滚动行为
81
+ cancelAnimationFrame(rafRef.current);
82
+ setMoving(false);
83
+ currentObj =
84
+ document.ontouchmove =
85
+ document.ontouchend =
86
+ document.onmousemove =
87
+ document.onmouseup =
88
+ null;
89
+ };
90
+
91
+ const updatePosition = (event) => {
92
+ if (currentObj) {
93
+ const left = event.mx - offsetX;
94
+ const top = event.my - offsetY;
95
+ currentObj.style.left = left + 'px';
96
+ currentObj.style.top = top + 'px';
97
+ checkInWindow();
98
+ }
99
+ };
100
+
101
+ /**
102
+ * 鼠标是否在可拖拽区域内
103
+ * @param {*} event
104
+ * @returns
105
+ */
106
+ function inDragBox(event, drag) {
107
+ const { clientX, clientY } = event; // 鼠标位置
108
+ const { offsetHeight, offsetWidth, offsetTop, offsetLeft } =
109
+ drag.firstElementChild; // 窗口位置
110
+ const horizontal =
111
+ clientX > offsetLeft && clientX < offsetLeft + offsetWidth;
112
+ const vertical =
113
+ clientY > offsetTop && clientY < offsetTop + offsetHeight;
114
+
115
+ if (horizontal && vertical) {
116
+ return true;
117
+ }
118
+
119
+ return false;
120
+ }
121
+
122
+ /**
123
+ * 若超出窗口则吸附。
124
+ */
125
+ function checkInWindow() {
126
+ // 检查是否悬浮在窗口内
127
+ for (const drag of draggableElements) {
128
+ // 判断鼠标点击的区域是否是拖拽框内
129
+ const { offsetHeight, offsetWidth, offsetTop, offsetLeft } =
130
+ drag.firstElementChild;
131
+ const { clientHeight, clientWidth } = document.documentElement;
132
+ if (offsetTop < 0) {
133
+ drag.firstElementChild.style.top = 0;
134
+ }
135
+ if (offsetTop > clientHeight - offsetHeight) {
136
+ drag.firstElementChild.style.top = clientHeight - offsetHeight + 'px';
137
+ }
138
+ if (offsetLeft < 0) {
139
+ drag.firstElementChild.style.left = 0;
140
+ }
141
+ if (offsetLeft > clientWidth - offsetWidth) {
142
+ drag.firstElementChild.style.left = clientWidth - offsetWidth + 'px';
143
+ }
144
+ }
145
+ }
146
+
147
+ window.addEventListener('resize', checkInWindow);
148
+
149
+ return () => {
150
+ return () => {
151
+ window.removeEventListener('resize', checkInWindow);
152
+ cancelAnimationFrame(rafRef.current);
153
+ };
154
+ };
155
+ }, []);
156
+
157
+ return (
158
+ <div
159
+ className={`draggable ${moving ? 'cursor-grabbing' : 'cursor-grab'} select-none`}
160
+ ref={draggableRef}
161
+ >
162
+ {children}
163
+ </div>
164
+ );
165
+ };
166
+
167
+ Draggable.defaultProps = { left: 0, top: 0 };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+
3
+ import Katex from '@/components/KatexReact';
4
+ import { getBlockTitle } from 'notion-utils';
5
+
6
+ const katexSettings = {
7
+ throwOnError: false,
8
+ strict: false,
9
+ };
10
+
11
+ export const Equation = ({
12
+ block,
13
+ math,
14
+ inline = false,
15
+ className,
16
+ ...rest
17
+ }) => {
18
+ // const { recordMap } = useNotionContext()
19
+ math = math || getBlockTitle(block, null);
20
+ if (!math) return null;
21
+
22
+ return (
23
+ <span
24
+ role="button"
25
+ tabIndex={0}
26
+ className={`notion-equation ${inline ? 'notion-equation-inline' : 'notion-equation-block'} ${className}`}
27
+ >
28
+ <Katex math={math} settings={katexSettings} {...rest} />
29
+ </span>
30
+ );
31
+ };
@@ -0,0 +1,75 @@
1
+ import BLOG from 'blog.config';
2
+ import dynamic from 'next/dynamic';
3
+ import WebWhiz from './Webwhiz';
4
+
5
+ // import TwikooCommentCounter from '@/components/TwikooCommentCounter'
6
+ // import { DebugPanel } from '@/components/DebugPanel'
7
+ // import { ThemeSwitch } from '@/components/ThemeSwitch'
8
+ // import { Fireworks } from '@/components/Fireworks'
9
+ // import { Nest } from '@/components/Nest'
10
+ // import { FlutteringRibbon } from '@/components/FlutteringRibbon'
11
+ // import { Ribbon } from '@/components/Ribbon'
12
+ // import { Sakura } from '@/components/Sakura'
13
+ // import { StarrySky } from '@/components/StarrySky'
14
+ // import { Analytics } from '@vercel/analytics/react'
15
+
16
+ const TwikooCommentCounter = dynamic(() => import('@/components/TwikooCommentCounter'), { ssr: false });
17
+ const DebugPanel = dynamic(() => import('@/components/DebugPanel'), { ssr: false });
18
+ const ThemeSwitch = dynamic(() => import('@/components/ThemeSwitch'), { ssr: false });
19
+ const Fireworks = dynamic(() => import('@/components/Fireworks'), { ssr: false });
20
+ const Nest = dynamic(() => import('@/components/Nest'), { ssr: false });
21
+ const FlutteringRibbon = dynamic(() => import('@/components/FlutteringRibbon'), { ssr: false });
22
+ const Ribbon = dynamic(() => import('@/components/Ribbon'), { ssr: false });
23
+ const Sakura = dynamic(() => import('@/components/Sakura'), { ssr: false });
24
+ const StarrySky = dynamic(() => import('@/components/StarrySky'), { ssr: false });
25
+ const Analytics = dynamic(
26
+ () =>
27
+ import('@vercel/analytics/react').then(async (m) => {
28
+ return m.Analytics;
29
+ }),
30
+ { ssr: false },
31
+ );
32
+ const MusicPlayer = dynamic(() => import('@/components/Player'), { ssr: false });
33
+ const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false });
34
+ const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false });
35
+ const Busuanzi = dynamic(() => import('@/components/Busuanzi'), { ssr: false });
36
+ const GoogleAdsense = dynamic(() => import('@/components/GoogleAdsense'), { ssr: false });
37
+ const Messenger = dynamic(() => import('@/components/FacebookMessenger'), { ssr: false });
38
+ const VConsole = dynamic(() => import('@/components/VConsole'), { ssr: false });
39
+ const CustomContextMenu = dynamic(() => import('@/components/CustomContextMenu'), { ssr: false });
40
+ const DisableCopy = dynamic(() => import('@/components/DisableCopy'), { ssr: false });
41
+ const AdBlockDetect = dynamic(() => import('@/components/AdBlockDetect'), { ssr: false });
42
+ /**
43
+ * 各种第三方组件
44
+ * @param {*} props
45
+ * @returns
46
+ */
47
+ const ExternalPlugin = (props) => {
48
+ return (
49
+ <>
50
+ {JSON.parse(BLOG.THEME_SWITCH) && <ThemeSwitch />}
51
+ {JSON.parse(BLOG.DEBUG) && <DebugPanel />}
52
+ {BLOG.ANALYTICS_ACKEE_TRACKER && <Ackee />}
53
+ {BLOG.ANALYTICS_GOOGLE_ID && <Gtag />}
54
+ {BLOG.ANALYTICS_VERCEL && <Analytics />}
55
+ {JSON.parse(BLOG.ANALYTICS_BUSUANZI_ENABLE) && <Busuanzi />}
56
+ {BLOG.ADSENSE_GOOGLE_ID && <GoogleAdsense />}
57
+ {BLOG.FACEBOOK_APP_ID && BLOG.FACEBOOK_PAGE_ID && <Messenger />}
58
+ {JSON.parse(BLOG.FIREWORKS) && <Fireworks />}
59
+ {JSON.parse(BLOG.SAKURA) && <Sakura />}
60
+ {JSON.parse(BLOG.STARRY_SKY) && <StarrySky />}
61
+ {JSON.parse(BLOG.MUSIC_PLAYER) && <MusicPlayer />}
62
+ {JSON.parse(BLOG.NEST) && <Nest />}
63
+ {JSON.parse(BLOG.FLUTTERINGRIBBON) && <FlutteringRibbon />}
64
+ {JSON.parse(BLOG.COMMENT_TWIKOO_COUNT_ENABLE) && <TwikooCommentCounter {...props} />}
65
+ {JSON.parse(BLOG.RIBBON) && <Ribbon />}
66
+ {JSON.parse(BLOG.CUSTOM_RIGHT_CLICK_CONTEXT_MENU) && <CustomContextMenu {...props} />}
67
+ {!JSON.parse(BLOG.CAN_COPY) && <DisableCopy />}
68
+ {JSON.parse(BLOG.WEB_WHIZ_ENABLED) && <WebWhiz />}
69
+ {JSON.parse(BLOG.AD_WWADS_BLOCK_DETECT) && <AdBlockDetect />}
70
+ <VConsole />
71
+ </>
72
+ );
73
+ };
74
+
75
+ export default ExternalPlugin;
@@ -0,0 +1,29 @@
1
+ 'use client'
2
+
3
+ import { isBrowser } from '@/lib/utils'
4
+
5
+ /**
6
+ * 自定义外部 script
7
+ * 传入参数将转为 <script>标签。
8
+ * @returns
9
+ */
10
+ const ExternalScript = (props) => {
11
+ const { src } = props
12
+ if (!isBrowser || !src) {
13
+ return null
14
+ }
15
+
16
+ const element = document.querySelector(`script[src="${src}"]`)
17
+ if (element) {
18
+ return null
19
+ }
20
+ const script = document.createElement('script')
21
+ Object.entries(props).forEach(([key, value]) => {
22
+ script.setAttribute(key, value)
23
+ })
24
+ document.head.appendChild(script)
25
+ console.log('加载外部脚本', props, script)
26
+ return null
27
+ }
28
+
29
+ export default ExternalScript
@@ -0,0 +1,255 @@
1
+ import BLOG from '@/blog.config'
2
+ import { Component } from 'react'
3
+ import PropTypes from 'prop-types'
4
+
5
+ export default function Messenger() {
6
+ return <MessengerCustomerChat
7
+ pageId={BLOG.FACEBOOK_PAGE_ID}
8
+ appId={BLOG.FACEBOOK_APP_ID}
9
+ language={BLOG.LANG.replace('-', '_')}
10
+ shouldShowDialog={true}
11
+ />
12
+ }
13
+
14
+ /**
15
+ * @see https://github.com/Yoctol/react-messenger-customer-chat
16
+ */
17
+ class MessengerCustomerChat extends Component {
18
+ constructor(props) {
19
+ super(props)
20
+ this.state = {
21
+ fbLoaded: false,
22
+ shouldShowDialog: undefined
23
+ }
24
+ }
25
+
26
+ /**
27
+ * 初始化
28
+ */
29
+ componentDidMount() {
30
+ this.setFbAsyncInit()
31
+ this.reloadSDKAsynchronously()
32
+ }
33
+
34
+ componentDidUpdate(prevProps) {
35
+ if (
36
+ prevProps.pageId !== this.props.pageId ||
37
+ prevProps.appId !== this.props.appId ||
38
+ prevProps.shouldShowDialog !== this.props.shouldShowDialog ||
39
+ prevProps.htmlRef !== this.props.htmlRef ||
40
+ prevProps.minimized !== this.props.minimized ||
41
+ prevProps.themeColor !== this.props.themeColor ||
42
+ prevProps.loggedInGreeting !== this.props.loggedInGreeting ||
43
+ prevProps.loggedOutGreeting !== this.props.loggedOutGreeting ||
44
+ prevProps.greetingDialogDisplay !== this.props.greetingDialogDisplay ||
45
+ prevProps.greetingDialogDelay !== this.props.greetingDialogDelay ||
46
+ prevProps.autoLogAppEvents !== this.props.autoLogAppEvents ||
47
+ prevProps.xfbml !== this.props.xfbml ||
48
+ prevProps.version !== this.props.version ||
49
+ prevProps.language !== this.props.language
50
+ ) {
51
+ this.setFbAsyncInit()
52
+ this.reloadSDKAsynchronously()
53
+ }
54
+ }
55
+
56
+ componentWillUnmount() {
57
+ if (window.FB !== undefined) {
58
+ window.FB.CustomerChat.hide()
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 初始化
64
+ */
65
+ setFbAsyncInit() {
66
+ const { appId, autoLogAppEvents, xfbml, version } = this.props
67
+
68
+ window.fbAsyncInit = () => {
69
+ window.FB.init({
70
+ appId,
71
+ autoLogAppEvents,
72
+ xfbml,
73
+ version: `v${version}`
74
+ })
75
+
76
+ this.setState({ fbLoaded: true })
77
+ }
78
+ }
79
+
80
+ loadSDKAsynchronously() {
81
+ const { language } = this.props;
82
+ /* eslint-disable */
83
+ (function (d, s, id) {
84
+ var js,
85
+ fjs = d.getElementsByTagName(s)[0];
86
+ if (d.getElementById(id)) {
87
+ return;
88
+ }
89
+ js = d.createElement(s);
90
+ js.id = id;
91
+ js.src = `https://connect.facebook.net/${language}/sdk/xfbml.customerchat.js`;
92
+ fjs.parentNode.insertBefore(js, fjs);
93
+ })(document, 'script', 'facebook-jssdk');
94
+ /* eslint-enable */
95
+ }
96
+
97
+ removeFacebookSDK() {
98
+ removeElementByIds(['facebook-jssdk', 'fb-root'])
99
+
100
+ delete window.FB
101
+ }
102
+
103
+ reloadSDKAsynchronously() {
104
+ this.removeFacebookSDK()
105
+ this.loadSDKAsynchronously()
106
+ }
107
+
108
+ controlPlugin() {
109
+ const { shouldShowDialog } = this.props
110
+
111
+ if (shouldShowDialog) {
112
+ window.FB.CustomerChat.showDialog()
113
+ } else {
114
+ window.FB.CustomerChat.hideDialog()
115
+ }
116
+ }
117
+
118
+ subscribeEvents() {
119
+ const { onCustomerChatDialogShow, onCustomerChatDialogHide } = this.props
120
+
121
+ if (onCustomerChatDialogShow) {
122
+ window.FB.Event.subscribe(
123
+ 'customerchat.dialogShow',
124
+ onCustomerChatDialogShow
125
+ )
126
+ }
127
+
128
+ if (onCustomerChatDialogHide) {
129
+ window.FB.Event.subscribe(
130
+ 'customerchat.dialogHide',
131
+ onCustomerChatDialogHide
132
+ )
133
+ }
134
+ }
135
+
136
+ createMarkup() {
137
+ const {
138
+ pageId,
139
+ htmlRef,
140
+ minimized,
141
+ themeColor,
142
+ loggedInGreeting,
143
+ loggedOutGreeting,
144
+ greetingDialogDisplay,
145
+ greetingDialogDelay
146
+ } = this.props
147
+
148
+ const refAttribute = htmlRef !== undefined ? `ref="${htmlRef}"` : ''
149
+ const minimizedAttribute =
150
+ minimized !== undefined ? `minimized="${minimized}"` : ''
151
+ const themeColorAttribute =
152
+ themeColor !== undefined ? `theme_color="${themeColor}"` : ''
153
+ const loggedInGreetingAttribute =
154
+ loggedInGreeting !== undefined
155
+ ? `logged_in_greeting="${loggedInGreeting}"`
156
+ : ''
157
+ const loggedOutGreetingAttribute =
158
+ loggedOutGreeting !== undefined
159
+ ? `logged_out_greeting="${loggedOutGreeting}"`
160
+ : ''
161
+ const greetingDialogDisplayAttribute =
162
+ greetingDialogDisplay !== undefined
163
+ ? `greeting_dialog_display="${greetingDialogDisplay}"`
164
+ : ''
165
+ const greetingDialogDelayAttribute =
166
+ greetingDialogDelay !== undefined
167
+ ? `greeting_dialog_delay="${greetingDialogDelay}"`
168
+ : ''
169
+
170
+ return {
171
+ __html: `<div
172
+ class="fb-customerchat"
173
+ page_id="${pageId}"
174
+ ${refAttribute}
175
+ ${minimizedAttribute}
176
+ ${themeColorAttribute}
177
+ ${loggedInGreetingAttribute}
178
+ ${loggedOutGreetingAttribute}
179
+ ${greetingDialogDisplayAttribute}
180
+ ${greetingDialogDelayAttribute}
181
+ ></div>`
182
+ }
183
+ }
184
+
185
+ render() {
186
+ const { fbLoaded, shouldShowDialog } = this.state
187
+
188
+ if (fbLoaded && shouldShowDialog !== this.props.shouldShowDialog) {
189
+ document.addEventListener(
190
+ 'DOMNodeInserted',
191
+ (event) => {
192
+ const element = event.target
193
+ if (
194
+ element.className &&
195
+ typeof element.className === 'string' &&
196
+ element.className.includes('fb_dialog')
197
+ ) {
198
+ this.controlPlugin()
199
+ }
200
+ },
201
+ false
202
+ )
203
+ this.subscribeEvents()
204
+ }
205
+ // Add a random key to rerender. Reference:
206
+ // https://stackoverflow.com/questions/30242530/dangerouslysetinnerhtml-doesnt-update-during-render
207
+ return <div key={Date()} dangerouslySetInnerHTML={this.createMarkup()} />
208
+ }
209
+ }
210
+
211
+ const removeElementByIds = (ids) => {
212
+ ids.forEach((id) => {
213
+ const element = document.getElementById(id)
214
+ if (element && element.parentNode) {
215
+ element.parentNode.removeChild(element)
216
+ }
217
+ })
218
+ }
219
+
220
+ MessengerCustomerChat.propTypes = {
221
+ pageId: PropTypes.string.isRequired,
222
+ appId: PropTypes.string,
223
+ shouldShowDialog: PropTypes.bool,
224
+ htmlRef: PropTypes.string,
225
+ minimized: PropTypes.bool,
226
+ themeColor: PropTypes.string,
227
+ loggedInGreeting: PropTypes.string,
228
+ loggedOutGreeting: PropTypes.string,
229
+ greetingDialogDisplay: PropTypes.oneOf(['show', 'hide', 'fade']),
230
+ greetingDialogDelay: PropTypes.number,
231
+ autoLogAppEvents: PropTypes.bool,
232
+ xfbml: PropTypes.bool,
233
+ version: PropTypes.string,
234
+ language: PropTypes.string,
235
+ onCustomerChatDialogShow: PropTypes.func,
236
+ onCustomerChatDialogHide: PropTypes.func
237
+ }
238
+
239
+ MessengerCustomerChat.defaultProps = {
240
+ appId: null,
241
+ shouldShowDialog: false,
242
+ htmlRef: undefined,
243
+ minimized: undefined,
244
+ themeColor: undefined,
245
+ loggedInGreeting: undefined,
246
+ loggedOutGreeting: undefined,
247
+ greetingDialogDisplay: undefined,
248
+ greetingDialogDelay: undefined,
249
+ autoLogAppEvents: true,
250
+ xfbml: true,
251
+ version: '11.0',
252
+ language: 'en_US',
253
+ onCustomerChatDialogShow: undefined,
254
+ onCustomerChatDialogHide: undefined
255
+ }
@@ -0,0 +1,34 @@
1
+ import BLOG from '@/blog.config'
2
+ import { FacebookProvider, Page } from 'react-facebook'
3
+ import { FacebookIcon } from 'react-share'
4
+
5
+ /**
6
+ * facebook个人主页
7
+ * @returns
8
+ */
9
+ const FacebookPage = () => {
10
+ if (!BLOG.FACEBOOK_APP_ID || !BLOG.FACEBOOK_PAGE) {
11
+ return <></>
12
+ }
13
+ return <div className="shadow-md hover:shadow-xl dark:text-gray-300 border dark:border-black rounded-xl px-2 py-4 bg-white dark:bg-hexo-black-gray lg:duration-100 justify-center">
14
+ {BLOG.FACEBOOK_PAGE && (
15
+ <div className="flex items-center pb-2">
16
+ <a
17
+ href={BLOG.FACEBOOK_PAGE}
18
+ target="_blank"
19
+ rel="noopener noreferrer"
20
+ className="p-1 pr-2 pt-0"
21
+ >
22
+ <FacebookIcon size={28} round />
23
+ </a>
24
+ <a href={BLOG.FACEBOOK_PAGE} rel="noopener noreferrer" target="_blank">
25
+ {BLOG.FACEBOOK_PAGE_TITLE}
26
+ </a>
27
+ </div>
28
+ )}
29
+ {BLOG.FACEBOOK_APP_ID && <FacebookProvider appId={BLOG.FACEBOOK_APP_ID}>
30
+ <Page href={BLOG.FACEBOOK_PAGE} tabs="timeline" />
31
+ </FacebookProvider>}
32
+ </div>
33
+ }
34
+ export default FacebookPage