2023-08-21 06:46:25 +02:00
import {
2023-08-21 06:55:28 +02:00
eventSource ,
this _chid ,
characters ,
callPopup ,
2023-08-21 18:16:10 +02:00
getRequestHeaders ,
2023-08-21 06:46:25 +02:00
} from "../../../script.js" ;
import { selected _group } from "../../group-chats.js" ;
2023-08-21 17:21:32 +02:00
import { loadFileToDocument } from "../../utils.js" ;
2023-08-28 06:49:20 +02:00
import { loadMovingUIState } from '../../power-user.js' ;
import { dragElement } from '../../RossAscends-mods.js'
2023-08-21 06:46:25 +02:00
2023-08-21 06:55:28 +02:00
const extensionName = "gallery" ;
const extensionFolderPath = ` scripts/extensions/ ${ extensionName } / ` ;
let firstTime = true ;
2023-08-21 06:46:25 +02:00
2023-08-28 06:49:20 +02:00
// TODO:Some user wanted the gallery to be detachable / movable.For hmm, good chatting experience.I think it's easy to make any block movable with Ross's function, you need to check this out.
// TODO:Haven't seen any guidance on how to populate the gallery in-app still.
2023-08-21 06:55:28 +02:00
2023-08-21 06:46:25 +02:00
2023-08-21 06:55:28 +02:00
/ * *
2023-08-30 23:55:17 +02:00
* Retrieves a list of gallery items based on a given URL . This function calls an API endpoint
2023-08-21 06:55:28 +02:00
* to get the filenames and then constructs the item list .
2023-08-30 23:55:17 +02:00
*
2023-08-21 06:55:28 +02:00
* @ param { string } url - The base URL to retrieve the list of images .
* @ returns { Promise < Array > } - Resolves with an array of gallery item objects , rejects on error .
* /
async function getGalleryItems ( url ) {
2023-08-30 23:55:17 +02:00
const response = await fetch ( ` /listimgfiles/ ${ url } ` , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
2023-08-21 06:55:28 +02:00
} ) ;
2023-08-30 23:55:17 +02:00
const data = await response . json ( ) ;
const items = data . map ( ( file ) => ( {
src : ` user/images/ ${ url } / ${ file } ` ,
srct : ` user/images/ ${ url } / ${ file } ` ,
title : "" , // Optional title for each item
} ) ) ;
return items ;
2023-08-21 06:55:28 +02:00
}
2023-08-21 06:46:25 +02:00
2023-08-21 06:55:28 +02:00
/ * *
2023-08-21 18:16:10 +02:00
* Initializes a gallery using the provided items and sets up the drag - and - drop functionality .
2023-08-30 23:55:17 +02:00
* It uses the nanogallery2 library to display the items and also initializes
2023-08-21 18:16:10 +02:00
* event listeners to handle drag - and - drop of files onto the gallery .
2023-08-30 23:55:17 +02:00
*
2023-08-21 18:16:10 +02:00
* @ param { Array < Object > } items - An array of objects representing the items to display in the gallery .
* @ param { string } url - The URL to use when a file is dropped onto the gallery for uploading .
* @ returns { Promise < void > } - Promise representing the completion of the gallery initialization .
* /
async function initGallery ( items , url ) {
2023-08-30 21:33:30 +02:00
$ ( "#dragGallery" ) . nanogallery2 ( {
2023-08-28 06:49:20 +02:00
"items" : items ,
thumbnailWidth : 'auto' ,
2023-08-21 18:16:10 +02:00
thumbnailHeight : 150 ,
2023-08-28 06:49:20 +02:00
paginationVisiblePages : 5 ,
paginationMaxLinesPerPage : 2 ,
galleryMaxRows : 3 ,
2023-08-30 17:12:59 +02:00
galleryPaginationTopButtons : false ,
galleryNavigationOverlayButtons : true ,
2023-08-31 02:08:48 +02:00
galleryTheme : {
navigationBar : { background : 'none' , borderTop : '' , borderBottom : '' , borderRight : '' , borderLeft : '' } ,
navigationBreadcrumb : { background : '#111' , color : '#fff' , colorHover : '#ccc' , borderRadius : '4px' } ,
navigationFilter : { color : '#ddd' , background : '#111' , colorSelected : '#fff' , backgroundSelected : '#111' , borderRadius : '4px' } ,
navigationPagination : { background : '#111' , color : '#fff' , colorHover : '#ccc' , borderRadius : '4px' } ,
thumbnail : { background : '#444' , backgroundImage : 'linear-gradient(315deg, #111 0%, #445 90%)' , borderColor : '#000' , borderRadius : '0px' , labelOpacity : 1 , labelBackground : 'rgba(34, 34, 34, 0)' , titleColor : '#fff' , titleBgColor : 'transparent' , titleShadow : '' , descriptionColor : '#ccc' , descriptionBgColor : 'transparent' , descriptionShadow : '' , stackBackground : '#aaa' } ,
thumbnailIcon : { padding : '5px' , color : '#fff' , shadow : '' } ,
pagination : { background : '#181818' , backgroundSelected : '#666' , color : '#fff' , borderRadius : '2px' , shapeBorder : '3px solid var(--SmartThemeQuoteColor)' , shapeColor : '#444' , shapeSelectedColor : '#aaa' }
} ,
galleryDisplayMode : "pagination" ,
fnThumbnailOpen : viewWithDragbox ,
2023-08-21 18:16:10 +02:00
} ) ;
2023-08-28 06:49:20 +02:00
$ ( document ) . ready ( function ( ) {
$ ( '.nGY2GThumbnailImage' ) . on ( 'click' , function ( ) {
let imageUrl = $ ( this ) . find ( '.nGY2GThumbnailImg' ) . attr ( 'src' ) ;
// Do what you want with the imageUrl, for example:
// Display it in a full-size view or replace the gallery grid content with this image
console . log ( imageUrl ) ;
} ) ;
} ) ;
eventSource . on ( 'resizeUI' , function ( elmntName ) {
console . log ( 'resizeUI saw' , elmntName ) ;
// Your logic here
// If you want to resize the nanogallery2 instance when this event is triggered:
2023-08-30 21:33:30 +02:00
jQuery ( "#dragGallery" ) . nanogallery2 ( 'resize' ) ;
2023-08-28 06:49:20 +02:00
} ) ;
2023-08-30 23:55:17 +02:00
const dropZone = $ ( '#dragGallery' ) ;
2023-08-30 21:33:30 +02:00
//remove any existing handlers
dropZone . off ( 'dragover' ) ;
dropZone . off ( 'dragleave' ) ;
dropZone . off ( 'drop' ) ;
2023-08-30 23:55:17 +02:00
2023-08-28 06:49:20 +02:00
2023-08-21 18:16:10 +02:00
// Initialize dropzone handlers
dropZone . on ( 'dragover' , function ( e ) {
e . stopPropagation ( ) ; // Ensure this event doesn't propagate
e . preventDefault ( ) ;
2023-08-30 23:55:17 +02:00
$ ( this ) . addClass ( 'dragging' ) ; // Add a CSS class to change appearance during drag-over
2023-08-21 18:16:10 +02:00
} ) ;
dropZone . on ( 'dragleave' , function ( e ) {
e . stopPropagation ( ) ; // Ensure this event doesn't propagate
$ ( this ) . removeClass ( 'dragging' ) ;
} ) ;
dropZone . on ( 'drop' , function ( e ) {
e . stopPropagation ( ) ; // Ensure this event doesn't propagate
e . preventDefault ( ) ;
$ ( this ) . removeClass ( 'dragging' ) ;
let file = e . originalEvent . dataTransfer . files [ 0 ] ;
uploadFile ( file , url ) ; // Added url parameter to know where to upload
} ) ;
}
/ * *
* Displays a character gallery using the nanogallery2 library .
2023-08-30 23:55:17 +02:00
*
2023-08-21 18:16:10 +02:00
* This function takes care of :
* - Loading necessary resources for the gallery on the first invocation .
* - Preparing gallery items based on the character or group selection .
* - Handling the drag - and - drop functionality for image upload .
* - Displaying the gallery in a popup .
* - Cleaning up resources when the gallery popup is closed .
2023-08-30 23:55:17 +02:00
*
2023-08-21 18:16:10 +02:00
* @ returns { Promise < void > } - Promise representing the completion of the gallery display process .
2023-08-21 06:55:28 +02:00
* /
2023-08-21 06:46:25 +02:00
async function showCharGallery ( ) {
2023-08-21 06:55:28 +02:00
// Load necessary files if it's the first time calling the function
2023-08-21 06:46:25 +02:00
if ( firstTime ) {
2023-08-21 17:21:32 +02:00
await loadFileToDocument (
2023-08-21 06:55:28 +02:00
` ${ extensionFolderPath } nanogallery2.woff.min.css ` ,
"css"
) ;
2023-08-21 17:21:32 +02:00
await loadFileToDocument (
2023-08-21 06:55:28 +02:00
` ${ extensionFolderPath } jquery.nanogallery2.min.js ` ,
"js"
) ;
firstTime = false ;
2023-08-21 06:46:25 +02:00
}
try {
2023-08-21 06:55:28 +02:00
let url = selected _group || this _chid ;
if ( ! selected _group && this _chid ) {
const char = characters [ this _chid ] ;
url = char . avatar . replace ( ".png" , "" ) ;
2023-08-21 06:46:25 +02:00
}
const items = await getGalleryItems ( url ) ;
2023-08-30 21:33:30 +02:00
// if there already is a gallery, destroy it and place this one in its place
if ( $ ( ` #dragGallery ` ) . length ) {
$ ( ` #dragGallery ` ) . nanogallery2 ( "destroy" ) ;
initGallery ( items , url ) ;
} else {
makeMovable ( ) ;
setTimeout ( async ( ) => {
await initGallery ( items , url ) ;
} , 100 ) ;
2023-08-21 06:46:25 +02:00
}
} catch ( err ) {
2023-08-31 02:08:48 +02:00
console . trace ( ) ;
2023-08-21 06:46:25 +02:00
console . error ( err ) ;
}
}
2023-08-21 18:16:10 +02:00
/ * *
* Uploads a given file to a specified URL .
* Once the file is uploaded , it provides a success message using toastr ,
* destroys the existing gallery , fetches the latest items , and reinitializes the gallery .
2023-08-30 23:55:17 +02:00
*
2023-08-21 18:16:10 +02:00
* @ param { File } file - The file object to be uploaded .
* @ param { string } url - The URL indicating where the file should be uploaded .
* @ returns { Promise < void > } - Promise representing the completion of the file upload and gallery refresh .
* /
async function uploadFile ( file , url ) {
// Convert the file to a base64 string
const reader = new FileReader ( ) ;
reader . onloadend = async function ( ) {
const base64Data = reader . result ;
// Create the payload
const payload = {
image : base64Data
} ;
// Add the ch_name from the provided URL (assuming it's the character name)
payload . ch _name = url ;
try {
const headers = await getRequestHeaders ( ) ;
// Merge headers with content-type for JSON
Object . assign ( headers , {
'Content-Type' : 'application/json'
} ) ;
const response = await fetch ( '/uploadimage' , {
method : 'POST' ,
headers : headers ,
body : JSON . stringify ( payload )
} ) ;
if ( ! response . ok ) {
throw new Error ( ` HTTP error! Status: ${ response . status } ` ) ;
}
const result = await response . json ( ) ;
toastr . success ( 'File uploaded successfully. Saved at: ' + result . path ) ;
// Refresh the gallery
2023-08-30 21:33:30 +02:00
$ ( "#dragGallery" ) . nanogallery2 ( "destroy" ) ; // Destroy old gallery
2023-08-21 18:16:10 +02:00
const newItems = await getGalleryItems ( url ) ; // Fetch the latest items
initGallery ( newItems , url ) ; // Reinitialize the gallery with new items and pass 'url'
} catch ( error ) {
console . error ( "There was an issue uploading the file:" , error ) ;
// Replacing alert with toastr error notification
toastr . error ( 'Failed to upload the file.' ) ;
}
}
reader . readAsDataURL ( file ) ;
}
2023-08-21 06:55:28 +02:00
$ ( document ) . ready ( function ( ) {
// Register an event listener
eventSource . on ( "charManagementDropdown" , ( selectedOptionId ) => {
if ( selectedOptionId === "show_char_gallery" ) {
showCharGallery ( ) ;
}
2023-08-21 06:46:25 +02:00
} ) ;
2023-08-21 06:55:28 +02:00
// Add an option to the dropdown
$ ( "#char-management-dropdown" ) . append (
$ ( "<option>" , {
id : "show_char_gallery" ,
text : "Show Gallery" ,
} )
) ;
} ) ;
2023-08-28 06:49:20 +02:00
2023-08-30 17:12:59 +02:00
/ * *
* Creates a new draggable container based on a template .
* This function takes a template with the ID 'generic_draggable_template' and clones it .
* The cloned element has its attributes set , a new child div appended , and is made visible on the body .
* Additionally , it sets up the element to prevent dragging on its images .
* /
2023-08-30 21:33:30 +02:00
function makeMovable ( id = "gallery" ) {
2023-08-28 06:49:20 +02:00
console . debug ( 'making new container from template' )
const template = $ ( '#generic_draggable_template' ) . html ( ) ;
const newElement = $ ( template ) ;
2023-08-30 21:33:30 +02:00
newElement . attr ( 'forChar' , id ) ;
newElement . attr ( 'id' , ` ${ id } ` ) ;
newElement . find ( '.drag-grabber' ) . attr ( 'id' , ` ${ id } header ` ) ;
2023-08-28 06:49:20 +02:00
//add a div for the gallery
2023-08-30 21:33:30 +02:00
newElement . append ( ` <div id="dragGallery"></div> ` ) ;
2023-08-28 06:49:20 +02:00
// add no-scrollbar class to this element
newElement . addClass ( 'no-scrollbar' ) ;
2023-08-31 02:08:48 +02:00
// get the close button and set its id and data-related-id
const closeButton = newElement . find ( '.dragClose' ) ;
closeButton . attr ( 'id' , ` ${ id } close ` ) ;
closeButton . attr ( 'data-related-id' , ` ${ id } ` ) ;
2023-08-28 06:49:20 +02:00
2023-08-30 21:33:30 +02:00
$ ( ` #dragGallery ` ) . css ( 'display' , 'block' ) ;
2023-08-28 06:49:20 +02:00
$ ( 'body' ) . append ( newElement ) ;
loadMovingUIState ( ) ;
2023-08-30 21:33:30 +02:00
$ ( ` .draggable[forChar=" ${ id } "] ` ) . css ( 'display' , 'block' ) ;
2023-08-28 06:49:20 +02:00
dragElement ( newElement ) ;
2023-08-30 21:33:30 +02:00
$ ( ` .draggable[forChar=" ${ id } "] img ` ) . on ( 'dragstart' , ( e ) => {
2023-08-28 06:49:20 +02:00
console . log ( 'saw drag on avatar!' ) ;
e . preventDefault ( ) ;
return false ;
} ) ;
2023-08-31 02:08:48 +02:00
$ ( 'body' ) . on ( 'click' , '.dragClose' , function ( ) {
const relatedId = $ ( this ) . data ( 'related-id' ) ; // Get the ID of the related draggable
$ ( ` # ${ relatedId } ` ) . remove ( ) ; // Remove the associated draggable
} ) ;
2023-08-28 06:49:20 +02:00
}
2023-08-30 17:12:59 +02:00
/ * *
* Creates a new draggable image based on a template .
2023-08-30 23:55:17 +02:00
*
2023-08-30 17:12:59 +02:00
* This function clones a provided template with the ID 'generic_draggable_template' ,
* appends the given image URL , ensures the element has a unique ID ,
* and attaches the element to the body . After appending , it also prevents
* dragging on the appended image .
2023-08-30 23:55:17 +02:00
*
2023-08-30 17:12:59 +02:00
* @ param { string } id - A base identifier for the new draggable element .
* @ param { string } url - The URL of the image to be added to the draggable element .
* /
2023-08-30 04:44:09 +02:00
function makeDragImg ( id , url ) {
// Step 1: Clone the template content
const template = document . getElementById ( 'generic_draggable_template' ) ;
if ( ! ( template instanceof HTMLTemplateElement ) ) {
console . error ( 'The element is not a <template> tag' ) ;
return ;
}
const newElement = document . importNode ( template . content , true ) ;
// Step 2: Append the given image
const imgElem = document . createElement ( 'img' ) ;
imgElem . src = url ;
2023-08-30 17:12:59 +02:00
let uniqueId = ` draggable_ ${ id } ` ;
2023-08-30 04:44:09 +02:00
const draggableElem = newElement . querySelector ( '.draggable' ) ;
if ( draggableElem ) {
draggableElem . appendChild ( imgElem ) ;
2023-08-30 17:12:59 +02:00
// Find a unique id for the draggable element
let counter = 1 ;
while ( document . getElementById ( uniqueId ) ) {
uniqueId = ` draggable_ ${ id } _ ${ counter } ` ;
counter ++ ;
}
draggableElem . id = uniqueId ;
2023-08-30 04:44:09 +02:00
// Ensure that the newly added element is displayed as block
draggableElem . style . display = 'block' ;
2023-08-31 02:08:48 +02:00
// Add an id to the close button
// If the close button exists, set related-id
const closeButton = draggableElem . querySelector ( '.dragClose' ) ;
if ( closeButton ) {
closeButton . id = ` ${ uniqueId } close ` ;
closeButton . dataset . relatedId = uniqueId ;
}
2023-08-30 17:12:59 +02:00
// Find the .drag-grabber and set its matching unique ID
2023-08-30 04:44:09 +02:00
const dragGrabber = draggableElem . querySelector ( '.drag-grabber' ) ;
if ( dragGrabber ) {
2023-08-30 17:12:59 +02:00
dragGrabber . id = ` ${ uniqueId } header ` ; // appending _header to make it match the parent's unique ID
2023-08-30 04:44:09 +02:00
}
}
// Step 3: Attach it to the body
document . body . appendChild ( newElement ) ;
// Step 4: Call dragElement and loadMovingUIState
2023-08-30 17:12:59 +02:00
const appendedElement = document . getElementById ( uniqueId ) ;
2023-08-30 04:44:09 +02:00
if ( appendedElement ) {
var elmntName = $ ( appendedElement ) ;
loadMovingUIState ( ) ;
2023-08-30 06:11:20 +02:00
dragElement ( elmntName ) ;
2023-08-30 04:44:09 +02:00
// Prevent dragging the image
2023-08-30 17:12:59 +02:00
$ ( ` # ${ uniqueId } img ` ) . on ( 'dragstart' , ( e ) => {
2023-08-30 04:44:09 +02:00
console . log ( 'saw drag on avatar!' ) ;
e . preventDefault ( ) ;
return false ;
} ) ;
} else {
console . error ( "Failed to append the template content or retrieve the appended content." ) ;
}
2023-08-31 02:08:48 +02:00
$ ( 'body' ) . on ( 'click' , '.dragClose' , function ( ) {
const relatedId = $ ( this ) . data ( 'related-id' ) ; // Get the ID of the related draggable
$ ( ` # ${ relatedId } ` ) . remove ( ) ; // Remove the associated draggable
} ) ;
2023-08-30 04:44:09 +02:00
}
2023-08-30 17:12:59 +02:00
/ * *
* Processes a list of items ( containing URLs ) and creates a draggable box for the first item .
2023-08-30 23:55:17 +02:00
*
2023-08-30 17:12:59 +02:00
* If the provided list of items is non - empty , it takes the URL of the first item ,
* derives an ID from the URL , and uses the makeDragImg function to create
* a draggable image element based on that ID and URL .
2023-08-30 23:55:17 +02:00
*
2023-08-30 17:12:59 +02:00
* @ param { Array } items - A list of items where each item has a responsiveURL method that returns a URL .
* /
function viewWithDragbox ( items ) {
2023-08-28 06:49:20 +02:00
if ( items && items . length > 0 ) {
var url = items [ 0 ] . responsiveURL ( ) ; // Get the URL of the clicked image/video
2023-08-30 04:44:09 +02:00
// ID should just be the last part of the URL, removing the extension
var id = url . substring ( url . lastIndexOf ( '/' ) + 1 , url . lastIndexOf ( '.' ) ) ;
makeDragImg ( id , url ) ;
2023-08-28 06:49:20 +02:00
}
}
2023-08-30 04:44:09 +02:00