mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
The upload modal's Hashtags section now shows chips of the user's most-used past
hashtags ("Reuse a tag from your videos:"). Clicking one adds it to the NIP-71 't'
repeater; already-added chips dim + disable; the row is hidden for users with no
past tags.
- js/utils/hashtagSuggestions.js: pure rankHashtagsByFrequency(videos) — count =
number of the user's videos using each tag, normalized (case + '#') and deduped
per video, ranked by count then alphabetically. Reads video.nip71.hashtags, falls
back to raw event 't' tags minus bitvid's fixed t=video marker.
- app.getUserHashtagSuggestions() sources the user's own videos via
nostrService.getActiveVideosByAuthors([pubkey]) and ranks them (top 12), injected
into the modal as getHashtagSuggestions.
- UploadModal renders the chips on open and wires clicks through the existing
Nip71FormManager (addRepeaterEntry + setFieldValue, dedup via collectRepeaterValues).
Tests: tests/hashtag-suggestions.test.mjs (5 — per-video counting, normalization,
marker exclusion, tie-break + limit, empty input). Lint + build green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
99 lines
2.7 KiB
JavaScript
99 lines
2.7 KiB
JavaScript
import { UploadModal } from "./components/UploadModal.js";
|
|
|
|
export function initUploadModal({
|
|
app = null,
|
|
uploadModalOverride = null,
|
|
container = null,
|
|
services = {},
|
|
utilities = {},
|
|
callbacks = {},
|
|
eventTarget = null,
|
|
} = {}) {
|
|
const events = eventTarget instanceof EventTarget ? eventTarget : new EventTarget();
|
|
|
|
const {
|
|
authService = null,
|
|
r2Service = null,
|
|
s3Service = null,
|
|
storageService = null,
|
|
} = services;
|
|
|
|
const {
|
|
removeTrackingScripts: removeTrackingScriptsFn = null,
|
|
setGlobalModalState: setGlobalModalStateFn = null,
|
|
} = utilities;
|
|
|
|
const {
|
|
publishVideoNote = null,
|
|
showError = null,
|
|
showSuccess = null,
|
|
getCurrentPubkey = null,
|
|
safeEncodeNpub = null,
|
|
onSubmit = null,
|
|
} = callbacks;
|
|
|
|
const modal =
|
|
(typeof uploadModalOverride === "function"
|
|
? uploadModalOverride({ app, eventTarget: events })
|
|
: uploadModalOverride) ||
|
|
new UploadModal({
|
|
authService,
|
|
r2Service,
|
|
s3Service,
|
|
storageService,
|
|
publishVideoNote,
|
|
removeTrackingScripts: removeTrackingScriptsFn,
|
|
setGlobalModalState: setGlobalModalStateFn,
|
|
showError,
|
|
showSuccess,
|
|
getCurrentPubkey,
|
|
safeEncodeNpub,
|
|
// Frequency-ranked hashtags from the user's own past uploads → one-tap
|
|
// suggestion chips in the upload modal (TODO #45).
|
|
getHashtagSuggestions: (options) =>
|
|
typeof app?.getUserHashtagSuggestions === "function"
|
|
? app.getUserHashtagSuggestions(options)
|
|
: [],
|
|
eventTarget: events,
|
|
container,
|
|
onRequestStorageSettings: () => {
|
|
if (app && app.profileController && typeof app.profileController.show === "function") {
|
|
app.profileController.show("storage");
|
|
}
|
|
},
|
|
// Open the login modal's unlock-saved-key (passphrase) flow when a persisted
|
|
// nsec session is locked after a reload.
|
|
onRequestUnlock: () => {
|
|
try {
|
|
if (typeof app?.initializeLoginModalController === "function") {
|
|
app.initializeLoginModalController();
|
|
}
|
|
const controller = app?.loginModalController;
|
|
if (controller && typeof controller.openModal === "function") {
|
|
return controller.openModal({}) !== false;
|
|
}
|
|
} catch (error) {
|
|
/* best-effort */
|
|
}
|
|
return false;
|
|
},
|
|
});
|
|
|
|
const boundSubmitHandler =
|
|
typeof onSubmit === "function" ? (event) => onSubmit(event) : null;
|
|
|
|
if (boundSubmitHandler && typeof modal?.addEventListener === "function") {
|
|
modal.addEventListener("upload:submit", boundSubmitHandler);
|
|
}
|
|
|
|
return {
|
|
modal,
|
|
events,
|
|
handlers: {
|
|
submit: boundSubmitHandler,
|
|
},
|
|
};
|
|
}
|
|
|
|
export default initUploadModal;
|