@bbki.ng/site 1.1.27 → 1.2.0
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.
- package/CHANGELOG.md +4 -0
- package/dev-dist/workbox-baccbcc1.js +21 -21
- package/package.json +4 -4
- package/src/articles/loading.mdx +19 -4
- package/src/components/reload_prompt/index.tsx +12 -1
- package/src/constants/photo_projects.ts +2 -2
- package/src/constants/routes.ts +1 -0
- package/src/hooks/use_authed.ts +7 -0
- package/src/hooks/use_authed_fetcher.ts +8 -0
- package/src/hooks/use_file_to_post.ts +33 -0
- package/src/hooks/use_post.ts +25 -0
- package/src/hooks/use_text_plain_file.ts +36 -0
- package/src/pages/extensions/txt/index.tsx +10 -5
package/CHANGELOG.md
CHANGED
|
@@ -832,7 +832,7 @@ define(["exports"], function (exports) {
|
|
|
832
832
|
}
|
|
833
833
|
/**
|
|
834
834
|
* @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP
|
|
835
|
-
* method
|
|
835
|
+
* method title ('GET', etc.) to an array of all the corresponding `Route`
|
|
836
836
|
* instances that are registered.
|
|
837
837
|
*/
|
|
838
838
|
|
|
@@ -1958,7 +1958,7 @@ define(["exports"], function (exports) {
|
|
|
1958
1958
|
url,
|
|
1959
1959
|
timestamp,
|
|
1960
1960
|
cacheName: this._cacheName,
|
|
1961
|
-
// Creating an ID from the URL and cache
|
|
1961
|
+
// Creating an ID from the URL and cache title won't be necessary once
|
|
1962
1962
|
// Edge switches to Chromium and all browsers we support work with
|
|
1963
1963
|
// array keyPaths.
|
|
1964
1964
|
id: this._getId(url),
|
|
@@ -2055,7 +2055,7 @@ define(["exports"], function (exports) {
|
|
|
2055
2055
|
*/
|
|
2056
2056
|
|
|
2057
2057
|
_getId(url) {
|
|
2058
|
-
// Creating an ID from the URL and cache
|
|
2058
|
+
// Creating an ID from the URL and cache title won't be necessary once
|
|
2059
2059
|
// Edge switches to Chromium and all browsers we support work with
|
|
2060
2060
|
// array keyPaths.
|
|
2061
2061
|
return this._cacheName + "|" + normalizeURL(url);
|
|
@@ -2276,7 +2276,7 @@ define(["exports"], function (exports) {
|
|
|
2276
2276
|
* It can only be used with `workbox-strategy` instances that have a
|
|
2277
2277
|
* [custom `cacheName` property set](/web/tools/workbox/guides/configure-workbox#custom_cache_names_in_strategies).
|
|
2278
2278
|
* In other words, it can't be used to expire entries in strategy that uses the
|
|
2279
|
-
* default runtime cache
|
|
2279
|
+
* default runtime cache title.
|
|
2280
2280
|
*
|
|
2281
2281
|
* Whenever a cached response is used or updated, this plugin will look
|
|
2282
2282
|
* at the associated cache and remove any old or extra responses.
|
|
@@ -2435,7 +2435,7 @@ define(["exports"], function (exports) {
|
|
|
2435
2435
|
}
|
|
2436
2436
|
/**
|
|
2437
2437
|
* A simple helper method to return a CacheExpiration instance for a given
|
|
2438
|
-
* cache
|
|
2438
|
+
* cache title.
|
|
2439
2439
|
*
|
|
2440
2440
|
* @param {string} cacheName
|
|
2441
2441
|
* @return {CacheExpiration}
|
|
@@ -2523,7 +2523,7 @@ define(["exports"], function (exports) {
|
|
|
2523
2523
|
* metadata is also cleanly removed and open IndexedDB instances are deleted.
|
|
2524
2524
|
*
|
|
2525
2525
|
* Note that if you're *not* using cache expiration for a given cache, calling
|
|
2526
|
-
* `caches.delete()` and passing in the cache's
|
|
2526
|
+
* `caches.delete()` and passing in the cache's title should be sufficient.
|
|
2527
2527
|
* There is no Workbox-specific method needed for cleanup in that case.
|
|
2528
2528
|
*/
|
|
2529
2529
|
|
|
@@ -2911,7 +2911,7 @@ define(["exports"], function (exports) {
|
|
|
2911
2911
|
/**
|
|
2912
2912
|
* The request the strategy is performing (passed to the strategy's
|
|
2913
2913
|
* `handle()` or `handleAll()` method).
|
|
2914
|
-
* @
|
|
2914
|
+
* @title request
|
|
2915
2915
|
* @instance
|
|
2916
2916
|
* @type {Request}
|
|
2917
2917
|
* @memberof workbox-strategies.StrategyHandler
|
|
@@ -2919,7 +2919,7 @@ define(["exports"], function (exports) {
|
|
|
2919
2919
|
|
|
2920
2920
|
/**
|
|
2921
2921
|
* The event associated with this request.
|
|
2922
|
-
* @
|
|
2922
|
+
* @title event
|
|
2923
2923
|
* @instance
|
|
2924
2924
|
* @type {ExtendableEvent}
|
|
2925
2925
|
* @memberof workbox-strategies.StrategyHandler
|
|
@@ -2930,7 +2930,7 @@ define(["exports"], function (exports) {
|
|
|
2930
2930
|
* `handle()` or `handleAll()` method).
|
|
2931
2931
|
* Note: the `url` param will be present if the strategy was invoked
|
|
2932
2932
|
* from a workbox `Route` object.
|
|
2933
|
-
* @
|
|
2933
|
+
* @title url
|
|
2934
2934
|
* @instance
|
|
2935
2935
|
* @type {URL|undefined}
|
|
2936
2936
|
* @memberof workbox-strategies.StrategyHandler
|
|
@@ -2943,7 +2943,7 @@ define(["exports"], function (exports) {
|
|
|
2943
2943
|
* from a workbox `Route` object and the
|
|
2944
2944
|
* {@link workbox-routing~matchCallback} returned
|
|
2945
2945
|
* a truthy value (it will be that value).
|
|
2946
|
-
* @
|
|
2946
|
+
* @title params
|
|
2947
2947
|
* @instance
|
|
2948
2948
|
* @type {*|undefined}
|
|
2949
2949
|
* @memberof workbox-strategies.StrategyHandler
|
|
@@ -3305,7 +3305,7 @@ define(["exports"], function (exports) {
|
|
|
3305
3305
|
* Returns true if the strategy has at least one plugin with the given
|
|
3306
3306
|
* callback.
|
|
3307
3307
|
*
|
|
3308
|
-
* @param {string} name The
|
|
3308
|
+
* @param {string} name The title of the callback to check for.
|
|
3309
3309
|
* @return {boolean}
|
|
3310
3310
|
*/
|
|
3311
3311
|
|
|
@@ -3319,7 +3319,7 @@ define(["exports"], function (exports) {
|
|
|
3319
3319
|
return false;
|
|
3320
3320
|
}
|
|
3321
3321
|
/**
|
|
3322
|
-
* Runs all plugin callbacks matching the given
|
|
3322
|
+
* Runs all plugin callbacks matching the given title, in order, passing the
|
|
3323
3323
|
* given param object (merged ith the current plugin state) as the only
|
|
3324
3324
|
* argument.
|
|
3325
3325
|
*
|
|
@@ -3329,7 +3329,7 @@ define(["exports"], function (exports) {
|
|
|
3329
3329
|
* {@link workbox-strategies.StrategyHandler#iterateCallbacks}
|
|
3330
3330
|
* below for how to handle that case.
|
|
3331
3331
|
*
|
|
3332
|
-
* @param {string} name The
|
|
3332
|
+
* @param {string} name The title of the callback to run within each plugin.
|
|
3333
3333
|
* @param {Object} param The object to pass as the first (and only) param
|
|
3334
3334
|
* when executing each callback. This object will be merged with the
|
|
3335
3335
|
* current plugin state prior to callback execution.
|
|
@@ -3348,7 +3348,7 @@ define(["exports"], function (exports) {
|
|
|
3348
3348
|
* you call each callback, whatever object parameter you pass it will
|
|
3349
3349
|
* be merged with the plugin's current state).
|
|
3350
3350
|
*
|
|
3351
|
-
* @param {string} name The
|
|
3351
|
+
* @param {string} name The title fo the callback to run
|
|
3352
3352
|
* @return {Array<Function>}
|
|
3353
3353
|
*/
|
|
3354
3354
|
|
|
@@ -3497,7 +3497,7 @@ define(["exports"], function (exports) {
|
|
|
3497
3497
|
* constructor.
|
|
3498
3498
|
*
|
|
3499
3499
|
* @param {Object} [options]
|
|
3500
|
-
* @param {string} [options.cacheName] Cache
|
|
3500
|
+
* @param {string} [options.cacheName] Cache title to store and retrieve
|
|
3501
3501
|
* requests. Defaults to the cache names provided by
|
|
3502
3502
|
* {@link workbox-core.cacheNames}.
|
|
3503
3503
|
* @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
|
|
@@ -3512,7 +3512,7 @@ define(["exports"], function (exports) {
|
|
|
3512
3512
|
*/
|
|
3513
3513
|
constructor(options = {}) {
|
|
3514
3514
|
/**
|
|
3515
|
-
* Cache
|
|
3515
|
+
* Cache title to store and retrieve
|
|
3516
3516
|
* requests. Defaults to the cache names provided by
|
|
3517
3517
|
* {@link workbox-core.cacheNames}.
|
|
3518
3518
|
*
|
|
@@ -3728,7 +3728,7 @@ define(["exports"], function (exports) {
|
|
|
3728
3728
|
* cache, cache options, fetch options and plugins are used (per the current
|
|
3729
3729
|
* strategy instance).
|
|
3730
3730
|
*
|
|
3731
|
-
* @
|
|
3731
|
+
* @title _handle
|
|
3732
3732
|
* @instance
|
|
3733
3733
|
* @abstract
|
|
3734
3734
|
* @function
|
|
@@ -4263,7 +4263,7 @@ define(["exports"], function (exports) {
|
|
|
4263
4263
|
/**
|
|
4264
4264
|
*
|
|
4265
4265
|
* @param {Object} [options]
|
|
4266
|
-
* @param {string} [options.cacheName] Cache
|
|
4266
|
+
* @param {string} [options.cacheName] Cache title to store and retrieve
|
|
4267
4267
|
* requests. Defaults to the cache names provided by
|
|
4268
4268
|
* {@link workbox-core.cacheNames}.
|
|
4269
4269
|
* @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}
|
|
@@ -4777,7 +4777,7 @@ define(["exports"], function (exports) {
|
|
|
4777
4777
|
* [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)
|
|
4778
4778
|
* with the following differences:
|
|
4779
4779
|
*
|
|
4780
|
-
* - It knows what the
|
|
4780
|
+
* - It knows what the title of the precache is, and only checks in that cache.
|
|
4781
4781
|
* - It allows you to pass in an "original" URL without versioning parameters,
|
|
4782
4782
|
* and it will automatically look up the correct cache key for the currently
|
|
4783
4783
|
* active revision of that URL.
|
|
@@ -4866,7 +4866,7 @@ define(["exports"], function (exports) {
|
|
|
4866
4866
|
*
|
|
4867
4867
|
* @param {URL} urlObject The original URL.
|
|
4868
4868
|
* @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against
|
|
4869
|
-
* each search parameter
|
|
4869
|
+
* each search parameter title. Matches mean that the search parameter should be
|
|
4870
4870
|
* ignored.
|
|
4871
4871
|
* @return {URL} The URL with any ignored search parameters removed.
|
|
4872
4872
|
*
|
|
@@ -5118,7 +5118,7 @@ define(["exports"], function (exports) {
|
|
|
5118
5118
|
* This should be safe to use as long as you don't include `substringToFind`
|
|
5119
5119
|
* (defaulting to `-precache-`) in your non-precache cache names.
|
|
5120
5120
|
*
|
|
5121
|
-
* @param {string} currentPrecacheName The cache
|
|
5121
|
+
* @param {string} currentPrecacheName The cache title currently in use for
|
|
5122
5122
|
* precaching. This cache won't be deleted.
|
|
5123
5123
|
* @param {string} [substringToFind='-precache-'] Cache names which include this
|
|
5124
5124
|
* substring will be deleted (excluding `currentPrecacheName`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbki.ng/site",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "code behind bbki.ng",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"url": "git+https://github.com/bbbottle/bbki.ng.git"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@bbki.ng/components": "workspace:2.
|
|
19
|
+
"@bbki.ng/components": "workspace:2.2.0",
|
|
20
20
|
"@supabase/supabase-js": "^1.30.6",
|
|
21
21
|
"classnames": "2.3.1",
|
|
22
22
|
"react": "^18.0.0",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"react-hotkeys-hook": "^3.4.3",
|
|
26
26
|
"react-router-dom": "6",
|
|
27
27
|
"sonner": "1.4.0",
|
|
28
|
-
"swr": "^
|
|
28
|
+
"swr": "^2.2.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@bbki.ng/stylebase": "workspace:0.0
|
|
31
|
+
"@bbki.ng/stylebase": "workspace:0.1.0",
|
|
32
32
|
"@mdx-js/mdx": "2.0.0-next.9",
|
|
33
33
|
"@mdx-js/react": "^1.6.22",
|
|
34
34
|
"@mdx-js/rollup": "3.0.0",
|
package/src/articles/loading.mdx
CHANGED
|
@@ -10,15 +10,18 @@ import { SpinnerDemo } from "@/demo/SpinnerDemo";
|
|
|
10
10
|
import { DemoBox } from "@/demo/DemoBox";
|
|
11
11
|
|
|
12
12
|
<SpinnerDemo />
|
|
13
|
-
## 前言
|
|
14
|
-
|
|
13
|
+
## 前言 >《她》是讲述在不远的未来人与人工智能相爱的科幻爱情电影。主人公西奥多(杰昆·菲尼克斯
|
|
14
|
+
Joaquin Phoenix 饰)是一位信件撰写人,心思细腻而深邃,能写出最感人肺腑的信件。他刚结束与妻子凯瑟琳(鲁妮·玛拉
|
|
15
|
+
Rooney Mara 饰)的婚姻,还没走出心碎的阴影。一次偶然机会让他接触到最新的人工智能系统
|
|
16
|
+
OS1 ……
|
|
15
17
|
|
|
16
18
|
相信大多数人,在看这部电影的时候,不仅仅会被剧情打动,也会被电影的镜头美学吸引。《她》呈现出一个没有被冷酷金属主导的科幻世界,那里光线柔和,色调温暖。人们的着装跟我们并没有太大区别,但是人工智能操作系统能完成的事情以及普及程度,现在看来很难想象。有意思的是,技术的发展并没有消灭「加载时间」,OS1 在安装的时候,西奥多依然需要等待。屏幕上精美的加载动画给我留下深刻的印象,从那时候起,就希望动画能出现在我的网站上。
|
|
17
19
|
|
|
18
20
|
## 灵感
|
|
21
|
+
|
|
19
22
|
根据 Atwood's Law
|
|
20
23
|
|
|
21
|
-
> Any application that
|
|
24
|
+
> Any application that *can* be written in JavaScript, *will* eventually be written in JavaScript.
|
|
22
25
|
|
|
23
26
|
肯定有人已经用 JS 实现 OS1 系统的加载动画。没错,这个人是 [@psyonline](https://codepen.io/psyonline),他的[在线示例](https://codepen.io/psyonline/pen/yayYWg)也非常惊艳。
|
|
24
27
|
那么,直接复制粘贴过来可以吗?该示例使用 Three.js 实现,写这篇文章的时候,根据 [bundlephobia](https://bundlephobia.com/package/three@0.142.0) 显示,`three@142.0` 压缩后仍然有 `596.9kB`。对于个人网站,仅仅用来实现一个加载动画,无疑很不划算。
|
|
@@ -29,16 +32,21 @@ import { DemoBox } from "@/demo/DemoBox";
|
|
|
29
32
|
- [W](https://xem.github.io/W/)
|
|
30
33
|
|
|
31
34
|
前者压缩后,只有 `5.4KB`。正是我想找的工具。接下来,只需要 3 步就能实现文首的动画:
|
|
35
|
+
|
|
32
36
|
1. 熟悉 phenomenon 的 API
|
|
33
37
|
2. 用 phenomenon 绘制曲线
|
|
34
38
|
3. 让曲线动起来
|
|
35
39
|
|
|
36
40
|
## 行动
|
|
41
|
+
|
|
37
42
|
### WebGL
|
|
43
|
+
|
|
38
44
|
进行第一步时,发现自己还完全不了解 WebGL。看完 [The Book of Shaders](https://thebookofshaders.com/) 后,能基本理解 phenomenon 的[示例](https://codepen.io/collection/AOpMrm/)。至此,已经可以复制粘贴示例并修修改改,验证书中的概念和自己的理解。直到手痒痒想绘制自己想要的曲线,就进行到下一步。
|
|
39
45
|
|
|
40
46
|
### Curve
|
|
47
|
+
|
|
41
48
|
@psyonline [示例](https://codepen.io/psyonline/pen/yayYWg)中不难找到绘制曲线的代码:
|
|
49
|
+
|
|
42
50
|
```
|
|
43
51
|
function(percent) {
|
|
44
52
|
var x = length*Math.sin(pi2*percent)
|
|
@@ -61,6 +69,7 @@ function(percent) {
|
|
|
61
69
|
```
|
|
62
70
|
|
|
63
71
|
我的主要目标变成移植这段代码,其中 `pi2`, `length` 和 `radius` 均为常量,原文取值:
|
|
72
|
+
|
|
64
73
|
```
|
|
65
74
|
const length = 30;
|
|
66
75
|
const radius = 5.6;
|
|
@@ -68,10 +77,11 @@ const pi2 = Math.PI * 2;
|
|
|
68
77
|
```
|
|
69
78
|
|
|
70
79
|
还有一个比较重要的参数 `percent`,打印发现取值范围为 0 到 1。有了这些,我愉快的写好 phenomenon 的 attributes:
|
|
80
|
+
|
|
71
81
|
```
|
|
72
82
|
const attributes = [
|
|
73
83
|
{
|
|
74
|
-
|
|
84
|
+
title: 'pos',
|
|
75
85
|
data: (index: number, total: number) => {
|
|
76
86
|
const percent = index / total;
|
|
77
87
|
// ...
|
|
@@ -84,13 +94,17 @@ const attributes = [
|
|
|
84
94
|
},
|
|
85
95
|
]
|
|
86
96
|
```
|
|
97
|
+
|
|
87
98
|
然后得到一个空白的画布
|
|
99
|
+
|
|
88
100
|
<DemoBox />
|
|
89
101
|
|
|
90
102
|
因为对 WebGL 的了解依然接近于 0 在胡乱猜测并修改一通后,画布依然一片空白,于是沮丧放弃。直到晚上,突然想着将半径和长度都降低两个数量级,画布中终于出现一个扭曲的麻花。上次这么开心还是上一次。
|
|
91
103
|
|
|
92
104
|
### Matrix
|
|
105
|
+
|
|
93
106
|
现在可以回想一下大学课堂线性代数中矩阵运算的知识。回想不起来也没关系,如果有一个正在念大学的弟弟的话,他会帮助写出 `rotate` 方法。
|
|
107
|
+
|
|
94
108
|
```
|
|
95
109
|
export const VERTEX_SHADER = `
|
|
96
110
|
attribute vec3 pos;
|
|
@@ -121,6 +135,7 @@ export const VERTEX_SHADER = `
|
|
|
121
135
|
这些碎片代码,和碎片想法,最终组装出心心念念的动画。
|
|
122
136
|
|
|
123
137
|
## 参考链接
|
|
138
|
+
|
|
124
139
|
1. https://codepen.io/psyonline/pen/yayYWg
|
|
125
140
|
2. https://twitter.com/shuding_/status/1475916082875666441
|
|
126
141
|
3. https://thebookofshaders.com/
|
|
@@ -3,6 +3,8 @@ import { toast } from "sonner";
|
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
import { useRegisterSW } from "virtual:pwa-register/react";
|
|
5
5
|
|
|
6
|
+
const intervalMS = 60 * 60 * 1000;
|
|
7
|
+
|
|
6
8
|
export const ReloadPrompt = () => {
|
|
7
9
|
const {
|
|
8
10
|
needRefresh: [needRefresh, setNeedRefresh],
|
|
@@ -11,11 +13,20 @@ export const ReloadPrompt = () => {
|
|
|
11
13
|
onRegisterError(error: any) {
|
|
12
14
|
console.log("SW registration error", error);
|
|
13
15
|
},
|
|
16
|
+
onRegisteredSW(
|
|
17
|
+
swScriptUrl: string,
|
|
18
|
+
registration: ServiceWorkerRegistration
|
|
19
|
+
) {
|
|
20
|
+
console.log("SW registered: ", swScriptUrl, registration);
|
|
21
|
+
registration &&
|
|
22
|
+
setInterval(() => {
|
|
23
|
+
registration.update().then(() => {});
|
|
24
|
+
}, intervalMS);
|
|
25
|
+
},
|
|
14
26
|
onOfflineReady() {
|
|
15
27
|
console.log("App is offline-ready");
|
|
16
28
|
},
|
|
17
29
|
});
|
|
18
|
-
|
|
19
30
|
if (!needRefresh) {
|
|
20
31
|
return null;
|
|
21
32
|
}
|
|
@@ -42,12 +42,12 @@ export const PHOTO_PROJECTS: PhotoProject[] = [
|
|
|
42
42
|
],
|
|
43
43
|
},
|
|
44
44
|
// {
|
|
45
|
-
//
|
|
45
|
+
// title: "光前",
|
|
46
46
|
// description: "城中村,个人深圳经历出入口。",
|
|
47
47
|
// images: [OSS_PHOTOS.oldMan, OSS_PHOTOS.oldWomen, OSS_PHOTOS.littleChild],
|
|
48
48
|
// },
|
|
49
49
|
// {
|
|
50
|
-
//
|
|
50
|
+
// title: "三百零一",
|
|
51
51
|
// description: "陋室光影",
|
|
52
52
|
// images: [OSS_PHOTOS.phone, OSS_PHOTOS.chair],
|
|
53
53
|
// },
|
package/src/constants/routes.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useSupabaseSession } from "@/hooks/use_supa_session";
|
|
2
|
+
import { apiFetcher, withToken } from "@/utils";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
export const useAuthedFetcher = () => {
|
|
6
|
+
const { access_token: token } = useSupabaseSession() || {};
|
|
7
|
+
return useCallback(withToken(apiFetcher)(token), [token]);
|
|
8
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useTextPlainFile } from "@/hooks/use_text_plain_file";
|
|
2
|
+
import { useContext, useEffect } from "react";
|
|
3
|
+
import { usePost } from "@/hooks/use_post";
|
|
4
|
+
import { GlobalLoadingContext } from "@/global_loading_state_provider";
|
|
5
|
+
|
|
6
|
+
export type fileReader = (f: File) => void;
|
|
7
|
+
export const useFile2Post = (): fileReader => {
|
|
8
|
+
const { content, title, reader } = useTextPlainFile();
|
|
9
|
+
|
|
10
|
+
const { setIsLoading } = useContext(GlobalLoadingContext);
|
|
11
|
+
|
|
12
|
+
const post = usePost();
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!content || !title) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
|
|
21
|
+
post({ title, content })
|
|
22
|
+
.then((r) => {
|
|
23
|
+
console.log(r);
|
|
24
|
+
})
|
|
25
|
+
.finally(() => {
|
|
26
|
+
setIsLoading(false);
|
|
27
|
+
});
|
|
28
|
+
}, [title, content]);
|
|
29
|
+
|
|
30
|
+
return (f: File) => {
|
|
31
|
+
reader(f);
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { API } from "@/constants/routes";
|
|
3
|
+
import { useAuthedFetcher } from "@/hooks/use_authed_fetcher";
|
|
4
|
+
import useSWRMutation from "swr/mutation";
|
|
5
|
+
|
|
6
|
+
export const usePost = () => {
|
|
7
|
+
const authedFetcher = useAuthedFetcher();
|
|
8
|
+
|
|
9
|
+
const req = useCallback(
|
|
10
|
+
async (
|
|
11
|
+
url: string,
|
|
12
|
+
{ arg }: { arg: { title: string; content: string } }
|
|
13
|
+
) => {
|
|
14
|
+
return authedFetcher(url, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
body: JSON.stringify(arg),
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
[authedFetcher]
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const { trigger } = useSWRMutation(API.POST, req);
|
|
23
|
+
|
|
24
|
+
return trigger;
|
|
25
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export type Result = {
|
|
4
|
+
content: string;
|
|
5
|
+
title: string;
|
|
6
|
+
reader: (f: File) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const useTextPlainFile = (): Result => {
|
|
10
|
+
const [content, setContent] = useState("");
|
|
11
|
+
const [file, setFile] = useState<File | null>(null);
|
|
12
|
+
|
|
13
|
+
const reader = new FileReader();
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (file != null) {
|
|
17
|
+
reader.readAsText(file, "utf-8");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reader.onload = function () {
|
|
21
|
+
setContent(reader.result as string);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return () => {
|
|
25
|
+
reader.onload = null;
|
|
26
|
+
};
|
|
27
|
+
}, [file]);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
content,
|
|
31
|
+
title: file?.name ?? "",
|
|
32
|
+
reader: (f: File) => {
|
|
33
|
+
setFile(f);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ArticleList } from "./consts";
|
|
3
|
-
import {
|
|
3
|
+
import { LinkProps, DropZone } from "@bbki.ng/components";
|
|
4
4
|
import { useRouteName } from "@/hooks";
|
|
5
5
|
import { usePosts } from "@/hooks/use_posts";
|
|
6
|
-
import { CenterLinkList
|
|
6
|
+
import { CenterLinkList } from "@/components";
|
|
7
|
+
import { useAuthed } from "@/hooks/use_authed";
|
|
8
|
+
import { useFile2Post } from "@/hooks/use_file_to_post";
|
|
7
9
|
|
|
8
10
|
type TxtProps = {
|
|
9
11
|
title?: string;
|
|
@@ -30,9 +32,12 @@ const Posts = (props: TxtProps) => {
|
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
export default (props: TxtProps) => {
|
|
35
|
+
const reader = useFile2Post();
|
|
36
|
+
const isKing = useAuthed();
|
|
37
|
+
|
|
33
38
|
return (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
<DropZone onDrop={reader} disabled={!isKing}>
|
|
40
|
+
<Posts {...props} />
|
|
41
|
+
</DropZone>
|
|
37
42
|
);
|
|
38
43
|
};
|