Repository navigation
Expand file tree
/
Copy pathReleaseNotesSheet.tsx
More file actions
122 lines (108 loc) · 3.36 KB
/
Copy pathReleaseNotesSheet.tsx
File metadata and controls
122 lines (108 loc) · 3.36 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
import {
Device,
Markdown,
type MarkdownProps,
NavigationStack,
Path,
type PresentationDetent,
Script,
ScrollView,
useEffect,
useState,
} from "scripting";
type MarkdownReleaseNotesSheetConfig = {
markdownFile?: string;
storageKey?: string;
title?: string;
theme?: MarkdownProps["theme"];
detents?: PresentationDetent[];
};
const DEFAULT_CHANGELOG_FILE = "changelog.md";
function normalizeMarkdownContent(content: string) {
return content.replace(/\r\n/g, "\n").trim();
}
function hashString(value: string) {
let hash = 2166136261;
for (let index = 0; index < value.length; index += 1) {
hash ^= value.charCodeAt(index);
hash = Math.imul(hash, 16777619);
}
return (hash >>> 0).toString(16);
}
function MarkdownReleaseNotesSheet(props: {
content: string;
title?: string;
theme?: MarkdownProps["theme"];
detents?: PresentationDetent[];
}) {
const useGlassPresentation = Number.parseInt(Device.systemVersion, 10) >= 26;
return (
<NavigationStack presentationBackground={useGlassPresentation ? "clear" : undefined}>
<ScrollView
background="clear"
scrollContentBackground="hidden"
navigationTitle={props.title ?? "更新内容"}
navigationBarTitleDisplayMode="inline"
toolbarBackgroundVisibility="hidden"
presentationDragIndicator="visible"
presentationDetents={props.detents ?? ["medium", "large"]}
presentationBackground={useGlassPresentation ? "clear" : undefined}
padding={{ top: 24, leading: 18, bottom: 18, trailing: 18 }}
>
<Markdown
content={props.content}
theme={props.theme ?? "basic"}
useDefaultHighlighterTheme
scrollable={false}
background="clear"
/>
</ScrollView>
</NavigationStack>
);
}
export function useMarkdownReleaseNotesSheet(
config: MarkdownReleaseNotesSheetConfig = {},
) {
const markdownFile = config.markdownFile ?? DEFAULT_CHANGELOG_FILE;
const storageKey = config.storageKey ??
`release-notes:${markdownFile}:last-seen-hash`;
const [releaseNotesContent, setReleaseNotesContent] = useState("");
const [releaseNotesHash, setReleaseNotesHash] = useState("");
const [showReleaseNotes, setShowReleaseNotes] = useState(false);
useEffect(() => {
async function loadReleaseNotes() {
const filePath = Path.join(Script.directory, markdownFile);
const exists = await FileManager.exists(filePath);
if (!exists) return;
const content = normalizeMarkdownContent(
await FileManager.readAsString(filePath),
);
if (!content) return;
const contentHash = hashString(content);
const lastSeenHash = Storage.get<string>(storageKey);
if (lastSeenHash === contentHash) return;
setReleaseNotesContent(content);
setReleaseNotesHash(contentHash);
setShowReleaseNotes(true);
}
void loadReleaseNotes();
}, []);
function setReleaseNotesPresented(isPresented: boolean) {
if (!isPresented && releaseNotesHash) {
Storage.set(storageKey, releaseNotesHash);
}
setShowReleaseNotes(isPresented);
}
return {
isPresented: showReleaseNotes,
onChanged: setReleaseNotesPresented,
content: (
<MarkdownReleaseNotesSheet
content={releaseNotesContent}
title={config.title}
theme={config.theme}
detents={config.detents}
/>
),
};
}