This commit is contained in:
2026-01-01 15:05:29 -06:00
parent 65044a54ef
commit 716007802e
147 changed files with 40416 additions and 27 deletions

View File

@@ -0,0 +1,215 @@
<template>
<DropDownMenu>
<template #button>
<IconButton>
<MaterialDesignIcon icon-name="dots-vertical" class="size-5" />
</IconButton>
</template>
<template #items>
<!-- call button -->
<DropDownMenuItem @click="onStartCall">
<MaterialDesignIcon icon-name="phone" class="w-4 h-4" />
<span>Start a Call</span>
</DropDownMenuItem>
<!-- ping button -->
<DropDownMenuItem @click="onPingDestination">
<MaterialDesignIcon icon-name="flash" class="size-5" />
<span>Ping Destination</span>
</DropDownMenuItem>
<!-- set custom display name button -->
<DropDownMenuItem @click="onSetCustomDisplayName">
<MaterialDesignIcon icon-name="account-edit" class="size-5" />
<span>Set Custom Display Name</span>
</DropDownMenuItem>
<!-- block/unblock button -->
<div class="border-t">
<DropDownMenuItem v-if="!isBlocked" @click="onBlockDestination">
<MaterialDesignIcon icon-name="block-helper" class="size-5 text-red-500" />
<span class="text-red-500">Block User</span>
</DropDownMenuItem>
<DropDownMenuItem v-else @click="onUnblockDestination">
<MaterialDesignIcon icon-name="check-circle" class="size-5 text-green-500" />
<span class="text-green-500">Unblock User</span>
</DropDownMenuItem>
</div>
<!-- delete message history button -->
<div class="border-t">
<DropDownMenuItem @click="onDeleteMessageHistory">
<MaterialDesignIcon icon-name="delete" class="size-5 text-red-500" />
<span class="text-red-500">Delete Message History</span>
</DropDownMenuItem>
</div>
</template>
</DropDownMenu>
</template>
<script>
import DropDownMenu from "../DropDownMenu.vue";
import DropDownMenuItem from "../DropDownMenuItem.vue";
import IconButton from "../IconButton.vue";
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import DialogUtils from "../../js/DialogUtils";
export default {
name: "ConversationDropDownMenu",
components: {
IconButton,
DropDownMenuItem,
DropDownMenu,
MaterialDesignIcon,
},
props: {
peer: {
type: Object,
required: true,
},
},
emits: ["conversation-deleted", "set-custom-display-name", "block-status-changed"],
data() {
return {
isBlocked: false,
blockedDestinations: [],
};
},
watch: {
peer: {
handler() {
this.checkIfBlocked();
},
immediate: true,
},
},
async mounted() {
await this.loadBlockedDestinations();
},
methods: {
async loadBlockedDestinations() {
try {
const response = await window.axios.get("/api/v1/blocked-destinations");
this.blockedDestinations = response.data.blocked_destinations || [];
this.checkIfBlocked();
} catch (e) {
console.log(e);
}
},
checkIfBlocked() {
if (!this.peer) {
this.isBlocked = false;
return;
}
this.isBlocked = this.blockedDestinations.some((b) => b.destination_hash === this.peer.destination_hash);
},
async onBlockDestination() {
if (
!(await DialogUtils.confirm(
"Are you sure you want to block this user? They will not be able to send you messages or establish links."
))
) {
return;
}
try {
await window.axios.post("/api/v1/blocked-destinations", {
destination_hash: this.peer.destination_hash,
});
await this.loadBlockedDestinations();
DialogUtils.alert("User blocked successfully");
this.$emit("block-status-changed");
} catch (e) {
DialogUtils.alert("Failed to block user");
console.log(e);
}
},
async onUnblockDestination() {
try {
await window.axios.delete(`/api/v1/blocked-destinations/${this.peer.destination_hash}`);
await this.loadBlockedDestinations();
DialogUtils.alert("User unblocked successfully");
this.$emit("block-status-changed");
} catch (e) {
DialogUtils.alert("Failed to unblock user");
console.log(e);
}
},
async onDeleteMessageHistory() {
// ask user to confirm deleting conversation history
if (
!(await DialogUtils.confirm(
"Are you sure you want to delete all messages in this conversation? This can not be undone!"
))
) {
return;
}
// delete all lxmf messages from "us to destination" and from "destination to us"
try {
await window.axios.delete(`/api/v1/lxmf-messages/conversation/${this.peer.destination_hash}`);
} catch (e) {
DialogUtils.alert("failed to delete conversation");
console.log(e);
}
// fire callback
this.$emit("conversation-deleted");
},
async onSetCustomDisplayName() {
this.$emit("set-custom-display-name");
},
async onStartCall() {
try {
await window.axios.get(`/api/v1/telephone/call/${this.peer.destination_hash}`);
} catch (e) {
const message = e.response?.data?.message ?? "Failed to start call";
DialogUtils.alert(message);
}
},
async onPingDestination() {
try {
// ping destination
const response = await window.axios.get(`/api/v1/ping/${this.peer.destination_hash}/lxmf.delivery`, {
params: {
timeout: 30,
},
});
const pingResult = response.data.ping_result;
const rttMilliseconds = (pingResult.rtt * 1000).toFixed(3);
const rttDurationString = `${rttMilliseconds} ms`;
const info = [
`Valid reply from ${this.peer.destination_hash}`,
`Duration: ${rttDurationString}`,
`Hops There: ${pingResult.hops_there}`,
`Hops Back: ${pingResult.hops_back}`,
];
// add signal quality if available
if (pingResult.quality != null) {
info.push(`Signal Quality: ${pingResult.quality}%`);
}
// add rssi if available
if (pingResult.rssi != null) {
info.push(`RSSI: ${pingResult.rssi}dBm`);
}
// add snr if available
if (pingResult.snr != null) {
info.push(`SNR: ${pingResult.snr}dB`);
}
// show result
DialogUtils.alert(info.join("\n"));
} catch (e) {
console.log(e);
const message = e.response?.data?.message ?? "Ping failed. Try again later";
DialogUtils.alert(message);
}
},
},
};
</script>