-
-
Notifications
You must be signed in to change notification settings - Fork 512
Expand file tree
/
Copy pathutils.ts
More file actions
230 lines (210 loc) · 6.66 KB
/
utils.ts
File metadata and controls
230 lines (210 loc) · 6.66 KB
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
import intl from "react-intl-universal"
import { ThunkAction, ThunkDispatch } from "redux-thunk"
import { AnyAction } from "redux"
import { RootState } from "./reducer"
import Parser from "rss-parser"
import Url from "url"
import { SearchEngines } from "../schema-types"
export enum ActionStatus {
Request,
Success,
Failure,
Intermediate,
}
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
AnyAction
>
export type AppDispatch = ThunkDispatch<RootState, undefined, AnyAction>
const rssParser = new Parser({
customFields: {
item: [
"thumb",
"image",
["content:encoded", "fullContent"],
["media:content", "mediaContent", { keepArray: true }],
],
},
})
type extractGeneric<Type> = Type extends Parser<infer _, infer U> ? U : never
export type MyParserItem = extractGeneric<typeof rssParser> & Parser.Item
export async function parseRSS(url: string) {
let html: string
try {
html = await window.utils.fetchText(url)
} catch (e) {
throw new Error(intl.get("log.networkError"))
}
try {
return await rssParser.parseString(html)
} catch {
throw new Error(intl.get("log.parseError"))
}
}
export const domParser = new DOMParser()
export async function fetchFavicon(url: string) {
try {
url = url.split("/").slice(0, 3).join("/")
const html = await window.utils.fetchText(url)
let dom = domParser.parseFromString(html, "text/html")
let links = dom.getElementsByTagName("link")
for (let link of links) {
let rel = link.getAttribute("rel")
if (
(rel === "icon" || rel === "shortcut icon") &&
link.hasAttribute("href")
) {
let href = link.getAttribute("href")
let parsedUrl = Url.parse(url)
if (href.startsWith("//")) return parsedUrl.protocol + href
else if (href.startsWith("/")) return url + href
else return href
}
}
url = url + "/favicon.ico"
if (await validateFavicon(url)) {
return url
} else {
return null
}
} catch {
return null
}
}
export async function validateFavicon(url: string) {
let flag = false
try {
const result = await fetch(url, { credentials: "omit" })
if (
result.status == 200 &&
result.headers.has("Content-Type") &&
result.headers.get("Content-Type").startsWith("image")
) {
flag = true
}
} finally {
return flag
}
}
export function htmlDecode(input: string) {
var doc = domParser.parseFromString(input, "text/html")
return doc.documentElement.textContent
}
export const urlTest = (s: string) =>
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi.test(
s
)
export const getWindowBreakpoint = () => window.outerWidth >= 1440
export const cutText = (s: string, length: number) => {
return s.length <= length ? s : s.slice(0, length) + "…"
}
export function getSearchEngineName(engine: SearchEngines) {
switch (engine) {
case SearchEngines.Google:
return intl.get("searchEngine.google")
case SearchEngines.Bing:
return intl.get("searchEngine.bing")
case SearchEngines.Baidu:
return intl.get("searchEngine.baidu")
case SearchEngines.DuckDuckGo:
return intl.get("searchEngine.duckduckgo")
}
}
export function webSearch(text: string, engine = SearchEngines.Google) {
switch (engine) {
case SearchEngines.Google:
return window.utils.openExternal(
"https://www.google.com/search?q=" + encodeURIComponent(text)
)
case SearchEngines.Bing:
return window.utils.openExternal(
"https://www.bing.com/search?q=" + encodeURIComponent(text)
)
case SearchEngines.Baidu:
return window.utils.openExternal(
"https://www.baidu.com/s?wd=" + encodeURIComponent(text)
)
case SearchEngines.DuckDuckGo:
return window.utils.openExternal(
"https://duckduckgo.com/?q=" + encodeURIComponent(text)
)
}
}
export function mergeSortedArrays<T>(
a: T[],
b: T[],
cmp: (x: T, y: T) => number
): T[] {
let merged = new Array<T>()
let i = 0
let j = 0
while (i < a.length && j < b.length) {
if (cmp(a[i], b[j]) <= 0) {
merged.push(a[i++])
} else {
merged.push(b[j++])
}
}
while (i < a.length) merged.push(a[i++])
while (j < b.length) merged.push(b[j++])
return merged
}
export function byteToMB(B: number) {
let MB = Math.round(B / 1048576)
return MB + "MB"
}
function byteLength(str: string) {
var s = str.length
for (var i = str.length - 1; i >= 0; i--) {
var code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) s++
else if (code > 0x7ff && code <= 0xffff) s += 2
if (code >= 0xdc00 && code <= 0xdfff) i-- //trail surrogate
}
return s
}
export function calculateItemSize(): Promise<number> {
return new Promise((resolve, reject) => {
let result = 0
let openRequest = window.indexedDB.open("itemsDB")
openRequest.onsuccess = () => {
let db = openRequest.result
let objectStore = db.transaction("items").objectStore("items")
let cursorRequest = objectStore.openCursor()
cursorRequest.onsuccess = () => {
let cursor = cursorRequest.result
if (cursor) {
result += byteLength(JSON.stringify(cursor.value))
cursor.continue()
} else {
resolve(result)
}
}
cursorRequest.onerror = () => reject()
}
openRequest.onerror = () => reject()
})
}
export function validateRegex(regex: string, flags = ""): RegExp {
try {
return new RegExp(regex, flags)
} catch {
return null
}
}
export function platformCtrl(
e: React.MouseEvent | React.KeyboardEvent | MouseEvent | KeyboardEvent
) {
return window.utils.platform === "darwin" ? e.metaKey : e.ctrlKey
}
export function initTouchBarWithTexts() {
window.utils.initTouchBar({
menu: intl.get("nav.menu"),
search: intl.get("search"),
refresh: intl.get("nav.refresh"),
markAll: intl.get("nav.markAllRead"),
notifications: intl.get("nav.notifications"),
})
}