add a toggle for generate-embeddings flags for admins, and label DID actions
This commit is contained in:
@@ -138,12 +138,14 @@
|
||||
</div>
|
||||
<div class="flex justify-between mt-4">
|
||||
<div class="flex items-center">
|
||||
<div v-if="activeDid" class="flex justify-between">
|
||||
<div>
|
||||
<div v-if="activeDid" class="flex justify-between items-end">
|
||||
<div class="flex items-end gap-1">
|
||||
<div
|
||||
v-if="contactFromDid?.did !== activeDid"
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<button
|
||||
v-if="
|
||||
contactFromDid?.seesMe && contactFromDid.did !== activeDid
|
||||
"
|
||||
v-if="contactFromDid?.seesMe"
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="They can see your activity"
|
||||
@click="confirmSetVisibility(contactFromDid, false)"
|
||||
@@ -152,9 +154,7 @@
|
||||
<font-awesome icon="eye" class="fa-fw" />
|
||||
</button>
|
||||
<button
|
||||
v-else-if="
|
||||
!contactFromDid?.seesMe && contactFromDid?.did !== activeDid
|
||||
"
|
||||
v-else-if="!contactFromDid?.seesMe"
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="They cannot see your activity"
|
||||
@click="confirmSetVisibility(contactFromDid, true)"
|
||||
@@ -162,12 +162,15 @@
|
||||
<font-awesome icon="arrow-up" class="fa-fw" />
|
||||
<font-awesome icon="eye-slash" class="fa-fw" />
|
||||
</button>
|
||||
<span class="text-xs text-slate-600">See You</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="contactFromDid?.did !== activeDid"
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<button
|
||||
v-if="
|
||||
contactFromDid?.iViewContent &&
|
||||
contactFromDid.did !== activeDid
|
||||
"
|
||||
v-if="contactFromDid?.iViewContent"
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="You watch their activity"
|
||||
@click="confirmViewContent(contactFromDid, false)"
|
||||
@@ -176,10 +179,7 @@
|
||||
<font-awesome icon="eye" class="fa-fw" />
|
||||
</button>
|
||||
<button
|
||||
v-else-if="
|
||||
!contactFromDid?.iViewContent &&
|
||||
contactFromDid?.did !== activeDid
|
||||
"
|
||||
v-else-if="!contactFromDid?.iViewContent"
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="You do not watch their activity"
|
||||
@click="confirmViewContent(contactFromDid, true)"
|
||||
@@ -187,20 +187,30 @@
|
||||
<font-awesome icon="arrow-down" class="fa-fw" />
|
||||
<font-awesome icon="eye-slash" class="fa-fw" />
|
||||
</button>
|
||||
<span class="text-xs text-slate-600">Watch Them</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<div
|
||||
v-if="contactFromDid?.did !== activeDid"
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<button
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="Check Visibility"
|
||||
@click="checkVisibility(contactFromDid)"
|
||||
>
|
||||
<font-awesome icon="rotate" class="fa-fw" />
|
||||
</button>
|
||||
<span class="text-xs text-slate-600">Check</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
<div
|
||||
v-if="contactFromDid?.did !== activeDid"
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
class="flex flex-col items-center ml-6"
|
||||
>
|
||||
<button
|
||||
class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="Registration"
|
||||
@click="confirmRegister(contactFromDid)"
|
||||
>
|
||||
@@ -215,15 +225,23 @@
|
||||
class="fa-fw"
|
||||
/>
|
||||
</button>
|
||||
<span class="text-xs text-slate-600">Register</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="contactFromDid?.did !== activeDid"
|
||||
class="flex flex-col items-center ml-6"
|
||||
>
|
||||
<button
|
||||
class="text-sm uppercase bg-gradient-to-b from-rose-500 to-rose-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
class="text-sm uppercase bg-gradient-to-b from-rose-500 to-rose-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
|
||||
title="Delete"
|
||||
@click="confirmDeleteContact(contactFromDid)"
|
||||
>
|
||||
<font-awesome icon="trash-can" class="fa-fw" />
|
||||
</button>
|
||||
<span class="text-xs text-slate-600">Delete</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!contactFromDid?.profileImageUrl">
|
||||
<div>Auto-Generated Icon</div>
|
||||
@@ -263,6 +281,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Only admins can set the generate-embedding flag -->
|
||||
<div
|
||||
v-if="showGeneralAdvanced && viewingDid"
|
||||
class="mt-4 pt-4 border-t border-slate-300"
|
||||
data-testid="generateEmbeddingSection"
|
||||
>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Always generate embedding
|
||||
</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
:aria-checked="generateEmbedding"
|
||||
:disabled="generateEmbeddingSaving || generateEmbeddingLoading"
|
||||
class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
:class="generateEmbedding ? 'bg-blue-600' : 'bg-gray-200'"
|
||||
@click="toggleGenerateEmbedding"
|
||||
>
|
||||
<span
|
||||
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition"
|
||||
:class="generateEmbedding ? 'translate-x-5' : 'translate-x-1'"
|
||||
/>
|
||||
</button>
|
||||
<span class="text-sm text-gray-600">
|
||||
{{ generateEmbedding ? "On" : "Off" }}
|
||||
<span v-if="generateEmbeddingLoading" class="ml-1">(loading…)</span>
|
||||
<span v-else-if="generateEmbeddingSaving" class="ml-1"
|
||||
>(saving…)</span
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Animation -->
|
||||
<div
|
||||
v-if="isLoading"
|
||||
@@ -332,7 +384,10 @@ import { RouteLocationNormalizedLoaded, Router } from "vue-router";
|
||||
import QuickNav from "../components/QuickNav.vue";
|
||||
import InfiniteScroll from "../components/InfiniteScroll.vue";
|
||||
import TopMessage from "../components/TopMessage.vue";
|
||||
import { NotificationIface } from "../constants/app";
|
||||
import {
|
||||
DEFAULT_PARTNER_API_SERVER,
|
||||
NotificationIface,
|
||||
} from "../constants/app";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
import { BoundingBox } from "../db/tables/settings";
|
||||
|
||||
@@ -406,11 +461,16 @@ export default class DIDView extends Vue {
|
||||
contactLabels: string[] = [];
|
||||
|
||||
contactYaml = "";
|
||||
generateEmbedding: boolean | null = null;
|
||||
generateEmbeddingLoading = false;
|
||||
generateEmbeddingSaving = false;
|
||||
hitEnd = false;
|
||||
isLoading = false;
|
||||
isMyDid = false;
|
||||
partnerApiServer = DEFAULT_PARTNER_API_SERVER;
|
||||
searchBox: { name: string; bbox: BoundingBox } | null = null;
|
||||
showDidDetails = false;
|
||||
showGeneralAdvanced = false;
|
||||
showLargeIdenticonId?: string;
|
||||
showLargeIdenticonUrl?: string;
|
||||
viewingDid?: string;
|
||||
@@ -444,6 +504,9 @@ export default class DIDView extends Vue {
|
||||
await this.loadContactInformation();
|
||||
await this.loadClaimsAbout();
|
||||
await this.checkIfOwnDID();
|
||||
if (this.showGeneralAdvanced && this.activeDid) {
|
||||
await this.loadGenerateEmbeddingState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +522,9 @@ export default class DIDView extends Vue {
|
||||
this.activeDid = activeIdentity.activeDid || "";
|
||||
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.showGeneralAdvanced = !!settings.showGeneralAdvanced;
|
||||
this.partnerApiServer =
|
||||
(settings.partnerApiServer as string) || DEFAULT_PARTNER_API_SERVER;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,6 +585,71 @@ export default class DIDView extends Vue {
|
||||
this.isMyDid = allAccountDids.includes(this.viewingDid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads partner profile generateEmbedding state for the viewed DID (when showGeneralAdvanced).
|
||||
*/
|
||||
private async loadGenerateEmbeddingState() {
|
||||
if (!this.viewingDid || !this.activeDid) return;
|
||||
this.generateEmbeddingLoading = true;
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const url = `${this.partnerApiServer}/api/partner/userProfileForIssuer/${encodeURIComponent(this.viewingDid)}`;
|
||||
const response = await this.axios.get(url, { headers });
|
||||
const data = response.data?.data;
|
||||
this.generateEmbedding =
|
||||
data && typeof data.generateEmbedding === "boolean"
|
||||
? data.generateEmbedding
|
||||
: false;
|
||||
} catch {
|
||||
this.generateEmbedding = false;
|
||||
} finally {
|
||||
this.generateEmbeddingLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the "always generate embedding" flag for the viewed DID on the partner API.
|
||||
* Only permissioned (admin) users can change this; API returns 403 otherwise.
|
||||
*/
|
||||
async toggleGenerateEmbedding() {
|
||||
if (
|
||||
!this.viewingDid ||
|
||||
!this.activeDid ||
|
||||
this.generateEmbeddingSaving ||
|
||||
this.generateEmbeddingLoading
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const newValue = !this.generateEmbedding;
|
||||
this.generateEmbeddingSaving = true;
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const url = `${this.partnerApiServer}/api/partner/userProfile/${encodeURIComponent(this.viewingDid)}/generateEmbedding`;
|
||||
await this.axios.put(url, { generateEmbedding: newValue }, { headers });
|
||||
this.generateEmbedding = newValue;
|
||||
this.notify.success(
|
||||
newValue
|
||||
? "Contact tagged to always generate embedding."
|
||||
: "Contact untagged from always generating embedding.",
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
const error = (err as { response?: { data?: { error?: string } } })
|
||||
?.response?.data?.error;
|
||||
if (error) {
|
||||
this.notify.error(error, TIMEOUTS.LONG);
|
||||
} else {
|
||||
logger.error("Failed to update generate-embedding flag:", err);
|
||||
this.notify.error(
|
||||
"Failed to update generate-embedding flag. Try again.",
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
this.generateEmbeddingSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads additional claims when user scrolls to bottom
|
||||
* Used by infinite scroll component to implement pagination
|
||||
|
||||
Reference in New Issue
Block a user