Using react to render the image preview and removed the main app template

master
WolverinDEV 2021-03-17 21:20:03 +01:00
parent 4d2b83d7a3
commit 64b618d0bf
16 changed files with 239 additions and 186 deletions

View File

@ -188,7 +188,6 @@ function execute_build_debug() {
} }
chmod +x ./scripts/build.sh chmod +x ./scripts/build.sh
chmod +x ./scripts/web_package.sh
if [[ ${build_release} ]]; then if [[ ${build_release} ]]; then
execute_build_release execute_build_release
fi fi

View File

@ -5,29 +5,6 @@
<title>TeaSpeak-Web client templates</title> <title>TeaSpeak-Web client templates</title>
</head> </head>
<body> <body>
<script class="jsrender-template" id="tmpl_main" type="text/html">
<div class="overlay-image-preview hidden" id="overlay-image-preview">
<div class="container-menu-bar">
<div class="entry button-open-in-window">
<div class="container-icon">
<img src="img/icon_image_preview_browse.svg">
</div>
</div>
<!-- Why would you like to download images?
<div class="entry button-download">
<div class="icon_em client-download"></div>
</div>
-->
<div class="entry button-close">
<div class="icon_em client-close_button"></div>
</div>
</div>
<div class="container-image">
<img src="" alt="image"/>
</div>
</div>
</script>
<div class="template-group-modals"> <div class="template-group-modals">
<script class="jsrender-template" id="tmpl_modal" type="text/html"> <script class="jsrender-template" id="tmpl_modal" type="text/html">
<div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" enable-background="new 0 0 512.418 512.418" fill="#7289da" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="m13.667 2.3463c-3.1281-3.1282-8.1918-3.1287-11.32 0-3.1282 3.1282-3.1287 8.1918 0 11.32 3.1281 3.1282 8.1917 3.1286 11.32 0 3.1282-3.1282 3.1287-8.1918 0-11.32zm-.33456 10.179c-.374-.33444-.77407-.6304-1.1958-.88547.27872-.96278.44384-2.0222.48287-3.1236h2.3534c-.10641 1.4845-.67678 2.877-1.6405 4.009zm-12.292-4.009h2.3534c.039 1.1013.20416 2.1608.48288 3.1236-.42169.25507-.82175.55103-1.1958.88547-.96375-1.132-1.5341-2.5246-1.6405-4.009zm1.6406-5.0293c.374.33444.77406.63041 1.1958.88547-.27872.96279-.44384 2.0223-.48288 3.1236h-2.3534c.10643-1.4844.67678-2.877 1.6406-4.009zm4.8155.99091c-.83193-.0595-1.6341-.26441-2.3778-.60244.4325-1.1137 1.2298-2.4495 2.3778-2.7805zm0 1.0221v1.996h-3.082c.0353-.93163.16731-1.8262.38684-2.6448.8451.36944 1.754.59006 2.6952.64872zm0 3.0162v1.996c-.94122.0587-1.8501.27928-2.6952.64872-.21953-.81853-.35153-1.7131-.38684-2.6448zm0 3.0182v3.3829c-1.1479-.33103-1.9452-1.6667-2.3778-2.7805.74366-.33803 1.5458-.54293 2.3778-.60243zm1.0202 0c.83194.0595 1.6341.2644 2.3777.60243-.43253 1.1138-1.2298 2.4495-2.3777 2.7805zm0-1.0221v-1.996h3.082c-.0353.93162-.16732 1.8262-.38685 2.6448-.84509-.36944-1.754-.59006-2.6952-.64872zm0-3.0162v-1.996c.94122-.0587 1.8501-.27928 2.6952-.64872.21953.81853.35153 1.7131.38685 2.6448zm0-3.0182v-3.3829c1.1479.33103 1.9452 1.6668 2.3777 2.7805-.74365.338-1.5458.54291-2.3777.60241zm2.2912-2.8737c.65297.28594 1.2622.67366 1.8083 1.1547-.25994.22919-.53422.437-.82063.62285-.25434-.62975-.57925-1.2418-.98763-1.7776zm-6.5903 1.7776c-.28641-.18585-.56066-.39366-.82063-.62285.54609-.48106 1.1553-.86878 1.8082-1.1547-.40837.53588-.73328 1.1479-.98762 1.7776zm3e-5 9.2487c.25431.62972.57928 1.2418.98762 1.7776-.65297-.28593-1.2622-.67365-1.8082-1.1547.25994-.22919.53422-.43701.82063-.62285zm7.5779 0c.28641.18584.56069.39366.82063.62281-.5461.48107-1.1553.86879-1.8083 1.1547.40835-.53575.73329-1.1478.98763-1.7775zm.82381-5.1344c-.039-1.1013-.20415-2.1608-.48287-3.1236.42169-.25507.82172-.55104 1.1958-.88547.96375 1.132 1.5341 2.5246 1.6405 4.009z" style="stroke-width:.03125"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1 +0,0 @@
<svg id="Layer_1" fill="#7289da" enable-background="new 0 0 512.418 512.418" height="512" viewBox="0 0 512.418 512.418" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m437.335 75.082c-100.1-100.102-262.136-100.118-362.252 0-100.103 100.102-100.118 262.136 0 362.253 100.1 100.102 262.136 100.117 362.252 0 100.103-100.102 100.117-262.136 0-362.253zm-10.706 325.739c-11.968-10.702-24.77-20.173-38.264-28.335 8.919-30.809 14.203-64.712 15.452-99.954h75.309c-3.405 47.503-21.657 92.064-52.497 128.289zm-393.338-128.289h75.309c1.249 35.242 6.533 69.145 15.452 99.954-13.494 8.162-26.296 17.633-38.264 28.335-30.84-36.225-49.091-80.786-52.497-128.289zm52.498-160.936c11.968 10.702 24.77 20.173 38.264 28.335-8.919 30.809-14.203 64.712-15.452 99.954h-75.31c3.406-47.502 21.657-92.063 52.498-128.289zm154.097 31.709c-26.622-1.904-52.291-8.461-76.088-19.278 13.84-35.639 39.354-78.384 76.088-88.977zm0 32.708v63.873h-98.625c1.13-29.812 5.354-58.439 12.379-84.632 27.043 11.822 56.127 18.882 86.246 20.759zm0 96.519v63.873c-30.119 1.877-59.203 8.937-86.246 20.759-7.025-26.193-11.249-54.82-12.379-84.632zm0 96.581v108.254c-36.732-10.593-62.246-53.333-76.088-88.976 23.797-10.817 49.466-17.374 76.088-19.278zm32.646 0c26.622 1.904 52.291 8.461 76.088 19.278-13.841 35.64-39.354 78.383-76.088 88.976zm0-32.708v-63.873h98.625c-1.13 29.812-5.354 58.439-12.379 84.632-27.043-11.822-56.127-18.882-86.246-20.759zm0-96.519v-63.873c30.119-1.877 59.203-8.937 86.246-20.759 7.025 26.193 11.249 54.82 12.379 84.632zm0-96.581v-108.254c36.734 10.593 62.248 53.338 76.088 88.977-23.797 10.816-49.466 17.373-76.088 19.277zm73.32-91.957c20.895 9.15 40.389 21.557 57.864 36.951-8.318 7.334-17.095 13.984-26.26 19.931-8.139-20.152-18.536-39.736-31.604-56.882zm-210.891 56.882c-9.165-5.947-17.941-12.597-26.26-19.931 17.475-15.394 36.969-27.801 57.864-36.951-13.068 17.148-23.465 36.732-31.604 56.882zm.001 295.958c8.138 20.151 18.537 39.736 31.604 56.882-20.895-9.15-40.389-21.557-57.864-36.951 8.318-7.334 17.095-13.984 26.26-19.931zm242.494 0c9.165 5.947 17.942 12.597 26.26 19.93-17.475 15.394-36.969 27.801-57.864 36.951 13.067-17.144 23.465-36.729 31.604-56.881zm26.362-164.302c-1.249-35.242-6.533-69.146-15.452-99.954 13.494-8.162 26.295-17.633 38.264-28.335 30.84 36.225 49.091 80.786 52.497 128.289z"/></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -53,8 +53,6 @@ import {clientServiceInvite} from "tc-shared/clientservice";
import {ActionResult} from "tc-services"; import {ActionResult} from "tc-services";
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration"; import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
import {ErrorCode} from "tc-shared/connection/ErrorCode"; import {ErrorCode} from "tc-shared/connection/ErrorCode";
import "./Bookmarks";
import {bookmarks} from "tc-shared/Bookmarks"; import {bookmarks} from "tc-shared/Bookmarks";
assertMainApplication(); assertMainApplication();
@ -568,19 +566,4 @@ loader.register_task(loader.Stage.LOADED, {
bookmarks.executeAutoConnect(); bookmarks.executeAutoConnect();
}, },
name: tr("bookmark auto connect") name: tr("bookmark auto connect")
});
/* TODO: Remove this after the image preview has been rewritten into react */
loader.register_task(Stage.JAVASCRIPT_INITIALIZING,{
name: "app init",
function: async () => {
try {
$("body").append($("#tmpl_main").renderTag());
} catch(error) {
logError(LogCategory.GENERAL, error);
loader.critical_error(tr("Failed to setup main page!"));
return;
}
},
priority: 100
}); });

