import { onCleanup, onMount, Show } from "solid-js"; import { bytesToFormatted } from "../utils"; import { invoke } from '@tauri-apps/api/core'; import anime from "animejs"; import { ViewState } from "./Managers/ViewManager"; let SettingsMenu = () => { let sliderBar: HTMLElement; let settingsContainer: HTMLElement; let currentButton = 0; let lastClickedButton = -1; let finalPathConfirm: HTMLElement; let finalPathInput: HTMLElement; let finalPathData: string; let finalPathPreviousData: string; let closeWithKey = ( e: KeyboardEvent ) => { if(e.key === 'Escape'){ window.ViewManager.ChangeState(ViewState.PHOTO_LIST); anime({ targets: '.settings', opacity: 0, translateX: '500px', easing: 'easeInOutQuad', duration: 250, complete: () => { anime.set('.settings', { display: 'none' }); } }) } } onMount(async () => { if(await invoke('get_config_value_string', { key: 'transparent' }) === "true"){ invoke('set_config_value_string', { key: 'transparent', value: 'true' }); anime({ targets: document.body, background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); anime({ targets: '.settings', background: 'rgba(0, 0, 0, 0.5)', easing: 'linear', duration: 100 }); } else{ invoke('set_config_value_string', { key: 'transparent', value: 'false' }); anime({ targets: document.body, background: 'rgba(0, 0, 0, 1)', easing: 'linear', duration: 100 }); anime({ targets: '.settings', background: 'rgba(0, 0, 0, 0)', easing: 'linear', duration: 100 }); } let sliderMouseDown = false; let mouseStartX = 0; let width = window.innerWidth; let buttons = [ 370, 680 ]; let sliderPos = width / 2 - buttons[currentButton]; let sliderScale = width / (buttons[1] - buttons[0]); let render = () => { requestAnimationFrame(render); if(!sliderMouseDown){ sliderPos = sliderPos + (width / 2 - buttons[currentButton] - sliderPos) * 0.25; anime.set(sliderBar, { translateX: sliderPos }); settingsContainer.style.left = (sliderPos - (width / 2 - buttons[0])) * sliderScale + 'px'; } } render(); anime.set(sliderBar, { translateX: sliderPos }); sliderBar.addEventListener('touchstart', ( e: TouchEvent ) => { sliderMouseDown = true; mouseStartX = e.touches[0].clientX; }) window.addEventListener('touchmove', ( e: TouchEvent ) => { if(sliderMouseDown){ anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.touches[0].clientX) }); settingsContainer.style.left = (sliderPos - (mouseStartX - e.touches[0].clientX) - (width / 2 - buttons[0])) * sliderScale + 'px'; } }) window.addEventListener('keyup', closeWithKey); window.addEventListener('touchend', ( e: TouchEvent ) => { if(sliderMouseDown){ sliderPos = sliderPos - (mouseStartX - e.touches[0].clientX); anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.touches[0].clientX) }); sliderMouseDown = false; if(Math.abs(mouseStartX - e.touches[0].clientX) > 50){ let shortestDistance = 0; let selectedButton = -1; buttons.forEach(( pos, indx ) => { let dis = Math.abs(sliderPos - (width / 2 - pos)); if(selectedButton === -1){ shortestDistance = dis; selectedButton = indx; } else if(shortestDistance > dis){ shortestDistance = dis; selectedButton = indx; } }) currentButton = selectedButton; } else if(lastClickedButton != -1){ currentButton = lastClickedButton; lastClickedButton = -1 } } }) sliderBar.addEventListener('mousedown', ( e: MouseEvent ) => { sliderMouseDown = true; mouseStartX = e.clientX; }); window.addEventListener('mousemove', ( e: MouseEvent ) => { if(sliderMouseDown){ anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.clientX) }); settingsContainer.style.left = sliderPos - (mouseStartX - e.clientX) + 'px'; settingsContainer.style.left = (sliderPos - (mouseStartX - e.clientX) - (width / 2 - buttons[0])) * sliderScale + 'px'; } }) window.addEventListener('mouseup', ( e: MouseEvent ) => { if(sliderMouseDown){ sliderPos = sliderPos - (mouseStartX - e.clientX); anime.set(sliderBar, { translateX: sliderPos - (mouseStartX - e.clientX) }); sliderMouseDown = false; if(Math.abs(mouseStartX - e.clientX) > 50){ let shortestDistance = 0; let selectedButton = -1; buttons.forEach(( pos, indx ) => { let dis = Math.abs(sliderPos - (width / 2 - pos)); if(selectedButton === -1){ shortestDistance = dis; selectedButton = indx; } else if(shortestDistance > dis){ shortestDistance = dis; selectedButton = indx; } }) currentButton = selectedButton; } else if(lastClickedButton != -1){ currentButton = lastClickedButton; lastClickedButton = -1 } } }) window.addEventListener('resize', () => { width = window.innerWidth; sliderPos = width / 2 - buttons[currentButton]; sliderScale = width / (buttons[1] - buttons[0]); anime.set(sliderBar, { translateX: sliderPos }); }) sliderBar.addEventListener('wheel', ( e: WheelEvent ) => { if(e.deltaY > 0){ if(buttons[currentButton + 1]) currentButton++; } else{ if(buttons[currentButton - 1]) currentButton--; } }) }) onCleanup(() => { window.removeEventListener('keyup', closeWithKey); }) return (
{ window.PhotoManager.PhotoCount() } Photos ({ bytesToFormatted(window.PhotoManager.PhotoSize(), 0) })
VRChat Photo Path:
invoke('get_user_photos_path').then(( path: any ) => {
el.innerHTML = '';
el.appendChild( finalPathInput = el} onInput={( el ) => {
finalPathConfirm.style.display = 'inline-block';
finalPathData = el.target.innerHTML;
}} contenteditable>{path} as Node);
finalPathPreviousData = path;
})
}>
Loading...
finalPathConfirm = el}>
{
finalPathPreviousData = finalPathData;
finalPathConfirm.style.display = 'none';
await invoke('change_final_path', { newPath: finalPathData });
await invoke('relaunch');
anime({
targets: '.settings',
opacity: 0,
translateX: '500px',
easing: 'easeInOutQuad',
duration: 250,
complete: () => {
anime.set('.settings', { display: 'none' });
}
})
window.location.reload();
}}>
Save
{
finalPathData = finalPathPreviousData;
finalPathInput.innerHTML = finalPathPreviousData;
finalPathConfirm.style.display = 'none';
}}>
Cancel
VRCPM Version: invoke('get_version').then((ver: any) => el.innerHTML = ver)}>Loading...
To change the directory VRChat outputs photos to, you can change the "picture_output_folder" key in the invoke('open_url', { url: 'https://docs.vrchat.com/docs/configuration-file#camera-and-screenshot-settings' })}>config.json file
Alternitavely, you can use VRCX to edit the config file.
VRChat Photo Manager supports photos with extra metadata provided by VRCX.