Files
RomhackPlaza/resources/js/submissions.js

440 lines
14 KiB
JavaScript
Raw Permalink Normal View History

2026-05-20 18:25:15 +02:00
import { FSUploader } from "./SubmissionsClass/FSUploader.js";
import { HashesManager } from "./SubmissionsClass/HashesManager.js";
import { GameSelector } from "./SubmissionsClass/GameSelector.js";
import { MainImageManager } from "./SubmissionsClass/MainImageManager.js";
import { GalleryManager } from "./SubmissionsClass/GalleryManager.js";
import { Credits } from "./SubmissionsClass/Credits.js";
/**
* If there is some server side errors.
* We may need reload some things.
* @type {boolean}
*/
const SERVER_SIDE_ERRORS = document.querySelector('meta[name="submission-has-errors"]')?.content === '1';
/**
* Object map of errors messages
* @type {Object<string,string>}
*/
const ERROR_TABLE = {
isUploading: "A file is uploading. Please wait.",
noFiles: "Please select a file to upload",
uploadError: "One or more files failed to upload.",
notAllFilesDone: "Not all the files have finished uploading yet.",
noModifications: "Please select at least a type of hack.",
2026-06-10 11:04:26 +02:00
noSystems: "Please select at least a system.",
2026-05-20 18:25:15 +02:00
noDescription: "Please provide a description.",
noGame: "Please provide a game or create a new one and fill all the required fields.",
noLanguages: "Please select at least a language.",
noAuthors: "Please provide at least an author or create a new one and fill all the required fields.",
2026-06-23 19:24:38 +02:00
noMainImage: "Please upload a main image.",
2026-05-20 18:25:15 +02:00
noGalleryImages: "Please select at least a gallery image.",
isSubmitting: "The entry is already during submission."
}
/**
* Current section.
* @returns {string}
* @constructor
*/
const SECTION = () => document.querySelector("meta[name='fs-section']")?.content ?? '';
2026-06-16 16:21:43 +02:00
const CSRF = () => document.querySelector("meta[name='csrf-token']")?.content ?? '';
2026-05-20 18:25:15 +02:00
window.FSUploader = FSUploader;
window.HashesManager = HashesManager;
window.GameSelector = GameSelector;
window.MainImageManager = MainImageManager;
window.GalleryManager = GalleryManager;
window.Credits = Credits;
/**
* Verify if at least one checkbox is checked in this element.
* @param {HTMLElement} element
* @returns {boolean}
*/
function verifyCheckboxes( element ){
if( !parent ) return false;
return Array.from(element.querySelectorAll('input[type="checkbox"]')).some(el => el.checked);
}
/**
* Verify if an EasyMDE field is filled.
*
* @param {string} fieldName
* @returns {boolean}
*/
function verifyMDE( fieldName ){
const textarea = document.querySelector('#field_' + fieldName);
if( textarea && textarea.value.trim().length > 0 ) {
return true;
}
const field = window['mde_' + fieldName] || null;
return field && typeof field.value === 'function' && field.value().trim().length > 0;
}
window.SubmissionVerifications = {
/**
* Verify if we are in an upload.
* @param {FSUploader} Uploader
* @returns {boolean}
*/
step1_DuringFSUpload: function( Uploader ){
return !Uploader.isUploading;
},
/**
* Verify if at least one file is uploaded.
* @param {FSUploader} Uploader
* @returns {boolean}
*/
step2_NoFilesFSUpload: function( Uploader ){
return Uploader.numberOfFiles > 0;
},
/**
* Verify if any files haven't error.
* @param {FSUploader} Uploader
* @returns {boolean}
*/
step3_ErrorsFSUpload: function( Uploader ){
return !Uploader.hasErrors;
},
/**
* Check if all files are uploaded.
* @param {FSUploader} Uploader
* @returns {boolean}
*/
step4_AllFilesUploadedFSUpload: function( Uploader ){
return Uploader.allFilesUploaded;
},
/**
* Verify if at least one checkbox of romhacks modifications is checked.
* @returns {boolean}
*/
step5_RomhacksModificationsCheckboxes: function(){
return verifyCheckboxes( document.querySelector( '#modifications-group' ) );
},
2026-06-10 11:04:26 +02:00
step5_UtilitiesSystemsCheckboxes: function(){
return verifyCheckboxes( document.querySelector( '#systems-group' ) );
},
2026-05-20 18:25:15 +02:00
/**
* Verify if the description field has at least one character.
* @returns {boolean}
*/
step6_VerifyDescription: function(){
return verifyMDE('description');
},
/**
* Verify if a game is provided.
* @param element this.$el
* @returns {boolean}
*/
step7_VerifyGame: function( element ){
2026-06-10 11:04:26 +02:00
const GAME_SELECTOR_MODE = document.querySelector('input[name="game_selection_mode"]')?.value ?? "game";
if( GAME_SELECTOR_MODE === 'platform' || GAME_SELECTOR_MODE === 'none' )
return true;
2026-05-20 18:25:15 +02:00
// Check if we have an already existent selected game.
const GAME_ID_INPUT = document.querySelector('input[name="game_id"]');
if( GAME_ID_INPUT ){
if( GAME_ID_INPUT.value !== '' && Number(GAME_ID_INPUT.value) > 0){
return true;
}
}
// Check if we have a new game.
let gameSelector = element.querySelector('[x-data="GameSelector()"]');
gameSelector = gameSelector ? Alpine.$data(gameSelector) : null;
if( gameSelector !== null ){
if( !gameSelector.name || !gameSelector.name.toString().trim().length )
return false;
if( !gameSelector.platformId || gameSelector.platformId === '' || gameSelector.platformId === 0 )
return false;
if( !gameSelector.genreId || gameSelector.genreId === '' || gameSelector.genreId === 0 )
return false;
return true;
}
return false;
},
/**
* Verify if at least one checkbox of languages is checked.
* @returns {boolean}
*/
step8_LanguagesCheckboxes: function(){
return verifyCheckboxes( document.querySelector( '#languages-group' ) );
},
/**
* Verify if at least one (new) author has been filled.
* @return {boolean}
*/
step9_verifyAuthors: function(){
const authorField = document.querySelectorAll('input[name="authors[]"]');
const newAuthorField = document.querySelectorAll('input[name="new-authors[]"]');
return ( authorField.length > 0 || newAuthorField.length > 0 );
},
/**
* Verify if a main image has been uploaded.
* @param element this.$el
* @return {boolean}
*/
step10_verifyMainImage: function( element ){
let MainImageData = element.querySelector('[x-data="MainImageManager()"]');
MainImageData = MainImageData ? Alpine.$data(MainImageData) : null;
if( ! MainImageData ){
return false;
}
return MainImageData.uploaded;
},
/**
* Verify if at least one image is uploaded in the gallery.
* @param element this.$el
* @return {boolean}
*/
step11_verifyGallery: function( element){
let GalleryData = element.querySelector('[x-data="GalleryManager()"]');
GalleryData = GalleryData ? Alpine.$data(GalleryData) : null;
if( ! GalleryData ){
return false;
}
return GalleryData.number > 0 && GalleryData.allUploaded;
}
}
/**
* Handle entire submission process.
*/
window.Submission = function(){
return {
/**
* If the script is during a try of submission process.
* @type {boolean}
*/
duringSubmissionProcess: false,
/**
* Error checked.
* @type {string|null}
*/
errorKey: null,
/**
* Return error message.
* @return {string}
*/
get errorMessage(){
return ERROR_TABLE[this.errorKey] ?? "Unknown error";
},
init(){
},
/**
* Get current FSUploader if initialized.
*
* @returns {FSUploader|null}
* @constructor
*/
get Uploader(){
const el = this.$el.querySelector('[x-data="FSUploader()"]');
return el ? Alpine.$data(el) : null;
},
/**
* Do each form verifications.
* Update also this.errorKey.
*
* @returns {boolean}
*/
verifyForm(){
2026-06-23 19:24:38 +02:00
console.info( "Step 1: During File upload" );
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step1_DuringFSUpload( this.Uploader ) ){
this.errorKey = "isUploading";
return false;
}
2026-06-23 19:24:38 +02:00
console.info( "Step 2: No files uploaded" );
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step2_NoFilesFSUpload( this.Uploader ) ){
this.errorKey = "noFiles";
return false;
}
2026-06-23 19:24:38 +02:00
console.info( 'Step 3: Error in file upload')
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step3_ErrorsFSUpload( this.Uploader ) ){
this.errorKey = "uploadError";
return false;
}
2026-06-23 19:24:38 +02:00
console.info("Step 4: All files uploaded");
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step4_AllFilesUploadedFSUpload( this.Uploader ) ){
this.errorKey = "notAllFilesDone";
return false;
}
2026-06-10 11:04:26 +02:00
if( SECTION() === "romhacks" || SECTION() === "lua-scripts" ){
2026-06-23 19:24:38 +02:00
console.info( "Step 5: Verify modifications")
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step5_RomhacksModificationsCheckboxes()){
this.errorKey = "noModifications";
return false;
}
2026-06-10 11:04:26 +02:00
} else if( SECTION() === "utilities" ){
2026-06-23 19:24:38 +02:00
console.info( "Step 5: Verify systems");
2026-06-10 11:04:26 +02:00
if( !SubmissionVerifications.step5_UtilitiesSystemsCheckboxes()){
this.errorKey = "noSystems";
return false;
}
2026-05-20 18:25:15 +02:00
}
2026-06-23 19:24:38 +02:00
console.info( "Step 6: Verify description");
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step6_VerifyDescription() ){
this.errorKey = "noDescription";
return false;
}
2026-06-23 19:24:38 +02:00
console.info( "Step 7: Verify game");
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step7_VerifyGame( this.$el ) ){
this.errorKey = "noGame";
return false;
}
2026-06-23 19:24:38 +02:00
console.info("Step 8: Verify languages");
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step8_LanguagesCheckboxes()){
this.errorKey = "noLanguages";
return false;
}
2026-06-23 19:24:38 +02:00
console.info( "Step 9: Verify authors" );
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step9_verifyAuthors()){
this.errorKey = "noAuthors";
return false;
}
2026-06-23 19:24:38 +02:00
console.info( "Step 10: Verify Main image" );
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step10_verifyMainImage( this.$el )){
this.errorKey = "noMainImage";
return false;
}
2026-06-23 19:24:38 +02:00
console.info( "Step 11: Verify gallery images" );
2026-05-20 18:25:15 +02:00
if( !SubmissionVerifications.step11_verifyGallery( this.$el )){
this.errorKey = "noGalleryImages";
return false;
}
return true;
},
/**
* Scroll to the specific error field.
*/
scrollToError(){
const refMap = {
noFiles: 'uploadTarget',
isUploading: 'uploadTarget',
notAllFilesDone: 'uploadTarget',
uploadError: 'uploadTarget',
noModifications: 'modificationsGroup',
2026-06-10 11:04:26 +02:00
noSystems: 'systemsGroup',
2026-05-20 18:25:15 +02:00
noDescription: 'descriptionField',
noGame: 'gameSelector',
noLanguages: 'languagesGroup',
noAuthors: 'authorsSelector',
noMainImage: 'main-image-field',
noGalleryImages: 'gallery-field',
isSubmitting: 'submitButton'
};
2026-06-23 19:24:38 +02:00
const targetKey = refMap[this.errorKey];
const target = this.$refs[targetKey]
|| this.$el.querySelector(`[data-target="${targetKey}"]`)
|| this.$el.querySelector(`[x-ref="${targetKey}"]`)
|| this.$el.querySelector('.upload-list')
|| this.$el.querySelector('.form-upload');
2026-05-20 18:25:15 +02:00
if (target) {
target.scrollIntoView({behavior: 'smooth', block: 'center'});
return;
}
},
/**
* If you want to submit the form.
* @param {Event} e
*/
submitForm( e ){
if( this.duringSubmissionProcess )
return; // Don't submit two times.
this.errorKey = null; // Reset.
this.duringSubmissionProcess = true;
2026-06-08 16:25:52 +02:00
const STATE = document.querySelector('select[name="submit-state"]')?.value;
if( STATE === 'draft' ){
e.target.submit();
return;
}
2026-05-20 18:25:15 +02:00
if( !this.verifyForm() ){
this.scrollToError();
this.duringSubmissionProcess = false;
return;
}
e.target.submit();
2026-06-16 16:21:43 +02:00
},
async requestFeatured( entryId ){
const csrf = CSRF();
const response = await fetch(`/api/entry/${entryId}/featured`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf
}
});
const json = await response.json();
const entry_featured_button = document.querySelector('#entry-featured-button');
const entry_featured_body = document.querySelector('#entry-featured-body');
if( json.success ){
entry_featured_body.innerHTML = '<p>Request submitted</p>';
entry_featured_button.style.display = 'none';
} else {
entry_featured_body.innerHTML = '<p>Request failed. Please refresh the page and retry.</p>';
entry_featured_button.style.display = 'none';
}
2026-05-20 18:25:15 +02:00
}
}
}