View File

@ -5,7 +5,7 @@ import * as loader from "tc-loader";
import {rendererReact, rendererText} from "tc-shared/text/bbcode/renderer"; import {rendererReact, rendererText} from "tc-shared/text/bbcode/renderer";
import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
import {copyToClipboard} from "tc-shared/utils/helpers"; import {copyToClipboard} from "tc-shared/utils/helpers";
import * as image_preview from "tc-shared/ui/frames/image_preview"; import * as image_preview from "tc-shared/ui/frames/ImagePreview";
export const regexImage = /^(?:https?):(?:\/{1,3}|\\)[-a-zA-Z0-9:;,@#%&()~_?+=\/\\.]*$/g; export const regexImage = /^(?:https?):(?:\/{1,3}|\\)[-a-zA-Z0-9:;,@#%&()~_?+=\/\\.]*$/g;
@ -49,7 +49,7 @@ function loadImageForElement(element: HTMLImageElement) {
icon_class: "client-copy" icon_class: "client-copy"
}) })
}); });
parent.css("cursor", "pointer").on('click', () => image_preview.preview_image(proxiedURL, url)); parent.css("cursor", "pointer").on('click', () => image_preview.showImagePreview(proxiedURL, url));
} }
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, { loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {

View File

@ -11,72 +11,72 @@
width: 100%; width: 100%;
padding: 5px; padding: 5px;
}
/* TODO: Move this into the control bar? */ /* TODO: Move this into the control bar? */
.controlBar { .controlBar {
z-index: 200; z-index: 200;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
flex-shrink: 0; flex-shrink: 0;
flex-grow: 0; flex-grow: 0;
border-radius: 5px; border-radius: 5px;
height: 2em; height: 2em;
width: 100%; width: 100%;
} }
.mainContainer { .mainContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: stretch; justify-content: stretch;
position: relative; position: relative;
flex-shrink: 1; flex-shrink: 1;
height: 100%; height: 100%;
min-height: 400px; min-height: 400px;
margin-top: 5px; margin-top: 5px;
} }
.channelTreeAndSidebar { .channelTreeAndSidebar {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: stretch; justify-content: stretch;
min-height: 27em; min-height: 27em;
} }
.channelTree { .channelTree {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: stretch; justify-content: stretch;
background: var(--channel-tree-background); background: var(--channel-tree-background);
min-width: 200px; min-width: 200px;
min-height: 100px; min-height: 100px;
overflow: hidden; overflow: hidden;
border-radius: 5px; border-radius: 5px;
} }
.sideBar { .sideBar {
min-width: 350px; min-width: 350px;
} }
.containerLog { .containerLog {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: stretch; justify-content: stretch;
flex-shrink: 1; flex-shrink: 1;
flex-grow: 1; flex-grow: 1;
min-height: 0; min-height: 0;
}
} }

