Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions src/components/friends/list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<script setup lang="ts">
import UserItem from './item.vue'
import { NGrid, NGi } from 'naive-ui'
import { ref } from 'vue'
import { ref, watch } from 'vue'
import type { RelationList } from '@services/../pl-serve-type-main/type/main'
import { getData } from '@services/api/getData.ts'
import { showAPiError } from '@popup/index.ts'
Expand All @@ -24,10 +24,11 @@ import { showMessage } from '@popup/naiveui'

// cols需要在父组件传参,这可能会在好友界面和Profile界面(未实现)展现
// Props `cols` needs to be passed from the parent component, which may be displayed in the Friends page and Profile page (not implemented yet).
const { userid, type } = defineProps<{
const { userid, type, query } = defineProps<{
userid?: string
type?: string
cols?: number
query?: string
}>()

let loading = ref(false)
Expand All @@ -53,7 +54,7 @@ async function handleLoad() {
DisplayType: type ? Number(type) : 0,
Skip: skip.value,
Take: 24,
Query: '',
Query: query || '',
})
if (getRelationsRes.Status !== 200) {
showAPiError(t('errors.apiErrorTitle'), t('errors.apiErrorMessage'), handleLoad)
Expand All @@ -62,7 +63,7 @@ async function handleLoad() {
DisplayType: type,
Skip: skip.value,
Take: 24,
Query: '',
Query: query || '',
})
const _res = removeToken(getRelationsRes)
window.$ErrorLogger.captureApiError(
Expand Down Expand Up @@ -92,6 +93,17 @@ window.$Logger.logPageView({
})

handleLoad()

watch(
() => query,
() => {
items.value = []
skip.value = 0
noMore.value = false
hasInformed.value = false
void handleLoad()
Comment on lines +100 to +104
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Queue a reload when query changes mid-request

If the user updates the search text while a previous /Users/GetRelations request is still in flight, this watcher clears items and immediately calls handleLoad(), but handleLoad() exits early because loading.value is still true. When the old request finally resolves, it appends results for the stale query, and no request for the new query is guaranteed to run, so the list can show incorrect search results until another scroll/load event happens.

Useful? React with 👍 / 👎.

Comment on lines +100 to +104
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Queue reload when query changes mid-request

When the search query changes while a /Users/GetRelations request is still in flight, this watcher clears items and immediately calls handleLoad(), but handleLoad() exits early because loading.value is still true. That means the new query is never fetched at that moment, and when the old request returns it can repopulate the list with stale results from the previous query. This is reproducible by typing quickly during loading and leaves the visible list inconsistent until another load is triggered.

Useful? React with 👍 / 👎.

},
)
</script>

<style scoped></style>
1 change: 1 addition & 0 deletions src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default {
editors: 'Redakteure und Administratoren',
retired: 'Emeritierte Redakteure',
baned: 'Gesperrt',
searchPlaceholder: 'Freunde nach Spitznamen suchen',
},
messagesI18n: {
errorOnDelete: 'Fehler beim Löschen der Nachricht, bitte versuchen Sie es später',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default {
editors: 'Editors and Admins',
retired: 'Retired Editors',
baned: 'Banned',
searchPlaceholder: 'Search friends by nickname',
},
messagesI18n: {
errorOnDelete: 'Failed to delete message, please try again later',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default {
editors: 'Éditeurs et administrateurs',
retired: 'Éditeurs retraités',
baned: 'Bloqué',
searchPlaceholder: 'Rechercher des amis par pseudo',
},
messagesI18n: {
errorOnDelete: 'Échec de la suppression du message, veuillez réessayer plus tard',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default {
editors: '編集者と管理者',
retired: '退職した編集者',
baned: 'ブロック済み',
searchPlaceholder: 'ニックネームで友達を検索',
},
messagesI18n: {
errorOnDelete: 'メッセージの削除に失敗しました。後でもう一度お試しください',
Expand Down
1 change: 1 addition & 0 deletions src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default {
editors: '编辑和管理员',
retired: '荣休编辑',
baned: '小黑屋',
searchPlaceholder: '搜索好友昵称',
},
messagesI18n: {
errorOnDelete: '删除消息失败,请稍后再试',
Expand Down
26 changes: 19 additions & 7 deletions src/views/Friends.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,42 @@
<h1>{{ t('friends.title') }}</h1>
</Header>
<div class="list">
<div class="friends__search">
<n-input
v-model:value="searchText"
clearable
:placeholder="t('friends.searchPlaceholder')"
/>
</div>
<n-tabs default-value="following" justify-content="space-evenly" type="line">
<n-tab-pane name="following" :tab="t('friends.following')" display-directive="show:lazy">
<div class="item">
<UserList :userid="userID" type="1" :cols="friendItemsPerRow" />
<UserList :userid="userID" type="1" :cols="friendItemsPerRow" :query="searchText.trim()" />
</div>
</n-tab-pane>
<n-tab-pane name="follower" :tab="t('friends.follower')" display-directive="show:lazy">
<div class="item">
<UserList :userid="userID" type="0" :cols="friendItemsPerRow" />
<UserList :userid="userID" type="0" :cols="friendItemsPerRow" :query="searchText.trim()" />
</div>
</n-tab-pane>
<n-tab-pane name="volunteers" :tab="t('friends.volunteers')" display-directive="show:lazy">
<div class="item">
<UserList :userid="userID" type="3" :cols="friendItemsPerRow" />
<UserList :userid="userID" type="3" :cols="friendItemsPerRow" :query="searchText.trim()" />
</div>
</n-tab-pane>
<n-tab-pane name="editors" :tab="t('friends.editors')" display-directive="show:lazy">
<div class="item">
<UserList :userid="userID" type="4" :cols="friendItemsPerRow" />
<UserList :userid="userID" type="4" :cols="friendItemsPerRow" :query="searchText.trim()" />
</div>
</n-tab-pane>
<n-tab-pane name="en" :tab="t('friends.retired')" display-directive="show:lazy">
<div class="item">
<UserList :userid="userID" type="5" :cols="friendItemsPerRow" />
<UserList :userid="userID" type="5" :cols="friendItemsPerRow" :query="searchText.trim()" />
</div>
</n-tab-pane>
<n-tab-pane name="baned" :tab="t('friends.baned')" display-directive="show:lazy">
<div class="item">
<UserList :userid="userID" type="2" :cols="friendItemsPerRow" />
<UserList :userid="userID" type="2" :cols="friendItemsPerRow" :query="searchText.trim()" />
</div>
</n-tab-pane>
</n-tabs>
Expand All @@ -47,12 +54,14 @@ import Footer from '../components/utils/Footer.vue'
import { NTabs, NTabPane } from 'naive-ui'
import { useResponsive } from '../layout/useResponsive'
import storageManager from '@services/storage'
import { onActivated } from 'vue'
import { onActivated, ref } from 'vue'
import { checkLogin } from '@services/utils.ts'
import { NInput } from 'naive-ui'

const userID = storageManager.getObj('userInfo').value?.ID
const { friendItemsPerRow } = useResponsive()
const { t } = useI18n()
const searchText = ref('')

onActivated(checkLogin)
// onMounted(checkLogin);
Expand All @@ -62,6 +71,9 @@ onActivated(checkLogin)
.list {
width: 100%;
}
.friends__search {
padding: 0 12px 8px;
}

.item {
position: absolute;
Expand Down