mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-07 01:43:15 +00:00
fix(extension): harden title privacy edge cases
This commit is contained in:
+20
-2
@@ -391,6 +391,19 @@ function withTitlePrivacy(payload, settings, keys) {
|
||||
return applyTitlePrivacyToPayload(payload, settings?.titlePrivacyMode, keys);
|
||||
}
|
||||
|
||||
function emitEpisodeLobbyForCurrentPrivacy() {
|
||||
if (!episodeLobby || episodeLobby.initiatorPeerId !== peerId) return;
|
||||
getSettings().then(settings => {
|
||||
if (!episodeLobby || episodeLobby.initiatorPeerId !== peerId) return;
|
||||
const expectedTitle = sanitizeSharedTitle(episodeLobby.expectedTitle, settings.titlePrivacyMode);
|
||||
if (expectedTitle) {
|
||||
emit(EVENTS.EPISODE_LOBBY, { peerId, expectedTitle });
|
||||
}
|
||||
}).catch(err => {
|
||||
addLog('Episode lobby privacy error: ' + err.message, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Privacy + correctness: only onboardingComplete and dismissedHints belong in
|
||||
// storage.sync. Everything else is per-device local storage. This actively
|
||||
// removes legacy keys that older versions wrote to sync (and that would
|
||||
@@ -1256,7 +1269,7 @@ function handleServerEvent(event, data) {
|
||||
}
|
||||
|
||||
if (episodeLobby && episodeLobby.initiatorPeerId === peerId) {
|
||||
emit(EVENTS.EPISODE_LOBBY, { peerId, expectedTitle: episodeLobby.expectedTitle });
|
||||
emitEpisodeLobbyForCurrentPrivacy();
|
||||
}
|
||||
}
|
||||
} else if (data.status === 'left') {
|
||||
@@ -1637,7 +1650,6 @@ chrome.alarms.onAlarm.addListener(async (alarm) => {
|
||||
status: 'heartbeat',
|
||||
username: settings.username,
|
||||
tabTitle: sharedTitles.tabTitle,
|
||||
mediaTitle: sharedTitles.mediaTitle,
|
||||
desynced: hcmDesynced
|
||||
});
|
||||
}
|
||||
@@ -2330,6 +2342,12 @@ async function handleAsyncMessage(message, sender, sendResponse) {
|
||||
sendResponse({ status: 'ok' });
|
||||
} else if (message.type === 'TITLE_PRIVACY_CHANGED') {
|
||||
const settings = await getSettings();
|
||||
if (episodeLobby && episodeLobby.initiatorPeerId === peerId) {
|
||||
const nextLobbyTitle = sanitizeSharedTitle(episodeLobby.expectedTitle, settings.titlePrivacyMode);
|
||||
if (!nextLobbyTitle || nextLobbyTitle !== episodeLobby.expectedTitle) {
|
||||
cancelEpisodeLobby('Title privacy changed');
|
||||
}
|
||||
}
|
||||
if (currentRoom && settings.titlePrivacyMode === 'hidden') {
|
||||
const sharedTitles = getSharedTitleFields(settings);
|
||||
emit(EVENTS.PEER_STATUS, {
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Reiniciar tutorial",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Reiniciar el tutorial de inicio",
|
||||
"HINT_SELECT_VIDEO": "¡Selecciona tu video aquí!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "T?tulos compartidos",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Elige c?mo se comparten los t?tulos de pesta?a y multimedia con la sala. El modo oculto borra los t?tulos del servidor y desactiva la creaci?n de salas de episodio desde tu cliente.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "T?tulos completos",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Solo episodio",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Ocultos"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Redémarrer le tutoriel",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Recommencer le tutoriel d'intégration",
|
||||
"HINT_SELECT_VIDEO": "Sélectionnez votre vidéo ici !",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "Titres partag?s",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choisissez comment les titres des onglets et des m?dias sont partag?s avec le salon. Le mode masqu? efface les titres du serveur et d?sactive la cr?ation de salons d??pisode depuis votre client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Titres complets",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "?pisode seulement",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Masqu?s"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Riavvia Tutorial",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Riavvia il tutorial introduttivo",
|
||||
"HINT_SELECT_VIDEO": "Scegli il tuo video qui!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "Titoli condivisi",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Scegli come condividere con la stanza i titoli delle schede e dei contenuti multimediali. La modalit? nascosta cancella i titoli sul server e disattiva la creazione di lobby episodio dal tuo client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Titoli completi",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Solo episodio",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Nascosti"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "チュートリアルを再起動",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "オンボーディングチュートリアルを再起動する",
|
||||
"HINT_SELECT_VIDEO": "ここで動画を選択してください!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "??????",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "???????????????????????????????????????????????????????????????????????????????????",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "???????",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "???????",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "???"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "튜토리얼 다시 시작",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "온보딩 튜토리얼을 다시 시작합니다",
|
||||
"HINT_SELECT_VIDEO": "여기에서 비디오를 선택하세요!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "?? ??",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "? ??? ??? ??? ?? ??? ???? ?????. ?? ??? ??? ??? ??? ? ??????? ???? ??? ??? ??? ???.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "?? ??",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "?????",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "??"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Tutorial opnieuw starten",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Start de introductie-tutorial opnieuw",
|
||||
"HINT_SELECT_VIDEO": "Selecteer hier je video!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "Gedeelde titels",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Kies hoe tab- en mediatitels met de kamer worden gedeeld. Verborgen modus wist titels op de server en schakelt het maken van afleveringslobby?s vanaf jouw client uit.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Volledige titels",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Alleen aflevering",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Verborgen"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Uruchom samouczek ponownie",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Uruchom ponownie samouczek powitalny",
|
||||
"HINT_SELECT_VIDEO": "Wybierz swoje wideo tutaj!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "Udost?pniane tytu?y",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Wybierz, jak tytu?y kart i multimedi?w s? udost?pniane w pokoju. Tryb ukryty czy?ci tytu?y na serwerze i wy??cza tworzenie poczekalni odcinka z tego klienta.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Pe?ne tytu?y",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Tylko odcinek",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Ukryte"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Reiniciar tutorial",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Reiniciar o tutorial de introdução",
|
||||
"HINT_SELECT_VIDEO": "Selecione seu vídeo aqui!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "T?tulos compartilhados",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Escolha como os t?tulos da aba e da m?dia s?o compartilhados com a sala. O modo oculto limpa os t?tulos no servidor e desativa a cria??o de lobby de epis?dio pelo seu cliente.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "T?tulos completos",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "S? epis?dio",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Ocultos"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Reiniciar Tutorial",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Reiniciar o tutorial introdutório",
|
||||
"HINT_SELECT_VIDEO": "Selecione o seu vídeo aqui!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "T?tulos partilhados",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Escolhe como os t?tulos do separador e do conte?do multim?dia s?o partilhados com a sala. O modo oculto limpa os t?tulos no servidor e desativa a cria??o de salas de epis?dio pelo teu cliente.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "T?tulos completos",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "S? epis?dio",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Ocultos"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Перезапустить обучение",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Запустить приветственное руководство заново",
|
||||
"HINT_SELECT_VIDEO": "Выберите ваше видео здесь!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "????? ????????",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "????????, ??? ???????? ??????? ? ????? ?????????? ? ???????. ??????? ????? ??????? ???????? ?? ??????? ? ????????? ???????? ????? ???????? ? ?????? ???????.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "?????? ????????",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "?????? ??????",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "??????"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Eğitimi Yeniden Başlat",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Tanıtım eğitimini yeniden başlat",
|
||||
"HINT_SELECT_VIDEO": "Videonuzu buradan seçin!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "Payla??lan ba?l?klar",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Sekme ve medya ba?l?klar?n?n odayla nas?l payla??laca??n? se?in. Gizli mod sunucudaki ba?l?klar? temizler ve istemcinizden b?l?m lobisi olu?turmay? devre d??? b?rak?r.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Tam ba?l?klar",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Sadece b?l?m",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Gizli"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "Перезапустіть підручник",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "Перезапустіть навчальний посібник із адаптації",
|
||||
"HINT_SELECT_VIDEO": "Виберіть своє відео тут!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "??????? ?????",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "????????, ?? ????? ??????? ? ????? ??????????? ?? ???????. ?????????? ????? ?????? ????? ?? ??????? ? ??????? ????????? ???? ???????? ?? ?????? ???????.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "????? ?????",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "???? ??????",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "?????????"
|
||||
}
|
||||
|
||||
@@ -230,9 +230,9 @@
|
||||
"BTN_RESTART_TOUR": "重启教程",
|
||||
"BTN_RESTART_TOUR_TOOLTIP": "重新启动入门教程",
|
||||
"HINT_SELECT_VIDEO": "在这里选择您的视频!",
|
||||
"LABEL_TITLE_PRIVACY": "Shared Titles",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "Choose how tab and media titles are shared with the room. Hidden mode clears titles on the server and disables creating episode lobbies from your client.",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "Full titles",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "Episode only",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "Hidden"
|
||||
"LABEL_TITLE_PRIVACY": "????",
|
||||
"LABEL_TITLE_PRIVACY_TOOLTIP": "??????????????????????????????????????????????????",
|
||||
"OPTION_TITLE_PRIVACY_FULL": "????",
|
||||
"OPTION_TITLE_PRIVACY_EPISODE": "???",
|
||||
"OPTION_TITLE_PRIVACY_HIDDEN": "??"
|
||||
}
|
||||
|
||||
+2
-1
@@ -89,6 +89,7 @@ let pendingConnectionErrorMsg = null;
|
||||
let roomListRefreshTimer = null;
|
||||
let roomListRefreshInterval = null;
|
||||
const ROOM_LIST_REFRESH_COOLDOWN_MS = 11000;
|
||||
const TITLE_PRIVACY_MODES = ['full', 'episode', 'hidden'];
|
||||
const FEATURE_HINTS = [
|
||||
{
|
||||
key: 'audio_processing',
|
||||
@@ -210,7 +211,7 @@ async function init() {
|
||||
elements.username.value = username;
|
||||
if (elements.filterNoise) elements.filterNoise.checked = localData.filterNoise !== false;
|
||||
if (elements.autoSyncNextEpisode) elements.autoSyncNextEpisode.checked = localData.autoSyncNextEpisode !== false;
|
||||
if (elements.titlePrivacyMode) elements.titlePrivacyMode.value = localData.titlePrivacyMode || 'full';
|
||||
if (elements.titlePrivacyMode) elements.titlePrivacyMode.value = TITLE_PRIVACY_MODES.includes(localData.titlePrivacyMode) ? localData.titlePrivacyMode : 'full';
|
||||
if (elements.forceSyncMode) elements.forceSyncMode.value = localData.forceSyncMode || 'jump-to-others';
|
||||
if (elements.browserNotifications) elements.browserNotifications.checked = localData.browserNotifications === true;
|
||||
if (elements.autoCopyInvite) elements.autoCopyInvite.checked = localData.autoCopyInvite !== false;
|
||||
|
||||
@@ -34,6 +34,18 @@ assert.deepEqual(
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
applyTitlePrivacyToPayload({
|
||||
tabTitle: 'Private Jellyfin - S01E04',
|
||||
status: 'heartbeat'
|
||||
}, 'episode'),
|
||||
{
|
||||
tabTitle: 'S01E04',
|
||||
status: 'heartbeat'
|
||||
},
|
||||
'privacy filtering must not add absent title keys'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
applyTitlePrivacyToPayload({
|
||||
tabTitle: 'Private Tab',
|
||||
|
||||
Reference in New Issue
Block a user