View File

@ -18,6 +18,7 @@ import {HostBannerUiEvents} from "tc-shared/ui/frames/HostBannerDefinitions";
import {AppUiEvents} from "tc-shared/ui/AppDefinitions"; import {AppUiEvents} from "tc-shared/ui/AppDefinitions";
import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer"; import {ChannelTreeRenderer} from "tc-shared/ui/tree/Renderer";
import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions"; import {ChannelTreeUIEvents} from "tc-shared/ui/tree/Definitions";
import {ImagePreviewHook} from "tc-shared/ui/frames/ImagePreview";
const cssStyle = require("./AppRenderer.scss"); const cssStyle = require("./AppRenderer.scss");
const VideoFrame = React.memo((props: { events: Registry<AppUiEvents> }) => { const VideoFrame = React.memo((props: { events: Registry<AppUiEvents> }) => {
@ -100,8 +101,10 @@ export const TeaAppMainView = (props: {
</ErrorBoundary> </ErrorBoundary>
</div> </div>
<FooterRenderer /> <FooterRenderer />
<ErrorBoundary>
<ImagePreviewHook />
</ErrorBoundary>
</div> </div>
); );
} }
/* ConnectionHandlerList */

View File

@ -0,0 +1,89 @@
@import "../../../css/static/properties";
@import "../../../css/static/mixin";
.overlay {
position: absolute;
z-index: 1000;
pointer-events: all;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 1;
background-color: #000000EF;
display: flex;
flex-direction: column;
justify-content: center;
&.hidden {
pointer-events: none;
opacity: 0;
}
@include transition(ease-in-out .25s);
}
.containerMenuBar {
position: absolute;
top: .25em;
left: 0;
right: .25em;
display: flex;
flex-direction: row;
justify-content: flex-end;
.entry {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 2em;
margin: .25em;
padding: .15em;
border-radius: .125em;
cursor: pointer;
.containerIcon {
width: 1em;
height: 1em;
display: flex;
img {
height: 100%;
width: 100%;
}
}
&:hover {
background-color: #FFFFFF1F;
}
}
}
.containerImage {
max-width: 90%;
max-height: 90%;
align-self: center;
display: flex;
flex-direction: column;
justify-content: stretch;
img {
flex-shrink: 1;
min-height: 1em;
min-width: 1em;
max-height: 100%;
max-width: 100%;
}
}

View File

@ -0,0 +1,79 @@
import * as loader from "tc-loader";
import React from "react";
import {joinClassList} from "tc-shared/ui/react-elements/Helper";
import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
import {ClientIcon} from "svg-sprites/client-icons";
import ReactDOM from "react-dom";
const cssStyle = require("./ImagePreview.scss");
const imagePreviewInstance = React.createRef<ImagePreview>();
class ImagePreview extends React.PureComponent<{}, {
imageUrl: string | undefined,
targetUrl: string | undefined
}> {
constructor(props) {
super(props);
this.state = { targetUrl: undefined, imageUrl: undefined };
}
render() {
return (
<div
className={joinClassList(cssStyle.overlay, (!this.state.imageUrl ? cssStyle.hidden : undefined))}
onClick={event => {
if(event.isDefaultPrevented()) {
return;
}
this.closePreview()
}}
>
<div className={cssStyle.containerMenuBar}>
<div
className={cssStyle.entry}
onClick={() => {
const win = window.open(this.state.targetUrl, '_blank');
win.focus();
}}
>
<ClientIconRenderer icon={ClientIcon.ImagePreviewBrowse} />
</div>
<div
className={cssStyle.entry}
onClick={() => this.closePreview()}
>
<ClientIconRenderer icon={ClientIcon.CloseButton} />
</div>
</div>
<div className={cssStyle.containerImage}>
<img src={this.state.imageUrl} title={this.state.targetUrl} alt={null} />
</div>
</div>
)
}
closePreview() {
this.setState({ imageUrl: undefined, targetUrl: undefined });
}
}
export const ImagePreviewHook = React.memo(() => {
return <ImagePreview ref={imagePreviewInstance} />;
})
export function showImagePreview(url: string, originalUrl: string) {
imagePreviewInstance.current?.setState({
imageUrl: url,
targetUrl: originalUrl
});
}
export function isImagePreviewAvailable() {
return !!imagePreviewInstance.current;
}
export function closeImagePreview() {
imagePreviewInstance.current?.closePreview();
}

View File

@ -1,81 +0,0 @@
import * as loader from "tc-loader";
let preview_overlay: JQuery<HTMLDivElement>;
let container_image: JQuery<HTMLDivElement>;
let button_open_in_browser: JQuery;
export function preview_image(url: string, original_url: string) {
if(!preview_overlay) return;
container_image.empty();
$.spawn("img").attr({
"src": url,
"title": original_url,
"x-original-src": original_url
}).appendTo(container_image);
preview_overlay.removeClass("hidden");
button_open_in_browser.show();
}
export function preview_image_tag(tag: JQuery) {
if(!preview_overlay) return;
container_image.empty();
container_image.append(tag);
preview_overlay.removeClass("hidden");
button_open_in_browser.hide();
}
export function current_url() {
const image_tag = container_image.find("img");
return image_tag.attr("x-original-src") || image_tag.attr("src") || "";
}
export function close_preview() {
preview_overlay.addClass("hidden");
}
loader.register_task(loader.Stage.LOADED, {
priority: 0,
name: "image preview init",
function: async () => {
preview_overlay = $("#overlay-image-preview");
container_image = preview_overlay.find(".container-image") as any;
preview_overlay.find("img").on('click', event => event.preventDefault());
preview_overlay.on('click', event => {
if(event.isDefaultPrevented()) return;
close_preview();
});
preview_overlay.find(".button-close").on('click', event => {
event.preventDefault();
close_preview();
});
preview_overlay.find(".button-download").on('click', event => {
event.preventDefault();
const link = document.createElement('a');
link.href = current_url();
link.target = "_blank";
const findex = link.href.lastIndexOf("/") + 1;
link.download = link.href.substr(findex);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
button_open_in_browser = preview_overlay.find(".button-open-in-window");
button_open_in_browser.on('click', event => {
event.preventDefault();
const win = window.open(current_url(), '_blank');
win.focus();
});
}
});

View File

@ -10,7 +10,7 @@ import {
MusicBotUiEvents MusicBotUiEvents
} from "tc-shared/ui/frames/side/MusicBotDefinitions"; } from "tc-shared/ui/frames/side/MusicBotDefinitions";
import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {preview_image} from "tc-shared/ui/frames/image_preview"; import {showImagePreview} from "tc-shared/ui/frames/ImagePreview";
import {Slider} from "tc-shared/ui/react-elements/Slider"; import {Slider} from "tc-shared/ui/react-elements/Slider";
const cssStyle = require("./MusicBotRenderer.scss"); const cssStyle = require("./MusicBotRenderer.scss");
@ -144,7 +144,7 @@ const Thumbnail = React.memo(() => {
draggable={false} draggable={false}
key={"song-thumbnail"} key={"song-thumbnail"}
src={info.thumbnail} src={info.thumbnail}
onClick={() => preview_image(info.thumbnail, info.thumbnail)} onClick={() => showImagePreview(info.thumbnail, info.thumbnail)}
alt={tr("Thumbnail")} alt={tr("Thumbnail")}
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
/> />

View File

@ -9,7 +9,7 @@ import {
import {Button} from "tc-shared/ui/react-elements/Button"; import {Button} from "tc-shared/ui/react-elements/Button";
import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots"; import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
import {preview_image} from "tc-shared/ui/frames/image_preview"; import {showImagePreview} from "tc-shared/ui/frames/ImagePreview";
import {joinClassList, useTr} from "tc-shared/ui/react-elements/Helper"; import {joinClassList, useTr} from "tc-shared/ui/react-elements/Helper";
import {spawnContextMenu} from "tc-shared/ui/ContextMenu"; import {spawnContextMenu} from "tc-shared/ui/ContextMenu";
import {copyToClipboard} from "tc-shared/utils/helpers"; import {copyToClipboard} from "tc-shared/utils/helpers";
@ -87,7 +87,7 @@ const PlaylistEntry = React.memo((props: { serverUniqueId: string, playlistId: n
draggable={false} draggable={false}
key={"song-thumbnail"} key={"song-thumbnail"}
src={status.thumbnailImage} src={status.thumbnailImage}
onClick={() => preview_image(status.thumbnailImage, status.thumbnailImage)} onClick={() => showImagePreview(status.thumbnailImage, status.thumbnailImage)}
alt={useTr("Thumbnail")} alt={useTr("Thumbnail")}
/> />
) )

View File

@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {ClientAvatar, kDefaultAvatarImage, kLoadingAvatarImage} from "tc-shared/file/Avatars"; import {ClientAvatar, kDefaultAvatarImage, kLoadingAvatarImage} from "tc-shared/file/Avatars";
import * as image_preview from "tc-shared/ui/frames/image_preview"; import * as image_preview from "tc-shared/ui/frames/ImagePreview";
const ImageStyle = { height: "100%", width: "100%", cursor: "pointer" }; const ImageStyle = { height: "100%", width: "100%", cursor: "pointer" };
export const AvatarRenderer = React.memo((props: { avatar: ClientAvatar | "loading" | "default", className?: string, alt?: string }) => { export const AvatarRenderer = React.memo((props: { avatar: ClientAvatar | "loading" | "default", className?: string, alt?: string }) => {
@ -28,7 +28,7 @@ export const AvatarRenderer = React.memo((props: { avatar: ClientAvatar | "loadi
return; return;
event.preventDefault(); event.preventDefault();
image_preview.preview_image(imageUrl, undefined); image_preview.showImagePreview(imageUrl, undefined);
}} }}
draggable={false} draggable={false}
/>; />;
@ -46,7 +46,7 @@ export const AvatarRenderer = React.memo((props: { avatar: ClientAvatar | "loadi
return; return;
event.preventDefault(); event.preventDefault();
image_preview.preview_image(imageUrl, undefined); image_preview.showImagePreview(imageUrl, undefined);
}} }}
draggable={false} draggable={false}
/>; />;

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,7 @@
z-index: 201; z-index: 201;
font-family: Arial, serif; font-family: Arial, serif;
z-index: 10000;
> .containerMenuItem { > .containerMenuItem {
> .menuItem { > .menuItem {