chore: enforce lint across codebase and ban "maybe" in identifiers
Remove member-ordering and complexity eslint-disable comments by reordering class members and applying targeted fixes. Add metoyou/no-maybe-in-naming, type-safe WebRTC e2e harness helpers, and resolve remaining lint errors so npm run lint exits cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -84,55 +83,68 @@ interface DmStatusLabel {
|
||||
export class DmChatComponent {
|
||||
@ViewChild(ChatMessageComposerComponent) composer?: ChatMessageComposerComponent;
|
||||
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly store = inject(Store);
|
||||
private readonly electronBridge = inject(ElectronBridgeService);
|
||||
private readonly attachments = inject(AttachmentFacade);
|
||||
private readonly klipy = inject(KlipyService);
|
||||
private readonly linkMetadata = inject(LinkMetadataService);
|
||||
private readonly profileCard = inject(ProfileCardService);
|
||||
private readonly viewport = inject(ViewportService);
|
||||
private readonly metadataRequestKeys = new Set<string>();
|
||||
private openedConversationId: string | null = null;
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
readonly isMobile = this.viewport.isMobile;
|
||||
|
||||
readonly directCalls = inject(DirectCallService);
|
||||
|
||||
readonly directMessages = inject(DirectMessageService);
|
||||
|
||||
readonly currentUser = this.store.selectSignal(selectCurrentUser);
|
||||
|
||||
readonly allUsers = this.store.selectSignal(selectAllUsers);
|
||||
|
||||
readonly showGifPicker = signal(false);
|
||||
|
||||
readonly conversationId = input<string | null>(null);
|
||||
|
||||
readonly showCallButton = input(true);
|
||||
|
||||
readonly composerBottomPadding = signal(140);
|
||||
|
||||
readonly gifPickerAnchorRight = signal(16);
|
||||
|
||||
readonly linkMetadataByMessageId = signal<Record<string, LinkMetadata[]>>({});
|
||||
|
||||
readonly replyTo = signal<Message | null>(null);
|
||||
|
||||
readonly lightboxState = signal<ChatLightboxState | null>(null);
|
||||
|
||||
readonly galleryAttachments = signal<Attachment[] | null>(null);
|
||||
|
||||
readonly imageContextMenu = signal<ChatMessageImageContextMenuEvent | null>(null);
|
||||
|
||||
readonly routeConversationId = toSignal(this.route.paramMap.pipe(map((params) => params.get('conversationId'))), {
|
||||
initialValue: this.route.snapshot.paramMap.get('conversationId')
|
||||
});
|
||||
|
||||
readonly effectiveConversationId = computed(() => this.conversationId() ?? this.routeConversationId());
|
||||
|
||||
readonly currentUserId = computed(() => this.currentUser()?.oderId || this.currentUser()?.id || '');
|
||||
|
||||
readonly conversation = this.directMessages.selectedConversation;
|
||||
|
||||
readonly klipyEnabled = computed(() => this.klipy.isEnabled(null));
|
||||
|
||||
readonly conversationKey = computed(() => this.conversation()?.id ?? 'dm:none');
|
||||
|
||||
readonly typingUsers = computed(() => {
|
||||
void this.directMessages.typingEntries();
|
||||
|
||||
return this.directMessages.typingUsers(this.conversation()?.id);
|
||||
});
|
||||
|
||||
readonly peerUser = computed(() => {
|
||||
const conversation = this.conversation();
|
||||
|
||||
return conversation ? this.peerUserFor(conversation) : null;
|
||||
});
|
||||
|
||||
readonly isGroupConversation = computed(() => {
|
||||
const conversation = this.conversation();
|
||||
|
||||
return !!conversation && (conversation.kind === 'group' || conversation.participants.length > 2);
|
||||
});
|
||||
|
||||
readonly participantUsers = computed<User[]>(() => {
|
||||
const conversation = this.conversation();
|
||||
const knownUsers = this.allUsers();
|
||||
@@ -164,6 +176,7 @@ export class DmChatComponent {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
readonly messageStatuses = computed<DmStatusLabel[]>(() => {
|
||||
const conversation = this.conversation();
|
||||
const currentUserId = this.currentUserId();
|
||||
@@ -179,6 +192,7 @@ export class DmChatComponent {
|
||||
status: message.status
|
||||
}));
|
||||
});
|
||||
|
||||
readonly chatMessages = computed<Message[]>(() => {
|
||||
const conversation = this.conversation();
|
||||
const metadataByMessageId = this.linkMetadataByMessageId();
|
||||
@@ -196,7 +210,11 @@ export class DmChatComponent {
|
||||
roomId: conversation.id,
|
||||
channelId: 'direct-message',
|
||||
senderId: message.senderId,
|
||||
senderName: knownUser?.displayName || participant?.displayName || (message.senderId === this.currentUserId() ? this.i18n.instant('common.labels.you') : message.senderId),
|
||||
senderName: knownUser?.displayName
|
||||
|| participant?.displayName
|
||||
|| (message.senderId === this.currentUserId()
|
||||
? this.i18n.instant('common.labels.you')
|
||||
: message.senderId),
|
||||
content: message.content,
|
||||
timestamp: message.timestamp,
|
||||
kind: message.kind,
|
||||
@@ -209,6 +227,7 @@ export class DmChatComponent {
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
readonly peerName = computed(() => {
|
||||
const conversation = this.conversation();
|
||||
const currentUserId = this.currentUserId();
|
||||
@@ -221,6 +240,7 @@ export class DmChatComponent {
|
||||
|
||||
return peerId ? conversation?.participantProfiles[peerId]?.displayName || peerId : this.i18n.instant('dm.chat.defaultTitle');
|
||||
});
|
||||
|
||||
readonly peerCallIcon = computed(() => {
|
||||
const conversation = this.conversation();
|
||||
|
||||
@@ -232,6 +252,7 @@ export class DmChatComponent {
|
||||
|
||||
return peer && this.directCalls.isCallingUser(peer) ? 'lucidePhoneCall' : 'lucidePhone';
|
||||
});
|
||||
|
||||
readonly canCallConversation = computed(() => {
|
||||
const conversation = this.conversation();
|
||||
|
||||
@@ -246,6 +267,28 @@ export class DmChatComponent {
|
||||
return !!this.peerUser();
|
||||
});
|
||||
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
|
||||
private readonly store = inject(Store);
|
||||
|
||||
private readonly electronBridge = inject(ElectronBridgeService);
|
||||
|
||||
private readonly attachments = inject(AttachmentFacade);
|
||||
|
||||
private readonly klipy = inject(KlipyService);
|
||||
|
||||
private readonly linkMetadata = inject(LinkMetadataService);
|
||||
|
||||
private readonly profileCard = inject(ProfileCardService);
|
||||
|
||||
private readonly viewport = inject(ViewportService);
|
||||
|
||||
private readonly metadataRequestKeys = new Set<string>();
|
||||
|
||||
private openedConversationId: string | null = null;
|
||||
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const conversationId = this.effectiveConversationId();
|
||||
@@ -659,4 +702,5 @@ export class DmChatComponent {
|
||||
|
||||
return `${names.slice(0, 3).join(', ')} +${names.length - 3}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -60,15 +59,16 @@ const EXIT_ANIMATION_MS = 160;
|
||||
styleUrl: './dm-rail.component.scss'
|
||||
})
|
||||
export class DmRailComponent implements OnDestroy {
|
||||
private readonly router = inject(Router);
|
||||
private readonly store = inject(Store);
|
||||
private readonly exitTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
readonly directMessages = inject(DirectMessageService);
|
||||
|
||||
readonly friends = inject(FriendService);
|
||||
|
||||
readonly users = this.store.selectSignal(selectAllUsers);
|
||||
|
||||
readonly currentUser = this.store.selectSignal(selectCurrentUser);
|
||||
|
||||
readonly currentUserId = computed(() => this.currentUser()?.oderId || this.currentUser()?.id || '');
|
||||
|
||||
readonly activeConversationId = toSignal(
|
||||
this.router.events.pipe(
|
||||
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
|
||||
@@ -76,11 +76,15 @@ export class DmRailComponent implements OnDestroy {
|
||||
),
|
||||
{ initialValue: this.getConversationIdFromUrl(this.router.url) }
|
||||
);
|
||||
|
||||
readonly friendUsers = computed(() => this.users().filter((user) =>
|
||||
this.friends.isFriend(user.oderId || user.id) && (user.oderId || user.id) !== this.currentUserId()
|
||||
));
|
||||
|
||||
readonly railItems = signal<DmRailItem[]>([]);
|
||||
|
||||
readonly contextMenu = signal<DmRailContextMenuState | null>(null);
|
||||
|
||||
readonly unreadRailItems = computed<DmRailItem[]>(() => {
|
||||
const currentUserId = this.currentUserId();
|
||||
const items = new Map<string, DmRailItem>();
|
||||
@@ -143,6 +147,7 @@ export class DmRailComponent implements OnDestroy {
|
||||
|
||||
return Array.from(items.values()).filter((item) => item.conversation && item.unreadCount > 0);
|
||||
});
|
||||
|
||||
readonly isOnDirectMessages = toSignal(
|
||||
this.router.events.pipe(
|
||||
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
|
||||
@@ -151,6 +156,14 @@ export class DmRailComponent implements OnDestroy {
|
||||
{ initialValue: this.router.url.startsWith('/dm') }
|
||||
);
|
||||
|
||||
private readonly router = inject(Router);
|
||||
|
||||
private readonly store = inject(Store);
|
||||
|
||||
private readonly exitTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const unreadItems = this.unreadRailItems();
|
||||
@@ -334,4 +347,5 @@ export class DmRailComponent implements OnDestroy {
|
||||
|
||||
return `${names.slice(0, 3).join(', ')} +${names.length - 3}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -44,26 +43,42 @@ import { DirectMessageService } from '../../application/services/direct-message.
|
||||
templateUrl: './dm-conversation-item.component.html'
|
||||
})
|
||||
export class DmConversationItemComponent {
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly router = inject(Router);
|
||||
private readonly store = inject(Store);
|
||||
private readonly attachments = inject(AttachmentFacade);
|
||||
private readonly directMessages = inject(DirectMessageService);
|
||||
private readonly directCalls = inject(DirectCallService);
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
readonly conversation = input.required<DirectMessageConversation>();
|
||||
|
||||
readonly conversationOpened = output<string>();
|
||||
|
||||
readonly users = this.store.selectSignal(selectAllUsers);
|
||||
|
||||
readonly routeConversationId = toSignal(this.route.paramMap.pipe(map((params) => params.get('conversationId'))), {
|
||||
initialValue: this.route.snapshot.paramMap.get('conversationId')
|
||||
});
|
||||
|
||||
readonly isSelected = computed(() => this.routeConversationId() === this.conversation().id);
|
||||
|
||||
readonly peerName = computed(() => this.resolvePeerName(this.conversation()));
|
||||
|
||||
readonly peerAvatarUrl = computed(() => this.resolvePeerAvatarUrl(this.conversation()));
|
||||
|
||||
readonly lastMessagePreview = computed(() => this.resolveLastMessagePreview(this.conversation()));
|
||||
|
||||
readonly canCall = computed(() => this.canCallConversation(this.conversation()));
|
||||
|
||||
readonly callIcon = computed(() => this.conversationCallIcon(this.conversation()));
|
||||
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
|
||||
private readonly router = inject(Router);
|
||||
|
||||
private readonly store = inject(Store);
|
||||
|
||||
private readonly attachments = inject(AttachmentFacade);
|
||||
|
||||
private readonly directMessages = inject(DirectMessageService);
|
||||
|
||||
private readonly directCalls = inject(DirectCallService);
|
||||
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const conversation = this.conversation();
|
||||
@@ -118,7 +133,12 @@ export class DmConversationItemComponent {
|
||||
const peerId = this.peerId(conversation);
|
||||
const knownUser = this.peerUser(conversation);
|
||||
|
||||
return peerId ? knownUser?.displayName || conversation.participantProfiles[peerId]?.displayName || peerId : this.i18n.instant('dm.chat.defaultTitle');
|
||||
if (!peerId)
|
||||
return this.i18n.instant('dm.chat.defaultTitle');
|
||||
|
||||
return knownUser?.displayName
|
||||
|| conversation.participantProfiles[peerId]?.displayName
|
||||
|| peerId;
|
||||
}
|
||||
|
||||
private resolvePeerAvatarUrl(conversation: DirectMessageConversation): string | undefined {
|
||||
@@ -229,4 +249,5 @@ export class DmConversationItemComponent {
|
||||
? this.i18n.instant('dm.previews.oneAttachment')
|
||||
: this.i18n.instant('dm.previews.manyAttachments');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -33,12 +32,16 @@ import { DmConversationItemComponent } from './dm-conversation-item.component';
|
||||
templateUrl: './dm-conversations-panel.component.html'
|
||||
})
|
||||
export class DmConversationsPanelComponent {
|
||||
private readonly theme = inject(ThemeService);
|
||||
readonly directMessages = inject(DirectMessageService);
|
||||
|
||||
readonly listPanelStyles = computed(() => this.theme.getLayoutItemStyles('dmConversationsPanel'));
|
||||
|
||||
readonly conversationSelected = output<string>();
|
||||
|
||||
private readonly theme = inject(ThemeService);
|
||||
|
||||
trackConversationId(index: number, conversation: DirectMessageConversation): string {
|
||||
return conversation.id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
Component,
|
||||
@@ -55,21 +54,18 @@ interface SwiperElement extends HTMLElement {
|
||||
templateUrl: './dm-workspace.component.html'
|
||||
})
|
||||
export class DmWorkspaceComponent implements OnDestroy {
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly router = inject(Router);
|
||||
private readonly theme = inject(ThemeService);
|
||||
private readonly viewport = inject(ViewportService);
|
||||
private readonly zone = inject(NgZone);
|
||||
private readonly directCalls = inject(DirectCallService);
|
||||
private lastSeenConversationId: string | null = null;
|
||||
private swiperListenerAttached: SwiperElement | null = null;
|
||||
readonly directMessages = inject(DirectMessageService);
|
||||
|
||||
readonly routeConversationId = toSignal(this.route.paramMap.pipe(map((params) => params.get('conversationId'))), {
|
||||
initialValue: this.route.snapshot.paramMap.get('conversationId')
|
||||
});
|
||||
|
||||
readonly layoutStyles = computed(() => this.theme.getLayoutContainerStyles('dmLayout'));
|
||||
|
||||
readonly isMobile = this.viewport.isMobile;
|
||||
|
||||
readonly swiperRef = viewChild<ElementRef<SwiperElement>>('swiperEl');
|
||||
|
||||
readonly activeCall = computed(() => {
|
||||
const currentSession = this.directCalls.currentSession();
|
||||
const visibleSessions = this.directCalls.visibleActiveSessions();
|
||||
@@ -80,6 +76,22 @@ export class DmWorkspaceComponent implements OnDestroy {
|
||||
/** Active page within the mobile single-pane navigation flow. Ignored on desktop. */
|
||||
readonly mobilePage = signal<DmWorkspaceMobilePage>('conversations');
|
||||
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
|
||||
private readonly router = inject(Router);
|
||||
|
||||
private readonly theme = inject(ThemeService);
|
||||
|
||||
private readonly viewport = inject(ViewportService);
|
||||
|
||||
private readonly zone = inject(NgZone);
|
||||
|
||||
private readonly directCalls = inject(DirectCallService);
|
||||
|
||||
private lastSeenConversationId: string | null = null;
|
||||
|
||||
private swiperListenerAttached: SwiperElement | null = null;
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const conversationId = this.routeConversationId();
|
||||
@@ -171,5 +183,6 @@ export class DmWorkspaceComponent implements OnDestroy {
|
||||
ngOnDestroy(): void {
|
||||
this.directMessages.closeConversationView(this.routeConversationId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -16,7 +15,7 @@ import {
|
||||
lucideUsers
|
||||
} from '@ng-icons/lucide';
|
||||
|
||||
import { AppI18nService, APP_TRANSLATE_IMPORTS } from '../../../../core/i18n';
|
||||
import { APP_TRANSLATE_IMPORTS } from '../../../../core/i18n';
|
||||
import { AutoFocusDirective, SelectOnFocusDirective } from '../../../../shared/directives';
|
||||
import { UserSearchListComponent } from '../user-search-list/user-search-list.component';
|
||||
import { selectAllUsers } from '../../../../store/users/users.selectors';
|
||||
@@ -47,10 +46,7 @@ import { selectSavedRooms } from '../../../../store/rooms/rooms.selectors';
|
||||
}
|
||||
})
|
||||
export class FindPeopleComponent {
|
||||
private store = inject(Store);
|
||||
searchQuery = signal('');
|
||||
private users = this.store.selectSignal(selectAllUsers);
|
||||
private savedRooms = this.store.selectSignal(selectSavedRooms);
|
||||
|
||||
/** True when the account has any people to surface (known users or server members). */
|
||||
hasDiscoverablePeople = computed(() => {
|
||||
@@ -61,7 +57,14 @@ export class FindPeopleComponent {
|
||||
return this.savedRooms().some((room) => (room.members?.length ?? 0) > 0);
|
||||
});
|
||||
|
||||
private store = inject(Store);
|
||||
|
||||
private users = this.store.selectSignal(selectAllUsers);
|
||||
|
||||
private savedRooms = this.store.selectSignal(selectSavedRooms);
|
||||
|
||||
onSearchChange(query: string): void {
|
||||
this.searchQuery.set(query);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -24,20 +23,26 @@ import type { User } from '../../../../shared-kernel';
|
||||
templateUrl: './friend-button.component.html'
|
||||
})
|
||||
export class FriendButtonComponent {
|
||||
private readonly friends = inject(FriendService);
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
readonly user = input.required<User>();
|
||||
|
||||
readonly userId = computed(() => this.user().oderId || this.user().id);
|
||||
|
||||
readonly isFriend = computed(() => this.friends.isFriend(this.userId()));
|
||||
|
||||
readonly ariaLabel = computed(() =>
|
||||
this.isFriend()
|
||||
? this.i18n.instant('dm.friend.remove')
|
||||
: this.i18n.instant('dm.friend.add')
|
||||
);
|
||||
|
||||
private readonly friends = inject(FriendService);
|
||||
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
toggle(event: Event): void {
|
||||
event.stopPropagation();
|
||||
void this.friends.toggleFriend(this.userId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
@@ -39,16 +38,18 @@ import type { User } from '../../../../shared-kernel';
|
||||
templateUrl: './user-search-list.component.html'
|
||||
})
|
||||
export class UserSearchListComponent {
|
||||
private readonly store = inject(Store);
|
||||
private readonly router = inject(Router);
|
||||
private readonly directMessages = inject(DirectMessageService);
|
||||
readonly directCalls = inject(DirectCallService);
|
||||
|
||||
readonly friends = inject(FriendService);
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
readonly searchQuery = input('');
|
||||
|
||||
readonly users = this.store.selectSignal(selectAllUsers);
|
||||
|
||||
readonly savedRooms = this.store.selectSignal(selectSavedRooms);
|
||||
|
||||
readonly currentUser = this.store.selectSignal(selectCurrentUser);
|
||||
|
||||
readonly discoveredUsers = computed(() => {
|
||||
const usersById = new Map<string, User>();
|
||||
|
||||
@@ -81,6 +82,7 @@ export class UserSearchListComponent {
|
||||
|
||||
return Array.from(usersById.values());
|
||||
});
|
||||
|
||||
readonly matchingUsers = computed(() => {
|
||||
const query = this.normalizedSearchQuery();
|
||||
const currentUserId = this.currentUserKey();
|
||||
@@ -90,13 +92,23 @@ export class UserSearchListComponent {
|
||||
.filter((user) => this.matchesQuery(user, query))
|
||||
.slice(0, 24);
|
||||
});
|
||||
|
||||
readonly friendResults = computed(() => this.matchingUsers().filter((user) => this.friends.isFriend(this.userKey(user))));
|
||||
|
||||
readonly results = computed(() => {
|
||||
const friendIds = this.friends.friendIds();
|
||||
|
||||
return this.matchingUsers().filter((user) => !friendIds.has(this.userKey(user)));
|
||||
});
|
||||
|
||||
private readonly store = inject(Store);
|
||||
|
||||
private readonly router = inject(Router);
|
||||
|
||||
private readonly directMessages = inject(DirectMessageService);
|
||||
|
||||
private readonly i18n = inject(AppI18nService);
|
||||
|
||||
async messageUser(user: User): Promise<void> {
|
||||
const conversation = await this.directMessages.createConversation(user);
|
||||
|
||||
@@ -151,4 +163,5 @@ export class UserSearchListComponent {
|
||||
.filter((value): value is string => !!value)
|
||||
.some((value) => value.toLowerCase().includes(query));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user