mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-04-26 15:17:52 +00:00
feat(ui): implement warning for stranger links and configurable messages sidebar position
This commit is contained in:
@@ -1783,6 +1783,9 @@ export default {
|
||||
showUnknownContactBanner() {
|
||||
return GlobalState.config?.show_unknown_contact_banner !== false;
|
||||
},
|
||||
warnOnStrangerLinksEnabled() {
|
||||
return GlobalState.config?.warn_on_stranger_links !== false;
|
||||
},
|
||||
filteredContacts() {
|
||||
if (!this.contactsSearch) return this.contacts;
|
||||
const s = this.contactsSearch.toLowerCase();
|
||||
@@ -2162,7 +2165,7 @@ export default {
|
||||
}
|
||||
return base;
|
||||
},
|
||||
handleMessageClick(event) {
|
||||
async handleMessageClick(event) {
|
||||
const nomadnetLink = event.target.closest(".nomadnet-link");
|
||||
if (nomadnetLink) {
|
||||
event.preventDefault();
|
||||
@@ -2177,6 +2180,7 @@ export default {
|
||||
query: { path: path },
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const lxmfLink = event.target.closest(".lxmf-link");
|
||||
@@ -2189,7 +2193,31 @@ export default {
|
||||
params: { destinationHash: address },
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const standardLink = event.target.closest("a[href]");
|
||||
if (!standardLink) {
|
||||
return;
|
||||
}
|
||||
|
||||
const href = String(standardLink.getAttribute("href") || "").trim();
|
||||
if (!/^https?:\/\//i.test(href)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
if (this.isStrangerPeer && this.warnOnStrangerLinksEnabled) {
|
||||
const proceed = await DialogUtils.confirm(
|
||||
this.$t("messages.stranger_link_open_confirm", { url: href })
|
||||
);
|
||||
if (!proceed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
window.open(href, "_blank", "noopener");
|
||||
},
|
||||
async updatePropagationNodeStatus() {
|
||||
try {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<!-- SPDX-License-Identifier: 0BSD AND MIT -->
|
||||
|
||||
<template>
|
||||
<div class="flex flex-1 min-w-0 h-full overflow-hidden">
|
||||
<div class="flex flex-1 min-w-0 h-full overflow-hidden" :class="{ 'flex-row-reverse': messagesSidebarOnRight }">
|
||||
<MessagesSidebar
|
||||
v-if="!isPopoutMode"
|
||||
:class="{ 'hidden sm:flex': destinationHash }"
|
||||
:sidebar-position="messagesSidebarPosition"
|
||||
:collapsed="messagesListSidebarCollapsed"
|
||||
:conversations="conversations"
|
||||
:peers="peers"
|
||||
@@ -202,6 +203,13 @@ export default {
|
||||
isPopoutMode() {
|
||||
return this.popoutRouteType === "conversation";
|
||||
},
|
||||
messagesSidebarPosition() {
|
||||
const p = this.config?.messages_sidebar_position;
|
||||
return p === "right" ? "right" : "left";
|
||||
},
|
||||
messagesSidebarOnRight() {
|
||||
return this.messagesSidebarPosition === "right";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
conversations() {
|
||||
|
||||
@@ -4,17 +4,21 @@
|
||||
<div :class="sidebarRootClass">
|
||||
<div
|
||||
v-if="effectiveCollapsed"
|
||||
class="flex flex-col h-full min-h-0 bg-white dark:bg-zinc-950 border-r border-gray-200 dark:border-zinc-800"
|
||||
:class="[
|
||||
'flex flex-col h-full min-h-0 bg-white dark:bg-zinc-950 border-gray-200 dark:border-zinc-800',
|
||||
edgeBorderClass,
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="hidden sm:flex h-12 shrink-0 items-center justify-end border-b border-gray-200 dark:border-zinc-800 px-2"
|
||||
class="hidden sm:flex h-12 shrink-0 items-center border-b border-gray-200 dark:border-zinc-800 px-2"
|
||||
:class="collapsedHeaderJustifyClass"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1.5 rounded-lg text-gray-500 hover:bg-gray-100 dark:text-zinc-400 dark:hover:bg-zinc-800 transition-colors"
|
||||
@click="$emit('toggle-collapse')"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="chevron-right" class="size-5" />
|
||||
<MaterialDesignIcon :icon-name="collapsedStripChevronIcon" class="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1 py-2 px-1 border-b border-gray-200 dark:border-zinc-800">
|
||||
@@ -73,8 +77,8 @@
|
||||
</div>
|
||||
<template v-else>
|
||||
<!-- tabs (h-12 matches App.vue main sidebar collapse row) -->
|
||||
<div class="bg-white dark:bg-zinc-950 border-b border-r border-gray-200 dark:border-zinc-800">
|
||||
<div class="-mb-px flex h-12 min-w-0 items-stretch">
|
||||
<div :class="['bg-white dark:bg-zinc-950 border-b border-gray-200 dark:border-zinc-800', edgeBorderClass]">
|
||||
<div class="-mb-px flex h-12 min-w-0 items-stretch" :class="{ 'flex-row-reverse': isRightSidebar }">
|
||||
<div class="flex min-w-0 flex-1">
|
||||
<div
|
||||
class="flex w-full cursor-pointer items-center justify-center border-b-2 px-1 text-center text-sm font-semibold uppercase tracking-wide transition"
|
||||
@@ -104,7 +108,7 @@
|
||||
class="hidden sm:flex shrink-0 items-center border-b-2 border-transparent px-1.5 text-gray-500 hover:bg-gray-100 dark:text-zinc-400 dark:hover:bg-zinc-800 transition-colors"
|
||||
@click="$emit('toggle-collapse')"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="chevron-left" class="size-5" />
|
||||
<MaterialDesignIcon :icon-name="expandedTabBarChevronIcon" class="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,7 +116,10 @@
|
||||
<!-- conversations -->
|
||||
<div
|
||||
v-if="tab === 'conversations'"
|
||||
class="flex-1 flex flex-col bg-white dark:bg-zinc-950 border-r border-gray-200 dark:border-zinc-800 overflow-hidden min-h-0"
|
||||
:class="[
|
||||
'flex-1 flex flex-col bg-white dark:bg-zinc-950 border-gray-200 dark:border-zinc-800 overflow-hidden min-h-0',
|
||||
edgeBorderClass,
|
||||
]"
|
||||
>
|
||||
<!-- Folders Section -->
|
||||
<div class="border-b border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950">
|
||||
@@ -394,9 +401,11 @@
|
||||
selectedHashes.has(conversation.destination_hash),
|
||||
pinnedSet.has(conversation.destination_hash),
|
||||
timeAgoTick,
|
||||
isRightSidebar,
|
||||
]"
|
||||
class="flex cursor-pointer p-2 border-l-2 relative group conversation-item"
|
||||
:class="[
|
||||
'flex cursor-pointer p-2 relative group conversation-item',
|
||||
selectionEdgeBorderClass,
|
||||
conversation.destination_hash === selectedDestinationHash
|
||||
? 'bg-gray-100 dark:bg-zinc-700 border-blue-500 dark:border-blue-400'
|
||||
: 'bg-white dark:bg-zinc-950 border-transparent hover:bg-gray-50 dark:hover:bg-zinc-700 hover:border-gray-200 dark:hover:border-zinc-600',
|
||||
@@ -640,7 +649,10 @@
|
||||
<!-- discover -->
|
||||
<div
|
||||
v-if="tab === 'announces'"
|
||||
class="flex-1 flex flex-col bg-white dark:bg-zinc-950 border-r border-gray-200 dark:border-zinc-800 overflow-hidden min-h-0"
|
||||
:class="[
|
||||
'flex-1 flex flex-col bg-white dark:bg-zinc-950 border-gray-200 dark:border-zinc-800 overflow-hidden min-h-0',
|
||||
edgeBorderClass,
|
||||
]"
|
||||
>
|
||||
<!-- search -->
|
||||
<div class="p-1 border-b border-gray-200 dark:border-zinc-800">
|
||||
@@ -667,9 +679,11 @@
|
||||
selectedDestinationHash === peer.destination_hash,
|
||||
GlobalState.config.banished_effect_enabled && isBlocked(peer.destination_hash),
|
||||
timeAgoTick,
|
||||
isRightSidebar,
|
||||
]"
|
||||
class="flex cursor-pointer p-2 border-l-2 relative"
|
||||
:class="[
|
||||
'flex cursor-pointer p-2 relative',
|
||||
selectionEdgeBorderClass,
|
||||
peer.destination_hash === selectedDestinationHash
|
||||
? 'bg-gray-100 dark:bg-zinc-700 border-blue-500 dark:border-blue-400'
|
||||
: 'bg-white dark:bg-zinc-950 border-transparent hover:bg-gray-50 dark:hover:bg-zinc-700 hover:border-gray-200 dark:hover:border-zinc-600',
|
||||
@@ -862,6 +876,11 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
sidebarPosition: {
|
||||
type: String,
|
||||
default: "left",
|
||||
validator: (v) => v === "left" || v === "right",
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"conversation-click",
|
||||
@@ -925,6 +944,24 @@ export default {
|
||||
collapsedSidebarConversations() {
|
||||
return this.displayedConversations.slice(0, 5);
|
||||
},
|
||||
isRightSidebar() {
|
||||
return this.sidebarPosition === "right";
|
||||
},
|
||||
edgeBorderClass() {
|
||||
return this.isRightSidebar ? "border-l" : "border-r";
|
||||
},
|
||||
selectionEdgeBorderClass() {
|
||||
return this.isRightSidebar ? "border-r-2" : "border-l-2";
|
||||
},
|
||||
collapsedHeaderJustifyClass() {
|
||||
return this.isRightSidebar ? "justify-start" : "justify-end";
|
||||
},
|
||||
collapsedStripChevronIcon() {
|
||||
return this.isRightSidebar ? "chevron-left" : "chevron-right";
|
||||
},
|
||||
expandedTabBarChevronIcon() {
|
||||
return this.isRightSidebar ? "chevron-right" : "chevron-left";
|
||||
},
|
||||
collapsedConversationIconStyle() {
|
||||
return { width: "36px", height: "36px" };
|
||||
},
|
||||
|
||||
@@ -195,6 +195,19 @@
|
||||
}}</span>
|
||||
</span>
|
||||
</label>
|
||||
<label class="setting-toggle">
|
||||
<Toggle
|
||||
id="warn-on-stranger-links"
|
||||
v-model="config.warn_on_stranger_links"
|
||||
@update:model-value="onWarnOnStrangerLinksChange"
|
||||
/>
|
||||
<span class="setting-toggle__label">
|
||||
<span class="setting-toggle__title">{{ $t("app.warn_on_stranger_links") }}</span>
|
||||
<span class="setting-toggle__description">{{
|
||||
$t("app.warn_on_stranger_links_description")
|
||||
}}</span>
|
||||
</span>
|
||||
</label>
|
||||
</SettingsSectionBlock>
|
||||
|
||||
<section
|
||||
@@ -832,6 +845,20 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("app.messages_sidebar_position") }}
|
||||
</div>
|
||||
<select
|
||||
v-model="config.messages_sidebar_position"
|
||||
class="input-field"
|
||||
@change="onMessagesSidebarPositionChange"
|
||||
>
|
||||
<option value="left">{{ $t("app.messages_sidebar_position_left") }}</option>
|
||||
<option value="right">{{ $t("app.messages_sidebar_position_right") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
@@ -2208,6 +2235,7 @@ export default {
|
||||
block_attachments_from_strangers: true,
|
||||
block_all_from_strangers: false,
|
||||
show_unknown_contact_banner: true,
|
||||
warn_on_stranger_links: true,
|
||||
banished_effect_enabled: true,
|
||||
banished_text: "BANISHED",
|
||||
banished_color: "#dc2626",
|
||||
@@ -2221,6 +2249,7 @@ export default {
|
||||
announce_search_max_fetch: 2000,
|
||||
discovered_interfaces_max_return: 500,
|
||||
message_font_size: 14,
|
||||
messages_sidebar_position: "left",
|
||||
message_icon_size: 28,
|
||||
ui_transparency: 0,
|
||||
ui_glass_enabled: true,
|
||||
@@ -2359,6 +2388,9 @@ export default {
|
||||
"app.theme",
|
||||
"app.light_theme",
|
||||
"app.dark_theme",
|
||||
"app.messages_sidebar_position",
|
||||
"app.messages_sidebar_position_left",
|
||||
"app.messages_sidebar_position_right",
|
||||
"app.ui_transparency",
|
||||
"app.ui_glass_enabled",
|
||||
"app.reset_appearance_defaults",
|
||||
@@ -2708,6 +2740,16 @@ export default {
|
||||
"theme"
|
||||
);
|
||||
},
|
||||
async onMessagesSidebarPositionChange() {
|
||||
const v = this.config.messages_sidebar_position === "right" ? "right" : "left";
|
||||
this.config.messages_sidebar_position = v;
|
||||
await this.updateConfig(
|
||||
{
|
||||
messages_sidebar_position: v,
|
||||
},
|
||||
"messages_sidebar_position"
|
||||
);
|
||||
},
|
||||
async onMessageFontSizeChange() {
|
||||
if (this.saveTimeouts.message_font_size) clearTimeout(this.saveTimeouts.message_font_size);
|
||||
this.saveTimeouts.message_font_size = setTimeout(async () => {
|
||||
@@ -2765,6 +2807,7 @@ export default {
|
||||
},
|
||||
async resetAppearanceDefaults() {
|
||||
this.config.theme = "light";
|
||||
this.config.messages_sidebar_position = "left";
|
||||
this.config.message_font_size = 14;
|
||||
this.config.message_icon_size = 28;
|
||||
this.config.ui_transparency = 0;
|
||||
@@ -2776,6 +2819,7 @@ export default {
|
||||
await this.updateConfig(
|
||||
{
|
||||
theme: "light",
|
||||
messages_sidebar_position: "left",
|
||||
message_font_size: 14,
|
||||
message_icon_size: 28,
|
||||
ui_transparency: 0,
|
||||
@@ -3064,6 +3108,10 @@ export default {
|
||||
this.config.show_unknown_contact_banner = value;
|
||||
await this.updateConfig({ show_unknown_contact_banner: value }, "stranger_protection");
|
||||
},
|
||||
async onWarnOnStrangerLinksChange(value) {
|
||||
this.config.warn_on_stranger_links = value;
|
||||
await this.updateConfig({ warn_on_stranger_links: value }, "stranger_protection");
|
||||
},
|
||||
async onBanishedEffectEnabledChange(value) {
|
||||
this.config.banished_effect_enabled = value;
|
||||
await this.updateConfig(
|
||||
|
||||
@@ -70,9 +70,7 @@
|
||||
:key="bot.id"
|
||||
:class="[
|
||||
'relative rounded-lg border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-3 sm:p-4',
|
||||
editingBotId === bot.id
|
||||
? 'pr-28 sm:pr-40'
|
||||
: 'pr-10 sm:pr-12',
|
||||
editingBotId === bot.id ? 'pr-28 sm:pr-40' : 'pr-10 sm:pr-12',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
@@ -151,7 +149,11 @@
|
||||
<div class="min-w-0 flex-1 space-y-1.5 sm:pr-2">
|
||||
<div
|
||||
class="flex items-center gap-1 min-w-0"
|
||||
:class="editingBotId === bot.id ? 'max-w-[min(100%,14rem)] sm:max-w-[16rem]' : ''"
|
||||
:class="
|
||||
editingBotId === bot.id
|
||||
? 'max-w-[min(100%,14rem)] sm:max-w-[16rem]'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<template v-if="editingBotId === bot.id">
|
||||
<input
|
||||
@@ -193,7 +195,9 @@
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-[11px] text-gray-600 dark:text-gray-300">
|
||||
<div
|
||||
class="flex items-center gap-2 text-[11px] text-gray-600 dark:text-gray-300"
|
||||
>
|
||||
<span
|
||||
class="inline-block size-2 rounded-full shrink-0"
|
||||
:class="bot.running ? 'bg-emerald-500' : 'bg-gray-400 dark:bg-gray-500'"
|
||||
|
||||
Reference in New Issue
Block a user