REVIEW : Migration Menu and Report Menu in Svelte (#1363)

* WIP: svelte menu

* temp

* temp

* New menu svelte

* Migration of report menu in svelte

* Migration of registerCustomMenu for Menu in Svelte
Refactor subMenuStore
Suppression of old MenuScene and ReportMenu

* Suppression of HTML files that aren't use anymore

* fix deeployer

* First pass on css

* First pass on css

* Second pass on css and reportMenu

* Second pass on css and reportMenu

* Second pass on css and reportMenu

* Third pass on css and reportMenu

* Correction following test

* Contact page only if environment variable exist

* Update service worker

Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com>

* Change requested

* Change requested

Co-authored-by: kharhamel <oognic@gmail.com>
Co-authored-by: Gregoire Parant <g.parant@thecodingmachine.com>
This commit is contained in:
GRL78 2021-08-26 12:01:07 +02:00 committed by GitHub
parent 29c1ea25c7
commit 7c956d1481
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1131 additions and 1257 deletions

View file

@ -0,0 +1,147 @@
<script lang="ts">
import { gameManager } from "../../Phaser/Game/GameManager";
import {onMount} from "svelte";
let gameScene = gameManager.getCurrentGameScene();
let HTMLShareLink: HTMLInputElement;
let expandedMapCopyright = false;
let expandedTilesetCopyright = false;
let mapName: string = "";
let mapDescription: string = "";
let mapCopyright: string = "The map creator did not declare a copyright for the map.";
let tilesetCopyright: string[] = [];
onMount(() => {
if (gameScene.mapFile.properties !== undefined) {
const propertyName = gameScene.mapFile.properties.find((property) => property.name === 'mapName')
if ( propertyName !== undefined && typeof propertyName.value === 'string') {
mapName = propertyName.value;
}
const propertyDescription = gameScene.mapFile.properties.find((property) => property.name === 'mapDescription')
if (propertyDescription !== undefined && typeof propertyDescription.value === 'string') {
mapDescription = propertyDescription.value;
}
const propertyCopyright = gameScene.mapFile.properties.find((property) => property.name === 'mapCopyright')
if (propertyCopyright !== undefined && typeof propertyCopyright.value === 'string') {
mapCopyright = propertyCopyright.value;
}
}
for (const tileset of gameScene.mapFile.tilesets) {
if (tileset.properties !== undefined) {
const propertyTilesetCopyright = tileset.properties.find((property) => property.name === 'tilesetCopyright')
if (propertyTilesetCopyright !== undefined && typeof propertyTilesetCopyright.value === 'string') {
tilesetCopyright = [...tilesetCopyright, propertyTilesetCopyright.value]; //Assignment needed to trigger Svelte's reactivity
}
}
}
})
function copyLink() {
HTMLShareLink.select();
document.execCommand('copy');
}
async function shareLink() {
const shareData = {url: location.toString()};
try {
await navigator.share(shareData);
} catch (err) {
console.error('Error: ' + err);
}
}
</script>
<div class="about-room-main">
<section class="share-url not-mobile">
<h3>Share the link of the room !</h3>
<input type="text" readonly bind:this={HTMLShareLink} value={location.toString()}>
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
</section>
<section class="is-mobile">
<h3>Share the link of the room !</h3>
<button type="button" class="nes-btn is-primary" on:click={shareLink}>Share</button>
</section>
<h2>Information on the map</h2>
<section class="container-overflow">
<h3>{mapName}</h3>
<p class="string-HTML">{mapDescription}</p>
<h3 class="nes-pointer hoverable" on:click={() => expandedMapCopyright = !expandedMapCopyright}>Copyrights of the map</h3>
<p class="string-HTML" hidden="{!expandedMapCopyright}">{mapCopyright}</p>
<h3 class="nes-pointer hoverable" on:click={() => expandedTilesetCopyright = !expandedTilesetCopyright}>Copyrights of the tilesets</h3>
<section hidden="{!expandedTilesetCopyright}">
{#each tilesetCopyright as copyright}
<p class="string-HTML">{copyright}</p>
{:else}
<p>The map creator did not declare a copyright for the tilesets. Warning, This doesn't mean that those tilesets have no license.</p>
{/each}
</section>
</section>
</div>
<style lang="scss">
.string-HTML{
white-space: pre-line;
}
div.about-room-main {
height: calc(100% - 56px);
section.share-url {
text-align: center;
margin-bottom: 20px;
input {
width: 85%;
border-radius: 32px;
padding: 3px;
}
input::selection {
background-color: #209cee;
}
}
section.is-mobile {
display: none;
}
h2, h3 {
width: 100%;
text-align: center;
}
h3.hoverable:hover {
background-color: #3c3e40;
border-radius: 32px;
}
section.container-overflow {
height: calc(100% - 220px);
margin: 0;
padding: 0;
overflow-y: auto;
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
div.about-room-main {
section.share-url.not-mobile {
display: none;
}
section.is-mobile {
display: block;
text-align: center;
margin-bottom: 20px;
}
section.container-overflow {
height: calc(100% - 120px);
}
}
}
</style>

View file

@ -0,0 +1,112 @@
<script lang="ts">
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { gameManager } from "../../Phaser/Game/GameManager";
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
import uploadFile from "../images/music-file.svg";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
interface EventTargetFiles extends EventTarget {
files: Array<File>;
}
let gameScene = gameManager.getCurrentGameScene();
let fileInput: HTMLInputElement;
let fileName: string;
let fileSize: string;
let errorFile: boolean;
const AUDIO_TYPE = AdminMessageEventTypes.audio;
export const handleSending = {
async sendAudioMessage(broadcast: boolean) {
if (gameScene == undefined) {
return;
}
const inputAudio = HtmlUtils.getElementByIdOrFail<HTMLInputElement>("input-send-audio");
const selectedFile = inputAudio.files ? inputAudio.files[0] : null;
if (!selectedFile) {
errorFile = true;
throw 'no file selected';
}
const fd = new FormData();
fd.append('file', selectedFile);
const res = await gameScene.connection?.uploadAudio(fd);
const audioGlobalMessage: PlayGlobalMessageInterface = {
content: (res as { path: string }).path,
type: AUDIO_TYPE,
broadcastToWorld: broadcast
}
inputAudio.value = '';
gameScene.connection?.emitGlobalMessage(audioGlobalMessage);
}
}
function inputAudioFile(event: Event) {
const eventTarget : EventTargetFiles = (event.target as EventTargetFiles);
if(!eventTarget || !eventTarget.files || eventTarget.files.length === 0){
return;
}
const file = eventTarget.files[0];
if(!file) {
return;
}
fileName = file.name;
fileSize = getFileSize(file.size);
errorFile = false;
}
function getFileSize(number: number) {
if (number < 1024) {
return number + 'bytes';
} else if (number >= 1024 && number < 1048576) {
return (number / 1024).toFixed(1) + 'KB';
} else if (number >= 1048576) {
return (number / 1048576).toFixed(1) + 'MB';
} else {
return '';
}
}
</script>
<section class="section-input-send-audio">
<img class="nes-pointer" src="{uploadFile}" alt="Upload a file" on:click|preventDefault={ () => {fileInput.click();}}>
{#if fileName !== undefined}
<p>{fileName} : {fileSize}</p>
{/if}
{#if errorFile}
<p class="err">No file selected. You need to upload a file before sending it.</p>
{/if}
<input type="file" id="input-send-audio" bind:this={fileInput} on:change={(e) => {inputAudioFile(e)}}>
</section>
<style lang="scss">
section.section-input-send-audio {
display: flex;
flex-direction: column;
height: 100%;
text-align: center;
img {
flex: 1 1 auto;
max-height: 80%;
margin-bottom: 20px;
}
p {
margin-bottom: 5px;
color: whitesmoke;
font-size: 1rem;
&.err {
color: #ce372b;
}
}
input {
display: none;
}
}
</style>

View file

@ -0,0 +1,15 @@
<script lang="ts">
import {CONTACT_URL} from "../../Enum/EnvironmentVariable";
</script>
<iframe title="contact" src="{CONTACT_URL}"></iframe>
<style lang="scss">
iframe {
border: none;
height: calc(100% - 56px);
width: 100%;
margin: 0;
}
</style>

View file

@ -0,0 +1,51 @@
<script lang="ts">
function goToGettingStarted() {
const sparkHost = "https://workadventu.re/getting-started";
window.open(sparkHost, "_blank");
}
function goToBuildingMap() {
const sparkHost = "https://workadventu.re/map-building";
window.open(sparkHost, "_blank");
}
</script>
<div class="create-map-main">
<section class="container-overflow">
<section>
<h3>Getting started</h3>
<p>
WorkAdventure allows you to create an online space to communicate spontaneously with others.
And it all starts with creating your own space. Choose from a large selection of prefabricated maps by our team.
</p>
<button type="button" class="nes-btn is-primary" on:click={goToGettingStarted}>Getting started</button>
</section>
<section>
<h3>Create your map</h3>
<p>You can also create your own custom map by following the step of the documentation.</p>
<button type="button" class="nes-btn" on:click={goToBuildingMap}>Create your map</button>
</section>
</section>
</div>
<style lang="scss">
div.create-map-main {
height: calc(100% - 56px);
text-align: center;
section {
margin-bottom: 50px;
}
section.container-overflow {
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
}
</style>

View file

@ -0,0 +1,118 @@
<script lang="ts">
import TextGlobalMessage from './TextGlobalMessage.svelte';
import AudioGlobalMessage from './AudioGlobalMessage.svelte';
let handleSendText: { sendTextMessage(broadcast: boolean): void };
let handleSendAudio: { sendAudioMessage(broadcast: boolean): Promise<void> };
let inputSendTextActive = true;
let uploadAudioActive = !inputSendTextActive;
let broadcastToWorld = false;
function activateInputText() {
inputSendTextActive = true;
uploadAudioActive = false;
}
function activateUploadAudio() {
inputSendTextActive = false;
uploadAudioActive = true;
}
function send() {
if (inputSendTextActive) {
handleSendText.sendTextMessage(broadcastToWorld);
}
if (uploadAudioActive) {
handleSendAudio.sendAudioMessage(broadcastToWorld);
}
}
</script>
<div class="global-message-main">
<div class="global-message-subOptions">
<section>
<button type="button" class="nes-btn {inputSendTextActive ? 'is-disabled' : ''}" on:click|preventDefault={activateInputText}>Text</button>
</section>
<section>
<button type="button" class="nes-btn {uploadAudioActive ? 'is-disabled' : ''}" on:click|preventDefault={activateUploadAudio}>Audio</button>
</section>
</div>
<div class="global-message-content">
{#if inputSendTextActive}
<TextGlobalMessage bind:handleSending={handleSendText}/>
{/if}
{#if uploadAudioActive}
<AudioGlobalMessage bind:handleSending={handleSendAudio}/>
{/if}
</div>
<div class="global-message-footer">
<label>
<input type="checkbox" class="nes-checkbox is-dark nes-pointer" bind:checked={broadcastToWorld}>
<span>Broadcast to all rooms of the world</span>
</label>
<section>
<button class="nes-btn is-primary" on:click|preventDefault={send}>Send</button>
</section>
</div>
</div>
<style lang="scss">
div.global-message-main {
height: calc(100% - 50px);
display: grid;
grid-template-rows: 15% 65% 20%;
div.global-message-subOptions {
display: grid;
grid-template-columns: 50% 50%;
margin-bottom: 20px;
section {
display: flex;
justify-content: center;
align-items: center;
}
}
div.global-message-footer {
margin-bottom: 10px;
display: grid;
grid-template-rows: 50% 50%;
section {
display: flex;
justify-content: center;
align-items: center;
}
label {
margin: 10px;
display: flex;
justify-content: center;
align-items: center;
span {
font-family: "Press Start 2P";
}
}
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
.global-message-content {
height: calc(100% - 5px);
}
.global-message-footer {
margin-bottom: 0;
label {
width: calc(100% - 10px);
}
}
}
</style>

View file

@ -0,0 +1,132 @@
<script lang="typescript">
import {fly} from "svelte/transition";
import SettingsSubMenu from "./SettingsSubMenu.svelte";
import ProfileSubMenu from "./ProfileSubMenu.svelte";
import CreateMapSubMenu from "./CreateMapSubMenu.svelte";
import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte";
import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte";
import ContactSubMenu from "./ContactSubMenu.svelte";
import {menuVisiblilityStore, SubMenusInterface, subMenusStore} from "../../Stores/MenuStore";
import {onMount} from "svelte";
import {get} from "svelte/store";
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
let activeSubMenu: string = SubMenusInterface.settings;
let activeComponent: typeof SettingsSubMenu = SettingsSubMenu;
onMount(() => {
switchMenu(SubMenusInterface.settings);
})
function switchMenu(menu: string) {
if (get(subMenusStore).find((subMenu) => subMenu === menu)) {
activeSubMenu = menu;
switch (menu) {
case SubMenusInterface.settings:
activeComponent = SettingsSubMenu;
break;
case SubMenusInterface.profile:
activeComponent = ProfileSubMenu;
break;
case SubMenusInterface.createMap:
activeComponent = CreateMapSubMenu;
break;
case SubMenusInterface.aboutRoom:
activeComponent = AboutRoomSubMenu;
break;
case SubMenusInterface.globalMessages:
activeComponent = GlobalMessageSubMenu;
break;
case SubMenusInterface.contact:
activeComponent = ContactSubMenu;
break;
default:
sendMenuClickedEvent(menu);
menuVisiblilityStore.set(false);
break;
}
} else throw ("There is no menu called " + menu);
}
function closeMenu() {
menuVisiblilityStore.set(false);
}
function onKeyDown(e:KeyboardEvent) {
if (e.key === 'Escape') {
closeMenu();
}
}
</script>
<svelte:window on:keydown={onKeyDown}/>
<div class="menu-container-main">
<div class="menu-nav-sidebar nes-container is-rounded" transition:fly="{{ x: -1000, duration: 500 }}">
<h2>Menu</h2>
<nav>
{#each $subMenusStore as submenu}
<button type="button" class="nes-btn {activeSubMenu === submenu ? 'is-disabled' : ''}" on:click|preventDefault={() => switchMenu(submenu)}>
{submenu}
</button>
{/each}
</nav>
</div>
<div class="menu-submenu-container nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}">
<h2>{activeSubMenu}</h2>
<svelte:component this={activeComponent}/>
</div>
</div>
<style lang="scss">
.nes-container {
padding: 5px;
}
div.menu-container-main {
--size-first-columns-grid: 200px;
font-family: "Press Start 2P";
pointer-events: auto;
height: 80vh;
width: 75vw;
top: 10vh;
position: relative;
margin: auto;
display: grid;
grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid));
grid-template-rows: 100%;
h2 {
text-align: center;
margin-bottom: 20px;
}
div.menu-nav-sidebar {
background-color: #333333;
color: whitesmoke;
nav button {
width: calc(100% - 10px);
margin-bottom: 10px;
}
}
div.menu-submenu-container {
background-color: #333333;
color: whitesmoke;
}
}
@media only screen and (max-width: 800px) {
div.menu-container-main {
--size-first-columns-grid: 120px;
height: 70vh;
top: 55px;
width: 100vw;
font-size: 0.5em;
}
}
</style>

View file

@ -1,33 +1,40 @@
<script lang="typescript">
import logoWA from "../images/logo-WA-min.png"
import {menuVisiblilityStore} from "../../Stores/MenuStore";
import {get} from "svelte/store";
function showMenu(){
menuVisiblilityStore.set(!get(menuVisiblilityStore))
}
function onKeyDown(e: KeyboardEvent) {
if (e.key === "Tab") {
showMenu();
}
}
</script>
<svelte:window on:keydown={onKeyDown}/>
<main class="menuIcon">
<section>
<button>
<img src="/static/images/menu.svg" alt="Open menu">
</button>
</section>
<img src={logoWA} alt="open menu" class="nes-pointer" on:click|preventDefault={showMenu}>
</main>
<style lang="scss">
.menuIcon button {
background-color: black;
color: white;
border-radius: 7px;
padding: 2px 8px;
img {
width: 14px;
padding-top: 0;
/*cursor: url('/resources/logos/cursor_pointer.png'), pointer;*/
}
.menuIcon {
pointer-events: auto;
margin: 25px;
img {
width: 60px;
padding-top: 0;
}
.menuIcon section {
margin: 10px;
}
@media only screen and (max-height: 700px) {
.menuIcon section {
margin: 2px;
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
.menuIcon {
margin: 3px;
img {
width: 50px;
}
}
}
</style>

View file

@ -0,0 +1,78 @@
<script lang="typescript">
import {gameManager} from "../../Phaser/Game/GameManager";
import {SelectCompanionScene, SelectCompanionSceneName} from "../../Phaser/Login/SelectCompanionScene";
import {menuIconVisiblilityStore, menuVisiblilityStore} from "../../Stores/MenuStore";
import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore";
import {LoginScene, LoginSceneName} from "../../Phaser/Login/LoginScene";
import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
import {SelectCharacterScene, SelectCharacterSceneName} from "../../Phaser/Login/SelectCharacterScene";
//import {connectionManager} from "../../Connexion/ConnectionManager";
function disableMenuStores(){
menuVisiblilityStore.set(false);
menuIconVisiblilityStore.set(false);
}
function openEditCompanionScene(){
disableMenuStores();
selectCompanionSceneVisibleStore.set(true);
gameManager.leaveGame(SelectCompanionSceneName,new SelectCompanionScene());
}
function openEditNameScene(){
disableMenuStores();
loginSceneVisibleStore.set(true);
gameManager.leaveGame(LoginSceneName,new LoginScene());
}
function openEditSkinScene(){
disableMenuStores();
selectCharacterSceneVisibleStore.set(true);
gameManager.leaveGame(SelectCharacterSceneName,new SelectCharacterScene());
}
//TODO: Uncomment when login will be completely developed
/*function clickLogin() {
connectionManager.loadOpenIDScreen();
}*/
</script>
<div class="customize-main">
<section>
<button type="button" class="nes-btn" on:click|preventDefault={openEditNameScene}>Edit Name</button>
</section>
<section>
<button type="button" class="nes-btn is-rounded" on:click|preventDefault={openEditSkinScene}>Edit Skin</button>
</section>
<section>
<button type="button" class="nes-btn" on:click|preventDefault={openEditCompanionScene}>Edit Companion</button>
</section>
<!-- <section>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={clickLogin}>Login</button>
</section>-->
</div>
<style lang="scss">
div.customize-main{
section {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20px;
button {
height: 50px;
width: 250px;
}
}
}
@media only screen and (max-width: 800px) {
div.customize-main section button {
width: 130px;
}
}
</style>

View file

@ -0,0 +1,140 @@
<script lang="typescript">
import {localUserStore} from "../../Connexion/LocalUserStore";
import {videoConstraintStore} from "../../Stores/MediaStore";
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
import {isMobile} from "../../Enum/EnvironmentVariable";
let fullscreen : boolean = localUserStore.getFullscreen();
let notification : boolean = localUserStore.getNotification() === 'granted';
let valueGame : number = localUserStore.getGameQualityValue();
let valueVideo : number = localUserStore.getVideoQualityValue();
let previewValueGame = valueGame;
let previewValueVideo = valueVideo;
function saveSetting(){
if (valueGame !== previewValueGame) {
previewValueGame = valueGame;
localUserStore.setGameQualityValue(valueGame);
window.location.reload();
}
if (valueVideo !== previewValueVideo) {
previewValueVideo = valueVideo;
videoConstraintStore.setFrameRate(valueVideo);
}
}
function changeFullscreen() {
const body = HtmlUtils.querySelectorOrFail('body');
if (body) {
if (document.fullscreenElement !== null && !fullscreen) {
document.exitFullscreen()
} else {
body.requestFullscreen();
}
localUserStore.setFullscreen(fullscreen);
}
}
function changeNotification() {
if (Notification.permission === 'granted') {
localUserStore.setNotification(notification ? 'granted' : 'denied');
} else {
Notification.requestPermission().then((response) => {
if (response === 'granted') {
localUserStore.setNotification(notification ? 'granted' : 'denied');
} else {
localUserStore.setNotification('denied');
notification = false;
}
})
}
}
</script>
<div class="settings-main" on:submit|preventDefault={saveSetting}>
<section>
<h3>Game quality</h3>
<div class="nes-select is-dark">
<select bind:value={valueGame}>
<option value="{120}">{isMobile() ? 'High (120 fps)' : 'High video quality (120 fps)'}</option>
<option value="{60}">{isMobile() ? 'Medium (60 fps)' : 'Medium video quality (60 fps, recommended)'}</option>
<option value="{40}">{isMobile() ? 'Minimum (40 fps)' : 'Minimum video quality (40 fps)'}</option>
<option value="{20}">{isMobile() ? 'Small (20 fps)' : 'Small video quality (20 fps)'}</option>
</select>
</div>
</section>
<section>
<h3>Video quality</h3>
<div class="nes-select is-dark">
<select bind:value={valueVideo}>
<option value="{30}">{isMobile() ? 'High (30 fps)' : 'High video quality (30 fps)'}</option>
<option value="{20}">{isMobile() ? 'Medium (20 fps)' : 'Medium video quality (20 fps, recommended)'}</option>
<option value="{10}">{isMobile() ? 'Minimum (10 fps)' : 'Minimum video quality (10 fps)'}</option>
<option value="{5}">{isMobile() ? 'Small (5 fps)' : 'Small video quality (5 fps)'}</option>
</select>
</div>
</section>
<section class="settings-section-save">
<p>(Saving these settings will restart the game)</p>
<button type="button" class="nes-btn is-primary" on:click|preventDefault={saveSetting}>Save</button>
</section>
<section class="settings-section-noSaveOption">
<label>
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={fullscreen} on:change={changeFullscreen}/>
<span>Fullscreen</span>
</label>
<label>
<input type="checkbox" class="nes-checkbox is-dark" bind:checked={notification} on:change={changeNotification}>
<span>Notifications</span>
</label>
</section>
</div>
<style lang="scss">
div.settings-main {
height: calc(100% - 40px);
section {
width: 100%;
padding: 20px 20px 0;
margin-bottom: 20px;
text-align: center;
div.nes-select select:focus {
outline: none;
}
}
section.settings-section-save {
text-align: center;
p {
margin: 16px 0;
}
}
section.settings-section-noSaveOption {
--nb-noSaveOptions: 2; //number of sub-element in the section
display: grid;
grid-template-columns: calc(100% / var(--nb-noSaveOptions)) calc(100% / var(--nb-noSaveOptions)); //Same size for every sub-element
label {
text-align: center;
width: 100%;
margin: 0;
}
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
div.settings-main {
section {
padding: 0;
}
section.settings-section-noSaveOption {
height: 80px;
grid-template-columns: none;
grid-template-rows: calc(100% / var(--nb-noSaveOptions)) calc(100% / var(--nb-noSaveOptions)); //Same size for every sub-element;
}
}
}
</style>

View file

@ -0,0 +1,82 @@
<script lang="ts">
import { menuInputFocusStore } from "../../Stores/MenuStore";
import { onDestroy, onMount } from "svelte";
import { gameManager } from "../../Phaser/Game/GameManager";
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
import type { Quill } from "quill";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
//toolbar
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{'header': 1}, {'header': 2}], // custom button values
[{'list': 'ordered'}, {'list': 'bullet'}],
[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
[{'direction': 'rtl'}], // text direction
[{'size': ['small', false, 'large', 'huge']}], // custom dropdown
[{'header': [1, 2, 3, 4, 5, 6, false]}],
[{'color': []}, {'background': []}], // dropdown with defaults from theme
[{'font': []}],
[{'align': []}],
['clean'], // remove formatting button
['link', 'image', 'video']
];
const gameScene = gameManager.getCurrentGameScene();
const MESSAGE_TYPE = AdminMessageEventTypes.admin;
let quill: Quill;
let QUILL_EDITOR: HTMLDivElement;
export const handleSending = {
sendTextMessage(broadcastToWorld: boolean) {
if (gameScene == undefined) {
return;
}
const text = JSON.stringify(quill.getContents(0, quill.getLength()));
const textGlobalMessage: PlayGlobalMessageInterface = {
type: MESSAGE_TYPE,
content: text,
broadcastToWorld: broadcastToWorld
};
quill.deleteText(0, quill.getLength());
gameScene.connection?.emitGlobalMessage(textGlobalMessage);
}
}
//Quill
onMount(async () => {
// Import quill
const {default: Quill} = await import("quill"); // eslint-disable-line @typescript-eslint/no-explicit-any
quill = new Quill(QUILL_EDITOR, {
placeholder: 'Enter your message here...',
theme: 'snow',
modules: {
toolbar: toolbarOptions
},
});
menuInputFocusStore.set(true);
});
onDestroy(() => {
menuInputFocusStore.set(false);
})
</script>
<section class="section-input-send-text">
<div class="input-send-text" bind:this={QUILL_EDITOR}></div>
</section>
<style lang="scss">
@import 'https://cdn.quilljs.com/1.3.7/quill.snow.css';
</style>