Files
securebit-chat/node_modules/html5-qrcode/src/ui/scanner/file-selection-ui.ts
lockbitchat 34094956b7 feat(core): update session, security system and QR exchange
- Removed session creation and Lightning payment logic
- Refactored security system:
  * no more restrictions
  * all systems enabled on session creation
- Improved QR code exchange for mobile devices
2025-09-23 20:01:02 -04:00

264 lines
8.9 KiB
TypeScript

/**
* @fileoverview
* File for file selection UI handling.
*
* @author mebjas <minhazav@gmail.com>
*
* The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
* http://www.denso-wave.com/qrcode/faqpatent-e.html
*/
import {Html5QrcodeScannerStrings} from "../../strings";
import {
BaseUiElementFactory,
PublicUiElementIdAndClasses
} from "./base";
/**
* Interface for callback when a file is selected by user using the button.
*/
export type OnFileSelected = (file: File) => void;
/** UI class for file selection handling. */
export class FileSelectionUi {
private readonly fileBasedScanRegion: HTMLDivElement;
private readonly fileScanInput: HTMLInputElement;
private readonly fileSelectionButton: HTMLButtonElement;
/** Creates object and renders. */
private constructor(
parentElement: HTMLDivElement,
showOnRender: boolean,
onFileSelected: OnFileSelected) {
this.fileBasedScanRegion = this.createFileBasedScanRegion();
this.fileBasedScanRegion.style.display
= showOnRender ? "block" : "none";
parentElement.appendChild(this.fileBasedScanRegion);
let fileScanLabel = document.createElement("label");
fileScanLabel.setAttribute("for", this.getFileScanInputId());
fileScanLabel.style.display = "inline-block";
this.fileBasedScanRegion.appendChild(fileScanLabel);
this.fileSelectionButton
= BaseUiElementFactory.createElement<HTMLButtonElement>(
"button",
PublicUiElementIdAndClasses.FILE_SELECTION_BUTTON_ID);
this.setInitialValueToButton();
// Bind click events with the label element.
this.fileSelectionButton.addEventListener("click", (_) => {
fileScanLabel.click();
});
fileScanLabel.append(this.fileSelectionButton);
this.fileScanInput
= BaseUiElementFactory.createElement<HTMLInputElement>(
"input", this.getFileScanInputId());
this.fileScanInput.type = "file";
this.fileScanInput.accept = "image/*";
this.fileScanInput.style.display = "none";
fileScanLabel.appendChild(this.fileScanInput);
let $this = this;
/*eslint complexity: ["error", 5]*/
this.fileScanInput.addEventListener("change", (e: Event) => {
if (e == null || e.target == null) {
return;
}
let target: HTMLInputElement = e.target as HTMLInputElement;
if (target.files && target.files.length === 0) {
return;
}
let fileList: FileList = target.files!;
const file: File = fileList[0];
let fileName = file.name;
$this.setImageNameToButton(fileName);
onFileSelected(file);
});
// Render drag and drop label
let dragAndDropMessage = this.createDragAndDropMessage();
this.fileBasedScanRegion.appendChild(dragAndDropMessage);
this.fileBasedScanRegion.addEventListener("dragenter", function(event) {
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionActiveBorder();
event.stopPropagation();
event.preventDefault();
});
this.fileBasedScanRegion.addEventListener("dragleave", function(event) {
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionDefaultBorder();
event.stopPropagation();
event.preventDefault();
});
this.fileBasedScanRegion.addEventListener("dragover", function(event) {
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionActiveBorder();
event.stopPropagation();
event.preventDefault();
});
/*eslint complexity: ["error", 10]*/
this.fileBasedScanRegion.addEventListener("drop", function(event) {
event.stopPropagation();
event.preventDefault();
$this.fileBasedScanRegion.style.border
= $this.fileBasedScanRegionDefaultBorder();
var dataTransfer = event.dataTransfer;
if (dataTransfer) {
let files = dataTransfer.files;
if (!files || files.length === 0) {
return;
}
let isAnyFileImage = false;
for (let i = 0; i < files.length; ++i) {
let file = files.item(i);
if (!file) {
continue;
}
let imageType = /image.*/;
// Only process images.
if (!file.type.match(imageType)) {
continue;
}
isAnyFileImage = true;
let fileName = file.name;
$this.setImageNameToButton(fileName);
onFileSelected(file);
dragAndDropMessage.innerText
= Html5QrcodeScannerStrings.dragAndDropMessage();
break;
}
// None of the files were images.
if (!isAnyFileImage) {
dragAndDropMessage.innerText
= Html5QrcodeScannerStrings
.dragAndDropMessageOnlyImages();
}
}
});
}
//#region Public APIs.
/** Hide the file selection UI. */
public hide() {
this.fileBasedScanRegion.style.display = "none";
this.fileScanInput.disabled = true;
}
/** Show the file selection UI. */
public show() {
this.fileBasedScanRegion.style.display = "block";
this.fileScanInput.disabled = false;
}
/** Returns {@code true} if UI container is displayed. */
public isShowing(): boolean {
return this.fileBasedScanRegion.style.display === "block";
}
/** Reset the file selection value */
public resetValue() {
this.fileScanInput.value = "";
this.setInitialValueToButton();
}
//#endregion
//#region private APIs
private createFileBasedScanRegion(): HTMLDivElement {
let fileBasedScanRegion = document.createElement("div");
fileBasedScanRegion.style.textAlign = "center";
fileBasedScanRegion.style.margin = "auto";
fileBasedScanRegion.style.width = "80%";
fileBasedScanRegion.style.maxWidth = "600px";
fileBasedScanRegion.style.border
= this.fileBasedScanRegionDefaultBorder();
fileBasedScanRegion.style.padding = "10px";
fileBasedScanRegion.style.marginBottom = "10px";
return fileBasedScanRegion;
}
private fileBasedScanRegionDefaultBorder() {
return "6px dashed #ebebeb";
}
/** Border when a file is being dragged over the file scan region. */
private fileBasedScanRegionActiveBorder() {
return "6px dashed rgb(153 151 151)";
}
private createDragAndDropMessage(): HTMLDivElement {
let dragAndDropMessage = document.createElement("div");
dragAndDropMessage.innerText
= Html5QrcodeScannerStrings.dragAndDropMessage();
dragAndDropMessage.style.fontWeight = "400";
return dragAndDropMessage;
}
private setImageNameToButton(imageFileName: string) {
const MAX_CHARS = 20;
if (imageFileName.length > MAX_CHARS) {
// Strip first 8
// Strip last 8
// Add 4 dots
let start8Chars = imageFileName.substring(0, 8);
let length = imageFileName.length;
let last8Chars = imageFileName.substring(length - 8, length);
imageFileName = `${start8Chars}....${last8Chars}`;
}
let newText = Html5QrcodeScannerStrings.fileSelectionChooseAnother()
+ " - "
+ imageFileName;
this.fileSelectionButton.innerText = newText;
}
private setInitialValueToButton() {
let initialText = Html5QrcodeScannerStrings.fileSelectionChooseImage()
+ " - "
+ Html5QrcodeScannerStrings.fileSelectionNoImageSelected();
this.fileSelectionButton.innerText = initialText;
}
private getFileScanInputId(): string {
return "html5-qrcode-private-filescan-input";
}
//#endregion
/**
* Creates a file selection UI and renders.
*
* @param parentElement parent div element to render the UI to.
* @param showOnRender if {@code true}, the UI will be shown upon render
* else hidden.
* @param onFileSelected callback to be called when file selection changes.
*
* @returns Instance of {@code FileSelectionUi}.
*/
public static create(
parentElement: HTMLDivElement,
showOnRender: boolean,
onFileSelected: OnFileSelected): FileSelectionUi {
let button = new FileSelectionUi(
parentElement, showOnRender, onFileSelected);
return button;
}
}