-
Notifications
You must be signed in to change notification settings - Fork 13
/
Article.ts
135 lines (109 loc) · 3.26 KB
/
Article.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { HTTPError } from 'koajax';
import {
BiDataQueryOptions,
BiDataTable,
LarkPageData,
makeSimpleFilter,
TableCellLink,
TableCellValue,
TableRecord,
} from 'mobx-lark';
import { Filter, NewData, toggle } from 'mobx-restful';
import { buildURLData, isEmpty } from 'web-utility';
import { blobClient, larkClient } from '../Base';
export type BaseArticle = Record<
| 'id'
| 'title'
| 'author'
| 'license'
| 'type'
| 'tags'
| 'summary'
| 'image'
| 'publishedAt'
| 'link'
| 'alias',
TableCellValue
>;
export interface Article extends BaseArticle {
content?: string;
}
export const ARTICLE_BASE_ID = process.env.NEXT_PUBLIC_ARTICLE_BASE_ID!;
export const ARTICLE_TABLE_ID = process.env.NEXT_PUBLIC_ARTICLE_TABLE_ID!;
export class ArticleModel extends BiDataTable<Article>() {
client = larkClient;
constructor(appId = ARTICLE_BASE_ID, tableId = ARTICLE_TABLE_ID) {
super(appId, tableId);
}
requiredKeys = ['title', 'image', 'publishedAt'] as const;
sort = { publishedAt: 'DESC' } as const;
queryOptions: BiDataQueryOptions = { text_field_as_array: false };
currentRecommend?: ArticleModel;
normalize({
id,
fields: { tags, link, ...fields },
}: TableRecord<Omit<Article, 'content'>>): Article {
return {
...fields,
id,
tags: (tags as string)?.trim().split(/\s+/),
link: (link as TableCellLink)?.link,
};
}
@toggle('downloading')
async getOne(alias: string) {
const { body } = await this.client.get<
LarkPageData<TableRecord<BaseArticle>>
>(
`${this.baseURI}?${buildURLData({
filter: makeSimpleFilter({ alias }, '='),
})}`,
);
const [rawItem] = body!.data!.items || [];
if (!rawItem)
throw new HTTPError(`Article "${alias}" is not found`, {
status: 404,
statusText: 'Not found',
headers: {},
});
const item = this.normalize(rawItem);
const path = `article/${
(item.link as string).split('/').slice(-1)[0]
}.html`;
const { body: raw } = await blobClient.get<ArrayBuffer>(path);
const content = new TextDecoder().decode(raw);
this.currentRecommend = new SearchArticleModel();
await this.currentRecommend.getList({ tags: item.tags });
return (this.currentOne = { ...item, content });
}
}
export class SearchArticleModel extends ArticleModel {
makeFilter(filter: NewData<Article>) {
return isEmpty(filter) ? '' : makeSimpleFilter(filter, 'contains', 'OR');
}
}
export default new ArticleModel();
export class CalendarSearchArticleModel extends ArticleModel {
currentDate?: Date;
async getMonthList(filter: Filter<Article>, date = new Date()) {
this.currentDate = date;
try {
this.clearList();
return await this.getAll(filter);
} finally {
this.currentDate = undefined;
}
}
makeFilter({ ...filter }: NewData<Article>) {
const [year, month] =
this.currentDate?.toJSON().split('T')[0].split('-') || [];
const nextMonth = (+month + 1 + '').padStart(2, '0');
return [
year && `CurrentValue.[publishedAt]>=TODATE("${year}-${month}-01")`,
year && `CurrentValue.[publishedAt]<TODATE("${year}-${nextMonth}-01")`,
!isEmpty(filter) && makeSimpleFilter(filter),
]
.filter(Boolean)
.join('&&');
}
}