fixes from peer review
This commit is contained in:
parent
19204851a0
commit
ef7cde6bc6
@ -1134,7 +1134,8 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async updateLatestNote(note: MiNote) {
|
private async updateLatestNote(note: MiNote) {
|
||||||
// Ignore DMs
|
// Ignore DMs.
|
||||||
|
// Followers-only posts are *included*, as this table is used to back the "following" feed.
|
||||||
if (note.visibility === 'specified') return;
|
if (note.visibility === 'specified') return;
|
||||||
|
|
||||||
// Ignore pure renotes
|
// Ignore pure renotes
|
||||||
@ -1143,7 +1144,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
// Make sure that this isn't an *older* post.
|
// Make sure that this isn't an *older* post.
|
||||||
// We can get older posts through replies, lookups, etc.
|
// We can get older posts through replies, lookups, etc.
|
||||||
const currentLatest = await this.latestNotesRepository.findOneBy({ userId: note.userId });
|
const currentLatest = await this.latestNotesRepository.findOneBy({ userId: note.userId });
|
||||||
if (currentLatest != null && currentLatest.userId >= note.id) return;
|
if (currentLatest != null && currentLatest.noteId >= note.id) return;
|
||||||
|
|
||||||
// Record this as the latest note for the given user
|
// Record this as the latest note for the given user
|
||||||
const latestNote = new LatestNote({
|
const latestNote = new LatestNote({
|
||||||
|
@ -240,6 +240,10 @@ export class NoteDeleteService {
|
|||||||
// If it's a DM, then it can't possibly be the latest note so we can safely skip this.
|
// If it's a DM, then it can't possibly be the latest note so we can safely skip this.
|
||||||
if (note.visibility === 'specified') return;
|
if (note.visibility === 'specified') return;
|
||||||
|
|
||||||
|
// Check if the deleted note was possibly the latest for the user
|
||||||
|
const hasLatestNote = await this.latestNotesRepository.existsBy({ userId: note.userId });
|
||||||
|
if (hasLatestNote) return;
|
||||||
|
|
||||||
// Find the newest remaining note for the user.
|
// Find the newest remaining note for the user.
|
||||||
// We exclude DMs and pure renotes.
|
// We exclude DMs and pure renotes.
|
||||||
const nextLatest = await this.notesRepository
|
const nextLatest = await this.notesRepository
|
||||||
@ -269,12 +273,14 @@ export class NoteDeleteService {
|
|||||||
noteId: nextLatest.id,
|
noteId: nextLatest.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// We use an upsert because this deleted note might not have been the newest.
|
// When inserting the latest note, it's possible that another worker has "raced" the insert and already added a newer note.
|
||||||
// In that case, the latest note may already be populated for this user.
|
// We must use orIgnore() to ensure that the query ignores conflicts, otherwise an exception may be thrown.
|
||||||
// We want postgres to do nothing instead of replacing the value or returning an error.
|
await this.latestNotesRepository
|
||||||
await this.latestNotesRepository.upsert(latestNote, {
|
.createQueryBuilder('latest')
|
||||||
conflictPaths: ['userId'],
|
.insert()
|
||||||
skipUpdateIfNoValuesChanged: true,
|
.into(LatestNote)
|
||||||
});
|
.values(latestNote)
|
||||||
|
.orIgnore()
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkA>
|
</MkA>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
<Mfm :class="$style.text" :text="getNoteSummary(note)" :isBlock="false" :plain="true" :nowrap="false" :isNote="true" :author="note.user"/>
|
<Mfm :class="$style.text" :text="getNoteSummary(note)" :isBlock="true" :plain="true" :nowrap="false" :isNote="true" nyaize="respect" :author="note.user"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,7 +70,7 @@ export const navbarItemDef = reactive({
|
|||||||
},
|
},
|
||||||
following: {
|
following: {
|
||||||
title: i18n.ts.following,
|
title: i18n.ts.following,
|
||||||
icon: 'ti ti-user-check',
|
icon: 'ph-user-check ph-bold ph-lg',
|
||||||
to: '/following-feed',
|
to: '/following-feed',
|
||||||
},
|
},
|
||||||
lists: {
|
lists: {
|
||||||
|
@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkPagination>
|
</MkPagination>
|
||||||
</MkPullToRefresh>
|
</MkPullToRefresh>
|
||||||
|
|
||||||
<MkPullToRefresh v-if="isDesktop" :refresher="() => reloadUserNotes()">
|
<MkPullToRefresh v-if="isWideViewport" :refresher="() => reloadUserNotes()">
|
||||||
<div v-if="selectedUser" :class="$style.userInfo">
|
<div v-if="selectedUser" :class="$style.userInfo">
|
||||||
<MkUserInfo class="user" :user="selectedUser"/>
|
<MkUserInfo class="user" :user="selectedUser"/>
|
||||||
<MkNotes :noGap="true" :pagination="userNotesPagination"/>
|
<MkNotes :noGap="true" :pagination="userNotesPagination"/>
|
||||||
@ -62,7 +62,7 @@ import FollowingFeedEntry from '@/components/FollowingFeedEntry.vue';
|
|||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkUserInfo from '@/components/MkUserInfo.vue';
|
import MkUserInfo from '@/components/MkUserInfo.vue';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import {useRouter} from "@/router/supplier.js";
|
import { useRouter } from '@/router/supplier.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
initialTab?: FollowingFeedTab,
|
initialTab?: FollowingFeedTab,
|
||||||
@ -79,17 +79,17 @@ const currentTab: Ref<FollowingFeedTab> = ref(props.initialTab);
|
|||||||
const mutualsOnly: Ref<boolean> = computed(() => currentTab.value === mutualsTab);
|
const mutualsOnly: Ref<boolean> = computed(() => currentTab.value === mutualsTab);
|
||||||
|
|
||||||
// We have to disable the per-user feed on small displays, and it must be done through JS instead of CSS.
|
// We have to disable the per-user feed on small displays, and it must be done through JS instead of CSS.
|
||||||
// Otherwise, the second column will resources in the background.
|
// Otherwise, the second column will waste resources in the background.
|
||||||
const desktopMediaQuery = window.matchMedia('(min-width: 750px)');
|
const wideViewportQuery = window.matchMedia('(min-width: 750px)');
|
||||||
const isDesktop: Ref<boolean> = ref(desktopMediaQuery.matches);
|
const isWideViewport: Ref<boolean> = ref(wideViewportQuery.matches);
|
||||||
desktopMediaQuery.addEventListener('change', () => isDesktop.value = desktopMediaQuery.matches);
|
wideViewportQuery.addEventListener('change', () => isWideViewport.value = wideViewportQuery.matches);
|
||||||
|
|
||||||
const selectedUserError: Ref<string> = ref('');
|
const selectedUserError: Ref<string> = ref('');
|
||||||
const selectedUserId: Ref<string> = ref('');
|
const selectedUserId: Ref<string> = ref('');
|
||||||
const selectedUser: Ref<Misskey.entities.UserDetailed | null> = ref(null);
|
const selectedUser: Ref<Misskey.entities.UserDetailed | null> = ref(null);
|
||||||
|
|
||||||
async function userSelected(user: Misskey.entities.UserLite): Promise<void> {
|
async function userSelected(user: Misskey.entities.UserLite): Promise<void> {
|
||||||
if (isDesktop.value) {
|
if (isWideViewport.value) {
|
||||||
await showUserNotes(user.id);
|
await showUserNotes(user.id);
|
||||||
} else {
|
} else {
|
||||||
if (user.host) {
|
if (user.host) {
|
||||||
@ -139,7 +139,7 @@ async function onListReady(): Promise<void> {
|
|||||||
// This just gets the first user ID
|
// This just gets the first user ID
|
||||||
const selectedNote: Misskey.entities.Note = latestNotesPaging.value.items.values().next().value;
|
const selectedNote: Misskey.entities.Note = latestNotesPaging.value.items.values().next().value;
|
||||||
|
|
||||||
// Wait for 1 second to match the animation effects.
|
// Wait for 1 second to match the animation effects in MkHorizontalSwipe, MkPullToRefresh, and MkPagination.
|
||||||
// Otherwise, the page appears to load "backwards".
|
// Otherwise, the page appears to load "backwards".
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
await showUserNotes(selectedNote.userId);
|
await showUserNotes(selectedNote.userId);
|
||||||
@ -179,19 +179,19 @@ const headerActions: PageHeaderItem[] = [
|
|||||||
const headerTabs = computed(() => [
|
const headerTabs = computed(() => [
|
||||||
{
|
{
|
||||||
key: followingTab,
|
key: followingTab,
|
||||||
icon: 'ti ti-user-check',
|
icon: 'ph-user-check ph-bold ph-lg',
|
||||||
title: i18n.ts.following,
|
title: i18n.ts.following,
|
||||||
} satisfies Tab,
|
} satisfies Tab,
|
||||||
{
|
{
|
||||||
key: mutualsTab,
|
key: mutualsTab,
|
||||||
icon: 'ti ti-user-heart',
|
icon: 'ph-user-switch ph-bold ph-lg',
|
||||||
title: i18n.ts.mutuals,
|
title: i18n.ts.mutuals,
|
||||||
} satisfies Tab,
|
} satisfies Tab,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
definePageMetadata(() => ({
|
definePageMetadata(() => ({
|
||||||
title: i18n.ts.following,
|
title: i18n.ts.following,
|
||||||
icon: 'ti ti-user-check',
|
icon: 'ph-user-check ph-bold ph-lg',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -312,7 +312,7 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList
|
|||||||
icon: basicTimelineIconClass(tl),
|
icon: basicTimelineIconClass(tl),
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
})), {
|
})), {
|
||||||
icon: 'ti ti-user-check',
|
icon: 'ph-user-check ph-bold ph-lg',
|
||||||
title: i18n.ts.following,
|
title: i18n.ts.following,
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
onClick: () => router.push('/following-feed'),
|
onClick: () => router.push('/following-feed'),
|
||||||
|
@ -348,7 +348,6 @@ export function pluginReplaceIcons() {
|
|||||||
'ti ti-user-circle': 'ph-user-circle ph-bold ph-lg',
|
'ti ti-user-circle': 'ph-user-circle ph-bold ph-lg',
|
||||||
'ti ti-user-edit': 'ph-user-list ph-bold ph-lg',
|
'ti ti-user-edit': 'ph-user-list ph-bold ph-lg',
|
||||||
'ti ti-user-exclamation': 'ph-warning-circle ph-bold ph-lg',
|
'ti ti-user-exclamation': 'ph-warning-circle ph-bold ph-lg',
|
||||||
'ti ti-user-heart': 'ph-user-switch ph-bold ph-lg',
|
|
||||||
'ti ti-user-off': 'ph-user-minus ph-bold ph-lg',
|
'ti ti-user-off': 'ph-user-minus ph-bold ph-lg',
|
||||||
'ti ti-user-plus': 'ph-user-plus ph-bold ph-lg',
|
'ti ti-user-plus': 'ph-user-plus ph-bold ph-lg',
|
||||||
'ti ti-user-search': 'ph-user-circle ph-bold ph-lg',
|
'ti ti-user-search': 'ph-user-circle ph-bold ph-lg',
|
||||||
|
Loading…
Reference in New Issue
Block a user