缓存机制完善

This commit is contained in:
Searchstars
2024-09-22 17:27:37 +08:00
parent 4d79405b44
commit e4fdb21a65
19 changed files with 541 additions and 137 deletions
+10 -10
View File
@@ -13164,6 +13164,16 @@
"node": ">=0.6"
}
},
"node_modules/request/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -16178,16 +16188,6 @@
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/ux-types": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ux-types/-/ux-types-0.1.1.tgz",
+4 -2
View File
@@ -9,6 +9,7 @@ import * as ambientlight from "./ambientlight"
import * as jumpcheck from "./jumpcheck"
import * as interconnecter from "./interconnecter"
import * as htmlparser from "./htmlparser"
import * as savedcontent from "./savedcontent"
export default {
animengine: animengine,
biliclient: null,
@@ -20,10 +21,11 @@ export default {
interconnecter: interconnecter,
articletools: articletools,
htmlparser: htmlparser,
onCreate() {
savedcontent: savedcontent,
async onCreate() {
console.log("app created")
settings.loadSettings()
await this.savedcontent.SavedContentManager.initialize()
this.biliclient = new BilibiliClient(this.settings.SETTINGS.enableInterconnectMode, this.interconnecter)
},
onDestroy() {
-1
View File
@@ -394,7 +394,6 @@ class BilibiliClient {
async getSearchHotwords(): Promise<any> {
const url = "https://s.search.bilibili.com/main/hotword";
const response = await this.getRequest(url);
console.log(response);
return response.data.list;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

+51
View File
@@ -0,0 +1,51 @@
<template>
<div class="articlebg">
<text class="articletitle" style="margin-left: 18px">{{ article.title }}</text>
<text if="{{showCategory}}" style="font-size: 20px; font-weight: 600; color: #9e9e9e; margin-top: 6px; margin-left: 18px">
类型:{{ article.category_name }}
</text>
</div>
</template>
<script>
export default {
props: {
article: {
default: {}
},
showCategory: {
default: ""
}
},
onInit(){
if(this.showCategory){
this.showCategory = true
}
else{
this.showCategory = false
}
}
}
</script>
<style>
.articlebg {
width: 72%;
min-height: 120px;
max-height: 120px;
justify-content: center;
margin-top: 5px;
background-color: #262626af;
flex-direction: column;
border-radius: 24px;
}
.articletitle {
font-size: 24px;
font-weight: 600;
margin-top: 13px;
width: 89%;
max-height: 70px;
text-overflow: ellipsis;
}
</style>
@@ -0,0 +1,51 @@
<template>
<div class="featurebtn" style="margin-top: {{marginTop}}; background-color: {{color_bg}}" @click="Click">
<text class="featurebtn_text" style="color: {{color_text}}">{{text}}</text>
</div>
</template>
<script>
export default {
props: {
text: {
default: ""
},
marginTop: {
default: ""
},
primary: {
default: "" //默认false 即空字符串
},
color_bg: {
default: "#262626"
},
color_text: {
default: "#ffffff"
}
},
Click(){
this.$emit('click')
},
onInit(){
if(this.primary){
this.color_bg = "#FF7DA8"
}
}
}
</script>
<style>
.featurebtn {
width: 322px;
height: 81px;
border-radius: 90px;
background-color: #262626;
justify-content: center;
align-items: center;
}
.featurebtn_text {
font-size: 40px;
font-weight: 600;
}
</style>
+6
View File
@@ -97,6 +97,12 @@
},
"pages/articleshow": {
"component": "articleshow"
},
"pages/articlesave": {
"component": "articlesave"
},
"pages/savedcontent": {
"component": "savedcontent"
}
}
}
+10 -1
View File
@@ -30,6 +30,14 @@
<text class="btntext">设置</text>
</div>
</div>
<div class="btnline" style="margin-top: 15px;">
<div class="btn" onclick="GoPage({{pages[4]}})">
<div class="btnbase">
<image src="/common/arealist_savedcontent.png" class="btnimg"></image>
</div>
<text class="btntext">缓存</text>
</div>
</div>
<text style="font-size: 24px; font-weight: 600; margin-top: 50px">上滑进入搜索页</text>
<text style="margin-top: 450px;"></text>
</scroll>
@@ -49,7 +57,8 @@ export default {
"main",
"dynamic",
"mypage",
"settings"
"settings",
"savedcontent"
],
anims: {
ChangePageFX: null,
+56
View File
@@ -0,0 +1,56 @@
<import name="title-bar" src="../../components/TitleBar/TitleBar.ux"></import>
<import name="default-button" src="../../components/DefaultButton/DefaultButton.ux"></import>
<template>
<div class="page">
<title-bar title="专栏缓存"></title-bar>
<image src="/common/articlesave_tips.png" style="margin-top: 35px"></image>
<default-button margin-top="20px" text="阅读更多信息" @click="ReadMore"></default-button>
<default-button margin-top="20px" primary="true" text="缓存" @click="Download"></default-button>
<text style="margin-top: 50px"></text>
</div>
</template>
<script>
import prompt from "@system.prompt"
import router from "@system.router"
export default {
public: {
cvtitle: "",
cvid: ""
},
ReadMore(){
router.push({
uri: "pages/textdetail",
params: {
text: "既然你能点进这里,说明你肯定是希望了解更多有关此问题的原因。说的详细点,每当你使用搜索功能搜索专栏并点进来时,都会跳转到这个页面,而不是会动态检测你的手表RAM够不够再跳转,因为它永远都不可能够。为什么呢?目前小米发布的S系列手表,无论是S3还是S4 Sport,都只配备了32M内存,分配给快应用的内存,实际测试下来只有4M不到。再如此有限的内存中能够开发一个包含如此多功能的应用程序属实不易,目前几乎所有功能都是卡在内存爆炸的边缘运行的。而专栏渲染需要Html渲染器,Html渲染器使用递归的自定义组件进行渲染,这样一来,内存占用自然及其多。在搜索页,内存占用就已高达3.1M,而Html渲染器占用的内存约有2.2M,加起来已经严重超限了。因此我们只能采用缓存的方式,缓存下来再看,不必进入搜索页面占内存,也减少了网络请求带来的内存开销,这是目前唯一的办法。",
titletext: "更多信息"
}
})
},
async Download(){
prompt.showToast({
message: "开始缓存"
})
var articleHtml = await this.$app.$def.biliclient.getArticle(this.cvid)
console.log("articleHtml=" + articleHtml)
var id = await this.$app.$def.savedcontent.SavedContentManager.storeContent(this.cvtitle, articleHtml, "article")
console.log("id=" + id)
prompt.showToast({
message: "专栏 " + this.cvtitle + " 缓存完毕!存储id=" + id,
duration: 5000
})
}
}
</script>
<style>
.page {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
position: absolute;
width: 100%;
}
</style>
+39 -15
View File
@@ -3,26 +3,27 @@
<template>
<div class="page">
<title-bar title="专栏阅读"></title-bar>
<title-bar title="专栏阅读" static></title-bar>
<image style="position: absolute; margin-top: 245px" if="{{show_loading}}" src="{{anims.loading_src.value}}"></image>
<div if="{{showarticle}}" class="articlebox">
<div class="infobox">
<text class="title">{{article.readInfo.title}}</text>
<div class="infofield">
<image src="/common/article_up.png"></image>
<div class="infofield" static>
<image src="/common/article_up.png" static></image>
<text class="infofield_text">{{article.readInfo.author.name}}</text>
</div>
<div class="infofield">
<image src="/common/article_clock.png"></image>
<text class="infofield_text">预计阅读用时</text>
<div class="infofield" static>
<image src="/common/article_clock.png" static></image>
<text class="infofield_text" static>预计阅读用时</text>
<text class="pinknum" style="margin-left: 5px">{{this.$app.$def.articletools.estimateReadingTime(article.readInfo.content)}}</text>
<text class="infofield_text" style="margin-left: 5px">分钟</text>
<text class="infofield_text" style="margin-left: 5px" static>分钟</text>
</div>
</div>
<div style="margin-top: 22px; width: 80%; justify-content: center">
<html-renderer doms="{{article_content}}" @open-image="OpenImage"></html-renderer>
<html-renderer doms="{{article_content[current_page_num]}}" @open-image="OpenImage"></html-renderer>
</div>
</div>
<text style="margin-top: 25px"></text>
</div>
</template>
@@ -30,14 +31,18 @@
import router from "@system.router"
export default {
public: {
cvid: ""
cvid: "cv1",
offline: false,
// 仅在离线模式(直读本地html)时需要传入该参数
article_html: ""
},
private: {
article_html: "",
article: {},
article_content: {},
showarticle: false,
show_loading: false,
current_page_num: 0,
max_page_num: 0,
anims: {
loading: null,
loading_src: {value: "/common/seqanims/loadingWhite/icons8-loading_颜色反转-1.png"}
@@ -51,18 +56,37 @@ export default {
}
})
},
SplitArticle(arr, split_count) {
let result = [];
for (let i = 0; i < arr.length; i += split_count) {
result.push(arr.slice(i, i + split_count));
}
return result;
},
async LoadArticle() {
this.show_loading = true
this.anims.loading.start()
this.article_html = await this.$app.$def.biliclient.getArticle(this.cvid)
this.article = this.$app.$def.articletools.parseArticlePageHtml(this.article_html)
console.log(this.article)
this.article_content = this.$app.$def.htmlparser.parseContentHtml(this.article.readInfo.content)
this.$app.$def.articletools.PatchArticleContent(this.article_content)
let article_html = ""
if(this.offline){
article_html = this.article_html
}
else{
article_html = await this.$app.$def.biliclient.getArticle(this.cvid)
}
const article = this.$app.$def.articletools.parseArticlePageHtml(article_html)
this.article.readInfo = article.readInfo // 原地更新 减少开辟内存空间
let article_full = this.$app.$def.htmlparser.parseContentHtml(article.readInfo.content)
this.$app.$def.articletools.PatchArticleContent(article_full)
this.article_content = this.SplitArticle(article_full, this.$app.$def.settings.SETTINGS.article_split_dom_count)
this.max_page_num = this.article_content.length
this.anims.loading.stop()
this.show_loading = false
global.runGC()
this.showarticle = true
},
onInit() {
+77
View File
@@ -0,0 +1,77 @@
<import name="switch-bar" src="../../components/SwitchBar/SwitchBar.ux"></import>
<import name="articleshow" src="../../components/Article/Article.ux"></import>
<template>
<div class="page">
<image style="position: absolute" src="/common/rounddevices_savedcontentbg.png" static></image>
<scroll class="scroll" scroll-y="true" bounces="true">
<switch-bar title="已缓存" static></switch-bar>
<div style="flex-direction: column" for="{{content}}">
<div if="{{$item.type == 'article' ? true : false}}" style="flex-direction: column; align-items: center; width: 100%" onclick="OpenArticle({{$item.id}})">
<articleshow article="{{$item}}"></articleshow>
</div>
</div>
</scroll>
</div>
</template>
<script>
import router from "@system.router"
export default {
private: {
content: []
},
PatchContent(origin_content){
try{
var new_content = []
// 暂无可进行的patch操作
new_content = origin_content
return new_content
}
catch(e){
console.error("[savedcontent.PatchContent] Patch Error: " + e.toString())
}
},
async OpenArticle(id){
var html = await this.$app.$def.savedcontent.SavedContentManager.getContent(id);
router.push({
uri: "pages/articleshow",
params: {
cvid: "",
offline: true,
article_html: html
}
})
},
async LoadContent(){
var origin_content = await this.$app.$def.savedcontent.SavedContentManager.listAllContent();
console.log(origin_content)
this.content = this.PatchContent(origin_content)
console.log(this.content)
},
onInit(){
this.LoadContent()
}
}
</script>
<style>
.page {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
position: absolute;
width: 100%;
}
.scroll {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
width: 100%;
}
</style>
+15 -10
View File
@@ -3,16 +3,16 @@
<template>
<div class="page">
<image style="position: absolute" src="/common/rounddevices_searchbg.png"></image>
<switch-bar title="搜索"></switch-bar>
<image style="position: absolute" src="/common/rounddevices_searchbg.png" static></image>
<switch-bar title="搜索" static></switch-bar>
<div class="searchbtn" style="margin-top: 20px" @click="OpenInput">
<image src="/common/searchpage_search.png" style="margin-left: 25px"></image>
<text class="searchtip" style="margin-left: 15px">点此搜索</text>
<image src="/common/searchpage_search.png" style="margin-left: 25px" static></image>
<text class="searchtip" style="margin-left: 15px" static>点此搜索</text>
</div>
<div class="hotwords" style="margin-top: 23px">
<div style="flex-direction: row; align-items: center">
<image src="/common/searchpage_fire.png" style="object-fit: scale-down"></image>
<text class="hottitle">热搜榜</text>
<div style="flex-direction: row; align-items: center" static>
<image src="/common/searchpage_fire.png" style="object-fit: scale-down" static></image>
<text class="hottitle" static>热搜榜</text>
</div>
<list style="margin-top: 13px" class="hotwordlist" if="{{showHotwordsList}}">
<list-item type="hotwordlist" style="
@@ -22,7 +22,7 @@
width: 100%;
height: 30px;
" for="{{hotwords}}">
<image src="{{$item.icon}}@22w_22h"></image>
<image src="{{$item.icon}}@22w_22h" static></image>
<text style="
font-size: 22px;
font-weight: 500;
@@ -46,7 +46,7 @@
import router from "@system.router"
export default {
private: {
hotwords: null,
hotwords: {},
showHotwordsList: false,
show_loading: true,
input_mode: false,
@@ -66,6 +66,7 @@ export default {
this.input_text.content = this.input_text.content.slice(0, -1)
},
GoSearch() {
this.hotwords = null
router.push({
uri: "pages/searchresult",
params: {
@@ -95,8 +96,12 @@ export default {
1000,
true
)
},
onShow(){
this.GetHotWords()
},
onDestroy(){
this.$app.$def.animengine.clearPageAnims("searchPage")
}
}
</script>
+46 -80
View File
@@ -1,6 +1,7 @@
<import name="switch-bar" src="../../components/SwitchBar/SwitchBar.ux"></import>
<import name="user" src="../../components/User/User.ux"></import>
<import name="videoshow" src="../../components/Video/Video.ux"></import>
<import name="articleshow" src="../../components/Article/Article.ux"></import>
<template>
<div class="page">
@@ -64,10 +65,8 @@
<div class="swiper_box">
<scroll if="{{typebar_if.c4}}" class="swiper_container" scroll-y="true"
@scroll="ScrollOverviewCheck">
<div class="articlebg"
for="{{search.articles}}" onclick="OpenArticle({{$item.id}})">
<text class="articletitle" style="margin-left: 18px">{{$item.title}}</text>
<text style="font-size: 20px; font-weight: 600; color: #9E9E9E; margin-top: 6px; margin-left: 18px">类型:{{$item.category_name}}</text>
<div style="flex-direction: column; align-items: center; width: 100%" for="{{search.articles}}" onclick="OpenArticle('{{$item.title}}', {{$item.id}})">
<articleshow article="{{$item}}"></articleshow>
</div>
<text style="margin-top: 30px"></text>
</scroll>
@@ -138,12 +137,13 @@ export default {
this.overview_mode = false
}
},
OpenArticle(id){
OpenArticle(title, id){
global.runGC()
router.push({
uri: "pages/articleshow",
uri: "pages/articlesave",
params: {
cvid: "cv" + id
cvid: "cv" + id,
cvtitie: title
}
})
},
@@ -155,60 +155,47 @@ export default {
params: {bvid}
})
},
// 搜索结果返回的信息烂到不能用,进行一个patch操作
async PatchSearchResult() {
try{
var videos = []
var comprehensive_videos = []
var articles = []
this.search.videos.forEach((video) => {
videos.push({
pic: video.pic,
title: video.title.replace(/<\/?[^>]+(>|$)/g, "").trim(),
owner: {
name: video.author
},
stat: {
view: video.play
},
bvid: video.bvid
})
})
console.log("[searchresult.PatchSearchResult] Patched Videos")
this.search.comprehensive_videos.forEach((video) => {
comprehensive_videos.push({
pic: video.pic,
title: video.title.replace(/<\/?[^>]+(>|$)/g, "").trim(),
owner: {
name: video.author
},
stat: {
view: video.play
},
bvid: video.bvid
})
})
console.log("[searchresult.PatchSearchResult] Patched Comprehensive Videos")
this.search.articles.forEach((article) => {
article.title = article.title.replace(/<\/?[^>]+(>|$)/g, "").trim()
articles.push(article)
});
console.log("[searchresult.PatchSearchResult] Patched Articles")
// 使用局部变量来减少内存占用
let videos = []
let comprehensive_videos = []
let articles = []
this.search.videos = videos
this.search.comprehensive_videos = comprehensive_videos
this.search.articles = articles
this.search.videos.forEach((video) => {
videos.push({
pic: video.pic,
title: video.title.replace(/<\/?[^>]+(>|$)/g, "").trim(),
owner: { name: video.author },
stat: { view: video.play },
bvid: video.bvid
})
})
console.log("[searchresult.PatchSearchResult] Patched Videos")
if (this.search.users.length < 1) {
this.comprehensive_status.has_users_result = false
}
if (this.search.videos.length < 1) {
this.comprehensive_status.has_video_result = false
}
}
catch(e){
console.error("[searchresult.PatchSearchResult] Patch Error: " + e.toString())
}
this.search.comprehensive_videos.forEach((video) => {
comprehensive_videos.push({
pic: video.pic,
title: video.title.replace(/<\/?[^>]+(>|$)/g, "").trim(),
owner: { name: video.author },
stat: { view: video.play },
bvid: video.bvid
})
})
console.log("[searchresult.PatchSearchResult] Patched Comprehensive Videos")
this.search.articles.forEach((article) => {
article.title = article.title.replace(/<\/?[^>]+(>|$)/g, "").trim()
articles.push(article)
})
console.log("[searchresult.PatchSearchResult] Patched Articles")
// 原地更新避免重新开辟内存
this.search.videos.splice(0, this.search.videos.length, ...videos)
this.search.comprehensive_videos.splice(0, this.search.comprehensive_videos.length, ...comprehensive_videos)
this.search.articles.splice(0, this.search.articles.length, ...articles)
this.comprehensive_status.has_users_result = this.search.users.length > 0
this.comprehensive_status.has_video_result = this.search.videos.length > 0
},
async DoSearch() {
this.show_loading = true
@@ -225,12 +212,11 @@ export default {
await this.PatchSearchResult()
console.log(this.search.articles)
this.show_loading = false
this.anims.loading.stop()
},
onInit() {
global.runGC()
this.UpdateTypeBarColors({index: 0})
this.anims.loading = new this.$app.$def.animengine.SequenceAnim(
@@ -313,24 +299,4 @@ export default {
width: 100%;
height: 100%;
}
.articlebg {
width: 72%;
min-height: 120px;
max-height: 120px;
justify-content: center;
margin-top: 5px;
background-color: #262626af;
flex-direction: column;
border-radius: 24px;
}
.articletitle {
font-size: 24px;
font-weight: 600;
margin-top: 13px;
width: 89%;
max-height: 70px;
text-overflow: ellipsis;
}
</style>
+2 -17
View File
@@ -1,5 +1,6 @@
<import name="title-bar" src="../../components/TitleBar/TitleBar.ux"></import>
<import name="full-screen-input" src="../../components/FullScreenInput/FullScreenInput.ux"></import>
<import name="default-button" src="../../components/DefaultButton/DefaultButton.ux"></import>
<template>
<div class="page">
@@ -50,9 +51,7 @@
<text class="vidtooltext">手机上看</text>
</div>
</scroll>
<div class="featurebtn" style="margin-top: 20px" @click="Reply">
<text class="featurebtn_text">发送评论</text>
</div>
<default-button text="发送评论" margin-top="20px" @click="Reply"></default-button>
<image src="/common/featurebtn_replyarea.png" style="margin-top: 15px" @click="GoReplyArea()"></image>
</scroll>
<full-screen-input style="position: absolute" input_text="{{input_text}}" if="{{replymode}}" @complete="InputComplete"
@@ -279,18 +278,4 @@ export default {
.vidtoolimg {
width: 62px;
}
.featurebtn {
width: 322px;
height: 81px;
border-radius: 90px;
background-color: #262626;
justify-content: center;
align-items: center;
}
.featurebtn_text {
font-size: 40px;
font-weight: 600;
}
</style>
+170
View File
@@ -0,0 +1,170 @@
import { file } from "./tsimports";
// 定义文件存储的基本结构
interface StoredContent {
id: string;
title: string;
type: string;
fileUri: string;
}
// 模拟存储的内容列表
let storageIndex: StoredContent[] = [];
// 定义文件存储的基础路径
const baseUri = 'internal://files/bilisavedcontent/';
const indexFileUri = `${baseUri}index.json`; // 存储 storageIndex 的文件
// 封装 file 接口的 Promise 方法
function fileWrite(uri: string, data: string): Promise<void> {
return new Promise((resolve, reject) => {
file.writeText({
uri,
text: data,
success: () => resolve(),
fail: (data, code) => reject(`Failed to write to ${uri}: ${code}`)
});
});
}
function fileRead(uri: string): Promise<string> {
return new Promise((resolve, reject) => {
file.readText({
uri,
success: (data) => resolve(data.text),
fail: (data, code) => reject(`Failed to read from ${uri}: ${code}`)
});
});
}
function fileList(dirUri: string): Promise<any[]> {
return new Promise((resolve, reject) => {
file.list({
uri: dirUri,
success: (data) => resolve(data.fileList),
fail: (data, code) => reject(`Failed to list files in ${dirUri}: ${code}`)
});
});
}
function fileAccess(uri: string): Promise<boolean> {
return new Promise((resolve) => {
file.access({
uri,
success: () => resolve(true),
fail: () => resolve(false)
});
});
}
function fileDelete(uri: string): Promise<void> {
return new Promise((resolve, reject) => {
file.delete({
uri,
success: () => resolve(),
fail: (data, code) => reject(`Failed to delete ${uri}: ${code}`)
});
});
}
// 同步 storageIndex 到本地文件
async function saveStorageIndex(): Promise<void> {
try {
console.log(storageIndex)
const indexData = JSON.stringify(storageIndex);
await fileWrite(indexFileUri, indexData);
} catch (e) {
console.error(`[SavedContentManager] saveStorageIndex Error: ${e.toString()}`);
}
}
// 从本地文件加载 storageIndex
async function loadStorageIndex(): Promise<void> {
try {
const fileExists = await fileAccess(indexFileUri);
if (fileExists) {
const indexData = await fileRead(indexFileUri);
storageIndex = JSON.parse(indexData);
} else {
storageIndex = [];
}
} catch (e) {
console.error(`[SavedContentManager] loadStorageIndex Error: ${e.toString()}`);
storageIndex = [];
}
}
function generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
export class SavedContentManager {
// 初始化时加载 storageIndex
static async initialize(): Promise<void> {
await loadStorageIndex()
console.log("loaded SavedContent StorageIndex", storageIndex)
}
// 存储内容
static async storeContent(title: string, data: string, type: string): Promise<string | void> {
try {
const id = generateUUID();
const fileUri = `${baseUri}${id}.txt`;
// 写入文件
await fileWrite(fileUri, data);
// 更新 storageIndex 并保存
storageIndex.push({ id, title, type, fileUri });
await saveStorageIndex();
return id;
} catch (e) {
console.error(`[SavedContentManager] storeContent Error: ${e.toString()}`);
}
}
// 根据 id 或 title 读取内容
static async getContent(identifier: string): Promise<string | null> {
try {
const content = storageIndex.find(item => item.id === identifier || item.title === identifier);
if (!content) return null;
// 读取文件内容
const fileExists = await fileAccess(content.fileUri);
if (!fileExists) throw new Error(`File does not exist: ${content.fileUri}`);
return await fileRead(content.fileUri);
} catch (e) {
console.error(`[SavedContentManager] getContent Error: ${e.toString()}`);
return null;
}
}
// 读取所有存储内容的 id 和 title 列表
static async listAllContent(): Promise<any> {
return storageIndex;
}
// 删除内容
static async deleteContent(identifier: string): Promise<void> {
try {
const contentIndex = storageIndex.findIndex(item => item.id === identifier || item.title === identifier);
if (contentIndex === -1) throw new Error('Content not found');
// 删除文件
const fileUri = storageIndex[contentIndex].fileUri;
await fileDelete(fileUri);
// 从 storageIndex 中删除记录并保存
storageIndex.splice(contentIndex, 1);
await saveStorageIndex();
} catch (e) {
console.error(`[SavedContentManager] deleteContent Error: ${e.toString()}`);
}
}
}
+2
View File
@@ -3,6 +3,7 @@ import { storage } from "./tsimports"
interface SettingsInterface {
fresh_type: number;
home_vid_count: number;
article_split_dom_count: number;
enableFullAnimation: boolean;
enableInterconnectMode: boolean;
}
@@ -10,6 +11,7 @@ interface SettingsInterface {
export let SETTINGS: SettingsInterface = {
fresh_type: 3, //视频推荐相关度,范围1-3,根据大数据推送
home_vid_count: 10,
article_split_dom_count: 9999,
enableFullAnimation: false,
// 是否启用interconnect联网模式,需要安卓APP能力支持
// 低人一等环9专用
+2 -1
View File
@@ -7,5 +7,6 @@ import router from "@system.router"
import interconnect from "@system.interconnect"
import prompt from "@system.prompt"
import network from "@system.network"
import file from "@system.file"
export { fetch, storage, crypto, device, folme, router, interconnect, prompt, network };
export { fetch, storage, crypto, device, folme, router, interconnect, prompt, network, file };