Add search box for favorites

This commit is contained in:
the1812 2020-04-02 17:02:04 +08:00
parent 2d32b79200
commit e4ba26acd5
18 changed files with 113 additions and 54 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,9 @@ namespace BilibiliEvolved.Build
var filename = "min/dark.min.css";
File.WriteAllText(filename, fullStyle);
var otherStyle = File.ReadAllText("min/dark-important.min.css") + File.ReadAllText("min/dark-navbar.min.css") + File.ReadAllText("min/scrollbar.min.css");
if (string.IsNullOrWhiteSpace(offlineVersion)) {
generateVersion();
}
File.WriteAllText("min/dark.user.css", $@"
/* ==UserStyle==
@name Bilibili Evolved -

View File

@ -10,8 +10,8 @@ namespace BilibiliEvolved.Build
{
partial class ProjectBuilder
{
private string offlineText;
private string offlineVersion;
private string offlineText = "";
private string offlineVersion = "";
private string readAsBase64Image(string path)
{

View File

@ -32,15 +32,15 @@
"compact-layout.min.js": "B20609A7CBBDB1845FA0156FB5BE6B1E1A1B8B069EA85F65D16241DD2C12D738",
"custom-control-background.min.css": "1981FD2BF3B17ECF33F98D5DEDAF0D32ACBE9532A51FDB70822286991AB98EF3",
"custom-control-background.min.js": "2A439B045A51D24F5115DD25995ED37E3D0371A074199FE6C727F2235F39C4D9",
"custom-navbar.min.css": "6EADE807DC6AC0457599076FF67F126FBC9214F33EB0F78A66EB892545D33BA8",
"custom-navbar.min.css": "A8AE67592B51E2614ADE769598CAFA316FD24891DA14445D1F86518A24E15FAE",
"custom-navbar.min.html": "FA6ACF1CA1DB6D3BD2BE310646B89B028756A36BF434BEBABE2BF00789581A09",
"custom-navbar.min.js": "1D1BBBD9482EAFAF1F12DBAD7FC3E34FFE36CA55AC42839ACBB34FA4142C3646",
"custom-navbar-activities.min.js": "7AC74BBC42C4AEB3DF50A169AF46F03998BF5E74722D78BFEF885F00B1AB003A",
"custom-navbar-blank.min.js": "DC2B75D5760EECD3A257785FDFBB7E5DEBF61CD9083C68DD5BCE468B093F54F3",
"custom-navbar-category.min.js": "A8A6190C090C92525EBA0A3C0C70EFC37AC7D1AFEEAB2834F1D69934C1B1F4D1",
"custom-navbar-component.min.js": "5CA104E636521F21BBD05D23E17BDDF945566DDA3042710D4D7E1DDD1677E30B",
"custom-navbar-favorites-list.min.js": "71931EEBCF0BA3B33D8E72FF56BD92F2A089907F7E54E863D1EDDE0EAA9242FE",
"custom-navbar-history-list.min.js": "08B3AE7CF9351E801BD644A92E6719C0A370336C3CC152DD2D6EBE7B6B55464C",
"custom-navbar-favorites-list.min.js": "9FB46E455E81679FA082B67D28C79D07DC88137A99C068FC695C23044DF578E0",
"custom-navbar-history-list.min.js": "ACB1F932E262DE643EAC53F17460DB6D710765FAE563C6FE76870C08C320DC05",
"custom-navbar-iframe.min.js": "DBE5844C2AA36057DBD33BBC5724D7D7F881AB15EC45A80228961942CD810A32",
"custom-navbar-logo.min.js": "236CCC887A3917A6D4840889B7380B648197765B8CDA27CB233F3F1E3215F7BB",
"custom-navbar-messages.min.js": "D7B315A4D8FD9E2EF9C1E40C1F3CA7EC18C5A87AC7A9410049061A0D851868DE",
@ -51,7 +51,7 @@
"custom-navbar-upload.min.js": "0CDEEA46DCF0D6187C86BB12DE8CD36737116EF171AA1F63AB20C5DC7549C976",
"custom-navbar-user-info.min.js": "86C30E3AD71C99630933B231861A271A647D9D6625CB46AB61D65F5F9560F188",
"custom-navbar-video-list.min.js": "08D6713144E910C1303CB610A5890A5663B92BE82630BDA3C8E6299FB4A429E2",
"custom-navbar-watchlater-list.min.js": "5503A751DD2FDC19A6F138201B8C1654916F2D9B467E6D8B845635A920BBC595",
"custom-navbar-watchlater-list.min.js": "57EA2C62F7442750BB177B9898CF07BA61C45F0FD99737516ED609051699B054",
"danmaku-converter.min.js": "908389A7D4E5F8546075B74F41BF03BD974073B4203723A575CF22082B358692",
"dark.min.css": "86D2B120F4C46847F420F940673F184571EFDCA7079ECD9A1D791637E9F8A687",
"dark.user.css": "B87F6CD0331497D8FB88199C9CCFD68358036F1A7B59CA1223A9DF4A10342D2A",

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(()=>{return(t,i)=>{const{NavbarComponent:a}=i.import("custom-navbar-component");class e extends a{constructor(){super();this.boundingWidth=380;this.noPadding=true;this.href="https://www.bilibili.com/watchlater/#/list";this.html="稍后再看";this.active=document.URL.startsWith("https://www.bilibili.com/watchlater/");this.popupHtml=`\n<div class="watchlater-list loading">\n<div class="loading-tip">\n 加载中...\n</div>\n<div class="empty-tip" v-if="filteredCards.length === 0">\n 空空如也哦 = ̄ω ̄=\n</div>\n<div class="header">\n \x3c!--<div class="operations">\n<div v-if="!redirect" class="round-button" title="播放全部"><i class="mdi mdi-play-circle-outline"></i></div>\n<div class="round-button" title="移除已观看"><i class="mdi mdi-eye-check-outline"></i></div>\n<div class="round-button" title="清空全部"><i class="mdi mdi-trash-can-outline"></i></div>\n</div>--\x3e\n<div class="search">\n<input type="text" placeholder="搜索" v-model="search">\n</div>\n<a class="more-info" href="https://www.bilibili.com/watchlater/#/list" title="查看更多" target="_blank">\n 查看更多\n<i class="mdi mdi-dots-horizontal"></i>\n</a>\n</div>\n<transition-group name="cards" tag="div" class="cards">\n<div class="watchlater-card" v-for="(card, index) of filteredCards" :key="card.aid">\n<a class="cover-container" target="_blank" :href="card.href">\n<dpi-img class="cover" :src="card.coverUrl" :size="{width: 130, height: 85}"></dpi-img>\n<div class="floating remove" title="移除" @click.prevent="remove(card.aid, index)"><i class="mdi mdi-close"></i></div>\n<div class="floating duration">{{card.durationText}}</div>\n<div class="floating viewed" v-if="card.complete">\n 已观看\n</div>\n</a>\n<a class="title" target="_blank" :href="card.href" :title="card.title">{{card.title}}</a>\n<a class="up" target="_blank" :href="'https://space.bilibili.com/' + card.upID" :title="card.upName">\n<dpi-img class="face" :src="card.upFaceUrl" :size="20"></dpi-img>\n<div class="name">{{card.upName}}</div>\n</a>\n</div>\n</transition-group>\n \x3c!--<div class="undo round-button">\n<i class="mdi mdi-undo-variant"></i>\n 撤销\n</div>--\x3e\n</div>\n`;this.initialPopup=(()=>{this.init()})}async init(){new Vue({el:await SpinQuery.select(`.custom-navbar [data-name="${this.name}"] .watchlater-list`),store:store,components:{DpiImg:()=>i.importAsync("dpi-img.vue")},data:{cards:[],filteredCards:[],search:"",lastRemovedAid:0},computed:{...Vuex.mapState(["watchlaterList"])},watch:{watchlaterList(){this.updateList()},search(){this.updateFilteredCards()}},methods:{...Vuex.mapActions(["toggleWatchlater"]),async updateList(){const{getWatchlaterList:a}=await i.importAsync("watchlater-api");const e=await a(true);if(!e){this.cards=[];return}const s=i=>{if(t.watchLaterRedirect){return`https://www.bilibili.com/video/av${i.aid}`}if(i.bvid){return`https://www.bilibili.com/watchlater/#/${i.bvid}`}return`https://www.bilibili.com/watchlater/#/av${i.aid}`};const r=e.map(i=>{const a=(()=>{if(i.pages===undefined){return s(i)}const a=i.pages.map(t=>t.cid);const e=i.cid===0?1:a.indexOf(i.cid)+1;return t.watchLaterRedirect?`${s(i)}?p=${e}`:`${s(i)}/p${e}`})();const e=Math.round(1e3*i.progress/i.duration)/1e3;return{aid:i.aid,href:a,coverUrl:i.pic.replace("http:","https:"),durationText:formatDuration(i.duration),duration:i.duration,complete:i.progress<0||e>.95,title:i.title,upName:i.owner.name,upFaceUrl:i.owner.face.replace("http:","https:"),upID:i.owner.mid}});this.cards=r;if(this.search){this.updateFilteredCards()}else{this.filteredCards=r}},async remove(t,i){this.cards.splice(i,1);await this.toggleWatchlater(t);this.lastRemovedAid=t},async undo(){const t=this.lastRemovedAid;if(t!==0){await this.toggleWatchlater(t)}},updateFilteredCards:_.debounce(function(){const t=this.search.toLowerCase();const i=this.$el.querySelector(".cards");i.scrollTo(0,0);this.filteredCards=this.cards.filter(i=>{return i.title.toLowerCase().includes(t)||i.upName.toLowerCase().includes(t)})},200)},async mounted(){try{await this.updateList()}finally{this.$el.classList.remove("loading")}}})}get name(){return"watchlaterList"}}return{export:{WatchlaterList:e}}}})();
(()=>{return(t,i)=>{const{NavbarComponent:a}=i.import("custom-navbar-component");class e extends a{constructor(){super();this.boundingWidth=380;this.noPadding=true;this.href="https://www.bilibili.com/watchlater/#/list";this.html="稍后再看";this.active=document.URL.startsWith("https://www.bilibili.com/watchlater/");this.popupHtml=`\n<div class="watchlater-list loading">\n<div class="loading-tip">\n 加载中...\n</div>\n<div class="empty-tip" v-if="filteredCards.length === 0">\n 空空如也哦 = ̄ω ̄=\n</div>\n<div class="header">\n \x3c!--<div class="operations">\n<div v-if="!redirect" class="round-button" title="播放全部"><i class="mdi mdi-play-circle-outline"></i></div>\n<div class="round-button" title="移除已观看"><i class="mdi mdi-eye-check-outline"></i></div>\n<div class="round-button" title="清空全部"><i class="mdi mdi-trash-can-outline"></i></div>\n</div>--\x3e\n<div class="search">\n<input type="text" placeholder="搜索" v-model="search">\n</div>\n<a class="more-info" href="https://www.bilibili.com/watchlater/#/list" title="查看更多" target="_blank">\n 查看更多\n<i class="mdi mdi-dots-horizontal"></i>\n</a>\n</div>\n<transition-group name="cards" tag="div" class="cards">\n<div class="watchlater-card" v-for="(card, index) of filteredCards" :key="card.aid">\n<a class="cover-container" target="_blank" :href="card.href">\n<dpi-img class="cover" :src="card.coverUrl" :size="{width: 130, height: 85}"></dpi-img>\n<div class="floating remove" title="移除" @click.prevent="remove(card.aid, index)"><i class="mdi mdi-close"></i></div>\n<div class="floating duration">{{card.durationText}}</div>\n<div class="floating viewed" v-if="card.complete">\n 已观看\n</div>\n</a>\n<a class="title" target="_blank" :href="card.href" :title="card.title">{{card.title}}</a>\n<a class="up" target="_blank" :href="'https://space.bilibili.com/' + card.upID" :title="card.upName">\n<dpi-img class="face" :src="card.upFaceUrl" :size="20"></dpi-img>\n<div class="name">{{card.upName}}</div>\n</a>\n</div>\n</transition-group>\n \x3c!--<div class="undo round-button">\n<i class="mdi mdi-undo-variant"></i>\n 撤销\n</div>--\x3e\n</div>\n`;this.initialPopup=(()=>{this.init()})}async init(){new Vue({el:await SpinQuery.select(`.custom-navbar [data-name="${this.name}"] .watchlater-list`),store:store,components:{DpiImg:()=>i.importAsync("dpi-img.vue")},data:{cards:[],filteredCards:[],search:"",lastRemovedAid:0},computed:{...Vuex.mapState(["watchlaterList"])},watch:{watchlaterList(){this.updateList()},search(){this.updateFilteredCards()}},methods:{...Vuex.mapActions(["toggleWatchlater"]),async updateList(){const{getWatchlaterList:a}=await i.importAsync("watchlater-api");const e=await a(true);if(!e){this.cards=[];return}const s=i=>{if(t.watchLaterRedirect){return`https://www.bilibili.com/video/av${i.aid}`}if(i.bvid){return`https://www.bilibili.com/watchlater/#/${i.bvid}`}return`https://www.bilibili.com/watchlater/#/av${i.aid}`};const r=e.map(i=>{const a=(()=>{if(i.pages===undefined){return s(i)}const a=i.pages.map(t=>t.cid);const e=i.cid===0?1:a.indexOf(i.cid)+1;return t.watchLaterRedirect?`${s(i)}?p=${e}`:`${s(i)}/p${e}`})();const e=Math.round(1e3*i.progress/i.duration)/1e3;return{aid:i.aid,href:a,coverUrl:i.pic.replace("http:","https:"),durationText:formatDuration(i.duration),duration:i.duration,complete:i.progress<0||e>.95,title:i.title,upName:i.owner.name,upFaceUrl:i.owner.face.replace("http:","https:"),upID:i.owner.mid}});this.cards=r;if(this.search){this.updateFilteredCards()}else{this.filteredCards=r}},async remove(t,i){this.cards.splice(i,1);await this.toggleWatchlater(t);this.lastRemovedAid=t},async undo(){const t=this.lastRemovedAid;if(t!==0){await this.toggleWatchlater(t)}},updateFilteredCards:_.debounce(function(){const t=this.search.toLowerCase();const i=this.$el.querySelector(".cards");i.scrollTo(0,0);this.filteredCards=this.cards.filter(i=>{return i.title.toLowerCase().includes(t)||i.upName.toLowerCase().includes(t)})},100)},async mounted(){try{await this.updateList()}finally{this.$el.classList.remove("loading")}}})}get name(){return"watchlaterList"}}return{export:{WatchlaterList:e}}}})();

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,10 @@
color: inherit;
background-color: transparent;
border-radius: 4px;
&::-webkit-input-placeholder {
color: inherit !important;
opacity: .3;
}
}
&::after {
content: "";

View File

@ -1,4 +1,5 @@
@import "../../common";
@import "../search-box";
.custom-navbar .favorites-list {
width: 380px;
height: 600px;
@ -46,14 +47,17 @@
}
.header {
@include h-center();
@include search-box();
justify-content: space-between;
margin: 16px 12px;
.list-select {
flex-shrink: 0;
height: 26px;
}
.more-info {
@include h-center();
@include round-bar(26);
flex-shrink: 0;
padding: 4px 6px 4px 10px;
background-color: #8882;
&:hover {
@ -64,7 +68,8 @@
}
}
.play-all-container {
flex-grow: 1;
// flex-grow: 1;
flex-shrink: 0;
padding: 0 6px;
@include h-center();
justify-content: flex-start;
@ -184,6 +189,10 @@
margin-right: 6px;
height: 20px;
width: 20px;
object-fit: cover;
body.dark &.placeholder {
filter: invert(.9);
}
}
.name {
overflow: hidden;

View File

@ -9,6 +9,24 @@ interface FavoritesItemInfo extends VideoCardInfo {
favoriteTimestamp: number
favoriteTime: Date
}
const favoriteItemMapper = (item: any) => {
return {
id: item.id,
aid: item.id,
coverUrl: item.cover.replace('http:', 'https:'),
favoriteTimestamp: item.fav_time * 1000,
favoriteTime: new Date(item.fav_time * 1000),
title: item.title,
description: item.intro,
duration: item.duration,
durationText: formatDuration(item.duration),
playCount: item.cnt_info.play,
danmakuCount: item.cnt_info.danmaku,
upName: item.upper.name,
upFaceUrl: item.upper.face.replace('http:', 'https:'),
upID: item.upper.mid,
} as FavoritesItemInfo
}
export class FavoritesList extends NavbarComponent {
constructor() {
super()
@ -31,16 +49,19 @@ export class FavoritesList extends NavbarComponent {
<i class="mdi mdi-play"></i>
</a>
</div>
<div class="search">
<input type="text" placeholder="搜索" v-model="search">
</div>
<a class="more-info" :href="moreLink" title="查看更多" target="_blank">
<i class="mdi mdi-dots-horizontal"></i>
</a>
</div>
<transition-group name="cards" tag="div" class="cards">
<div class="empty-tip" v-if="!cardsLoading && cards.length === 0" key="empty-tip">
<div class="empty-tip" v-if="!cardsLoading && filteredCards.length === 0" key="empty-tip">
=ω=
</div>
<div class="favorite-card" v-for="card of cards" :key="card.id">
<div class="favorite-card" v-for="card of filteredCards" :key="card.id">
<a class="cover-container" target="_blank" :href="'https://www.bilibili.com/video/av' + card.aid">
<dpi-img class="cover" :src="card.coverUrl" :size="{width: 130, height: 85}"></dpi-img>
<div class="floating duration">{{card.durationText}}</div>
@ -48,7 +69,7 @@ export class FavoritesList extends NavbarComponent {
</a>
<a class="title" target="_blank" :href="'https://www.bilibili.com/video/av' + card.aid" :title="card.title">{{card.title}}</a>
<a class="up" target="_blank" :href="'https://space.bilibili.com/' + card.upID" :title="card.upName">
<dpi-img class="face" :src="card.upFaceUrl" :size="20"></dpi-img>
<dpi-img placeholder-image class="face" :src="card.upFaceUrl" :size="20"></dpi-img>
<div class="name">{{card.upName}}</div>
</a>
</div>
@ -80,9 +101,24 @@ export class FavoritesList extends NavbarComponent {
data: {
list: [],
cards: [],
filteredCards: [],
selectedListName: '',
cardsLoading: true,
cardsPage: 1,
search: '',
},
watch: {
search(keyword: string) {
if (keyword === '') {
this.filteredCards = this.cards
return
}
keyword = keyword.toLowerCase()
this.filteredCards = (this.cards as FavoritesItemInfo[]).filter(it => {
return it.title.toLowerCase().includes(keyword) || it.upName.toLowerCase().includes(keyword)
})
this.searchAllList()
},
},
computed: {
listNames() {
@ -119,31 +155,16 @@ export class FavoritesList extends NavbarComponent {
}
return json.data.medias.filter((item: any) => {
return item.attr !== 9 // 过滤掉已失效视频
}).map((item: any) => {
return {
id: item.id,
aid: item.id,
coverUrl: item.cover.replace('http:', 'https:'),
favoriteTimestamp: item.fav_time * 1000,
favoriteTime: new Date(item.fav_time * 1000),
title: item.title,
description: item.intro,
duration: item.duration,
durationText: formatDuration(item.duration),
playCount: item.cnt_info.play,
danmakuCount: item.cnt_info.danmaku,
upName: item.upper.name,
upFaceUrl: item.upper.face.replace('http:', 'https:'),
upID: item.upper.mid,
} as FavoritesItemInfo
})
}).map(favoriteItemMapper)
},
async changeList() {
try {
this.search = ''
this.cards = []
this.cardsPage = 1
this.cardsLoading = true
this.cards = await this.getCards()
this.filteredCards = this.cards
this.setInfiniteScroll()
} catch (error) {
logError(error)
@ -169,13 +190,32 @@ export class FavoritesList extends NavbarComponent {
setInfiniteScroll() {
const cardsContainer = this.$el.querySelector('.cards') as HTMLElement
const scrollHandler = _.debounce(() => {
if (cardsContainer.scrollTop + cardsContainer.clientHeight >= cardsContainer.scrollHeight - 48) {
if (this.search === '' && cardsContainer.scrollTop + cardsContainer.clientHeight >= cardsContainer.scrollHeight - 48) {
cardsContainer.removeEventListener('scroll', scrollHandler)
this.loadNextPage()
}
}, 200)
cardsContainer.addEventListener('scroll', scrollHandler)
},
/** 搜索当前收藏夹所有的视频 */
searchAllList: _.debounce(async function () {
if (this.search === '') {
return
}
try {
const json = await Ajax.getJsonWithCredentials(`https://api.bilibili.com/x/v3/fav/resource/list?media_id=${this.selectedListId}&pn=1&ps=20&keyword=${this.search}&order=mtime&type=0&tid=0`)
if (json.code !== 0) {
return
}
const items = _.get(json, 'data.medias', []) || []
const results = _.uniqBy(this.filteredCards.concat(items.map(favoriteItemMapper)), (card: FavoritesItemInfo) => card.id)
console.log(_.cloneDeep(results))
this.filteredCards = results
} catch (error) {
console.error(error)
return
}
}, 200),
},
async mounted() {
try {

View File

@ -61,7 +61,7 @@
margin: 16px 12px;
.tabs {
flex: 0;
margin-right: 16px;
margin: 0 16px 0 0;
padding: 0 6px;
@include h-center();
justify-content: flex-start;
@ -132,6 +132,9 @@
@include no-scrollbar();
padding: 0 12px;
padding-bottom: 12px;
.empty-tip {
text-align: center;
}
.time-group {
padding-bottom: 16px;
@include items-animation();

View File

@ -197,7 +197,7 @@ export class HistoryList extends NavbarComponent {
</button>
</div>
<transition-group name="history-content" tag="div" class="history-content">
<div class="empty-tip" v-if="!timelineLoading && timeline.length === 0" key="empty-tip">
<div class="empty-tip" v-if="!timelineLoading && filteredTimeline.length === 0" key="empty-tip">
=ω=
</div>
<div class="loading-tip" v-if="timelineLoading" key="loading-tip">

View File

@ -172,7 +172,7 @@ export class WatchlaterList extends NavbarComponent {
this.filteredCards = (this.cards as WatchlaterCard[]).filter(card => {
return card.title.toLowerCase().includes(search) || card.upName.toLowerCase().includes(search)
})
}, 200),
}, 100),
},
async mounted() {
try {