Compare commits
No commits in common. "8c18d634c80dc30bf66e176077044727e274aea2" and "e2ba5f027fb2a911a8b5f6b290e32db306c6d8ae" have entirely different histories.
8c18d634c8
...
e2ba5f027f
|
|
@ -10,7 +10,7 @@ public record DeathBoxGameModel : IGameModel
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Coffin size (6-12 marks). When a player reaches this many marks, they're eliminated.
|
/// Coffin size (6-12 marks). When a player reaches this many marks, they're eliminated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int CoffinSize { get; init; } = 10;
|
public int CoffinSize { get; init; } = 12;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// State for each player (marks, X's, eggs, eliminated status).
|
/// State for each player (marks, X's, eggs, eliminated status).
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@
|
||||||
// Internal mutable class for two-way binding
|
// Internal mutable class for two-way binding
|
||||||
private class DeathBoxSetupOptionsInternal
|
private class DeathBoxSetupOptionsInternal
|
||||||
{
|
{
|
||||||
public int CoffinSize { get; set; } = 10;
|
public int CoffinSize { get; set; } = 12;
|
||||||
public bool RandomizePlayerOrder { get; set; } = true;
|
public bool RandomizePlayerOrder { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@
|
||||||
Label="Ziel-Punkte"
|
Label="Ziel-Punkte"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
Min="12"
|
Min="12"
|
||||||
Max="60"
|
Max="50"
|
||||||
HelperText="Vorsprung bei dem Fuchs entkommen erfolgreich ist (12-60)" />
|
HelperText="Vorsprung bei dem Fuchs entkommen erfolgreich ist (12-50)" />
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
{
|
{
|
||||||
public int LeadingThrows { get; set; } = 2;
|
public int LeadingThrows { get; set; } = 2;
|
||||||
|
|
||||||
public int PinCountGoal { get; set; } = 41;
|
public int PinCountGoal { get; set; } = 21;
|
||||||
|
|
||||||
public ParticipantsMode ParticipantsMode { get; set; } = ParticipantsMode.GameLogic;
|
public ParticipantsMode ParticipantsMode { get; set; } = ParticipantsMode.GameLogic;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||||
|
|
||||||
@using Fluxor
|
@using Fluxor
|
||||||
@using Koogle.Application.DTOs
|
@using Koogle.Application.DTOs
|
||||||
@using Koogle.Application.Interfaces
|
@using Koogle.Application.Interfaces
|
||||||
@using Koogle.Domain.Enums
|
@using Koogle.Domain.Enums
|
||||||
@using Koogle.Web.Store.GifState
|
@using Koogle.Web.Store.GifState
|
||||||
@inject IClubTerminologyService Terms
|
@inject IClubTerminologyService Terms
|
||||||
|
|
||||||
@inject IState<GifPlaybackState> GifState
|
@inject IState<GifPlaybackState> GifState
|
||||||
@inject IDispatcher Dispatcher
|
@inject IDispatcher Dispatcher
|
||||||
|
|
||||||
@if (GifState.Value.IsPlaying && GifState.Value.CurrentGif != null)
|
@if (GifState.Value.IsPlaying && GifState.Value.CurrentGif != null)
|
||||||
{
|
{
|
||||||
<MudOverlay Visible="true" DarkBackground="true" ZIndex="9999" OnClick="DismissGif"
|
<MudOverlay Visible="true" DarkBackground="true" ZIndex="9999" OnClick="DismissGif">
|
||||||
Class="fullscreen-overlay">
|
<div class="gif-player-container" @onclick:stopPropagation="true">
|
||||||
<div class="gif-player-container" @onclick:stopPropagation="true" @onclick="DismissGif">
|
|
||||||
|
|
||||||
@* Video/Image Bereich: Nimmt den verfügbaren Platz ein *@
|
|
||||||
<div class="gif-content">
|
<div class="gif-content">
|
||||||
@if (IsVideo)
|
@if (IsVideo)
|
||||||
{
|
{
|
||||||
<video autoplay muted loop playsinline class="gif-display" @ref="_videoElement">
|
<video autoplay muted loop class="gif-display" @ref="_videoElement">
|
||||||
<source src="@GifState.Value.CurrentGif.Url" type="@GifState.Value.CurrentGif.ContentType" />
|
<source src="@GifState.Value.CurrentGif.Url" type="@GifState.Value.CurrentGif.ContentType" />
|
||||||
</video>
|
</video>
|
||||||
}
|
}
|
||||||
|
|
@ -26,19 +25,17 @@
|
||||||
{
|
{
|
||||||
<img src="@GifState.Value.CurrentGif.Url" class="gif-display" alt="@GifState.Value.CurrentGif.Name" />
|
<img src="@GifState.Value.CurrentGif.Url" class="gif-display" alt="@GifState.Value.CurrentGif.Name" />
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
@* Overlay-Texte direkt auf dem Medium für maximale Platzersparnis *@
|
<div class="gif-info">
|
||||||
<div class="gif-overlay-text">
|
<MudText Typo="Typo.h3" Class="gif-event-name">
|
||||||
<MudText Typo="Typo.h2" Class="gif-event-name">
|
|
||||||
@GetEventDisplayName(GifState.Value.TriggerEvent)
|
@GetEventDisplayName(GifState.Value.TriggerEvent)
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudText Typo="Typo.h6" Class="gif-name">
|
<MudText Typo="Typo.subtitle1" Class="gif-name">
|
||||||
@GifState.Value.CurrentGif.Name
|
@GifState.Value.CurrentGif.Name
|
||||||
</MudText>
|
</MudText>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
@* Controls *@
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Close"
|
<MudIconButton Icon="@Icons.Material.Filled.Close"
|
||||||
Color="Color.Default"
|
Color="Color.Default"
|
||||||
Size="Size.Large"
|
Size="Size.Large"
|
||||||
|
|
@ -48,8 +45,14 @@
|
||||||
@if (ShowRating)
|
@if (ShowRating)
|
||||||
{
|
{
|
||||||
<div class="gif-rating">
|
<div class="gif-rating">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbUp" Color="Color.Success" OnClick="@(() => RateGif(1))" />
|
<MudIconButton Icon="@Icons.Material.Filled.ThumbUp"
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbDown" Color="Color.Error" OnClick="@(() => RateGif(-1))" />
|
Color="Color.Success"
|
||||||
|
Size="Size.Medium"
|
||||||
|
OnClick="@(() => RateGif(1))" />
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.ThumbDown"
|
||||||
|
Color="Color.Error"
|
||||||
|
Size="Size.Medium"
|
||||||
|
OnClick="@(() => RateGif(-1))" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -57,86 +60,64 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Das Overlay muss den gesamten Screen füllen */
|
|
||||||
.fullscreen-overlay {
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-player-container {
|
.gif-player-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
justify-content: center;
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 90vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gif-content {
|
.gif-content {
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wichtig: object-fit: contain sorgt für Beibehaltung des Seitenverhältnisses */
|
|
||||||
.gif-display {
|
.gif-display {
|
||||||
width: 100%;
|
max-width: 80vw;
|
||||||
height: 100%;
|
max-height: 60vh;
|
||||||
object-fit: contain;
|
border-radius: 8px;
|
||||||
max-width: 100vw;
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||||
max-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Texte über das Video legen, um keinen Platz zu verschwenden */
|
.gif-info {
|
||||||
.gif-overlay-text {
|
margin-top: 16px;
|
||||||
position: absolute;
|
|
||||||
bottom: 10%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
pointer-events: none; /* Klicks gehen durch auf das Video/Overlay */
|
color: white;
|
||||||
padding: 20px;
|
|
||||||
/* background: linear-gradient(transparent, rgba(0,0,0,0.7)); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gif-event-name {
|
.gif-event-name {
|
||||||
font-weight: 900;
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||||
text-shadow: 0 0 20px rgba(0,0,0,1), 2px 2px 5px rgba(0,0,0,0.8);
|
margin-bottom: 8px;
|
||||||
color: white;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gif-name {
|
.gif-name {
|
||||||
color: rgba(255, 255, 255, 0.7);
|
opacity: 0.8;
|
||||||
text-shadow: 1px 1px 3px rgba(0,0,0,0.8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gif-close-button {
|
.gif-close-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 20px;
|
top: -40px;
|
||||||
right: 20px;
|
right: -40px;
|
||||||
background: rgba(0, 0, 0, 0.4) !important;
|
background: rgba(255, 255, 255, 0.2);
|
||||||
color: white !important;
|
color: white !important;
|
||||||
z-index: 100;
|
}
|
||||||
|
|
||||||
|
.gif-close-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gif-rating {
|
.gif-rating {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 20px;
|
bottom: -60px;
|
||||||
right: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 16px;
|
||||||
background: rgba(0, 0, 0, 0.6);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
padding: 4px 12px;
|
padding: 8px 16px;
|
||||||
border-radius: 50px;
|
border-radius: 24px;
|
||||||
z-index: 100;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
|
||||||
|
|
||||||
@using Fluxor
|
|
||||||
@using Koogle.Application.DTOs
|
|
||||||
@using Koogle.Application.Interfaces
|
|
||||||
@using Koogle.Domain.Enums
|
|
||||||
@using Koogle.Web.Store.GifState
|
|
||||||
|
|
||||||
@inject IClubTerminologyService Terms
|
|
||||||
@inject IState<GifPlaybackState> GifState
|
|
||||||
@inject IDispatcher Dispatcher
|
|
||||||
|
|
||||||
@if (GifState.Value.IsPlaying && GifState.Value.CurrentGif != null)
|
|
||||||
{
|
|
||||||
<MudOverlay Visible="true" DarkBackground="true" ZIndex="9999" OnClick="DismissGif">
|
|
||||||
<!-- Vollbild-Container, Klicks innerhalb nicht zum Overlay propagieren -->
|
|
||||||
<div class="gif-player-container" @onclick:stopPropagation="true">
|
|
||||||
<div class="gif-content">
|
|
||||||
@if (IsVideo)
|
|
||||||
{
|
|
||||||
<video autoplay muted loop playsinline class="gif-display" @ref="_videoElement">
|
|
||||||
<source src="@GifState.Value.CurrentGif.Url" type="@GifState.Value.CurrentGif.ContentType" />
|
|
||||||
</video>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<img src="@GifState.Value.CurrentGif.Url" class="gif-display" alt="@GifState.Value.CurrentGif.Name" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gif-info">
|
|
||||||
<MudText Typo="Typo.h3" Class="gif-event-name">
|
|
||||||
@GetEventDisplayName(GifState.Value.TriggerEvent)
|
|
||||||
</MudText>
|
|
||||||
<MudText Typo="Typo.subtitle1" Class="gif-name">
|
|
||||||
@GifState.Value.CurrentGif.Name
|
|
||||||
</MudText>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Close"
|
|
||||||
Color="Color.Default"
|
|
||||||
Size="Size.Large"
|
|
||||||
Class="gif-close-button"
|
|
||||||
OnClick="DismissGif" />
|
|
||||||
|
|
||||||
@if (ShowRating)
|
|
||||||
{
|
|
||||||
<div class="gif-rating">
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbUp"
|
|
||||||
Color="Color.Success"
|
|
||||||
Size="Size.Medium"
|
|
||||||
OnClick="@(() => RateGif(1))" />
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbDown"
|
|
||||||
Color="Color.Error"
|
|
||||||
Size="Size.Medium"
|
|
||||||
OnClick="@(() => RateGif(-1))" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</MudOverlay>
|
|
||||||
}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Vollbild-Layout */
|
|
||||||
.gif-player-container {
|
|
||||||
position: fixed; /* unabhängig vom Dokumentfluss: immer viewportfüllend */
|
|
||||||
inset: 0; /* top/right/bottom/left = 0 */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
/* Abstand zu Rändern und Notch berücksichtigen */
|
|
||||||
padding: calc(16px + env(safe-area-inset-top)) calc(16px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(16px + env(safe-area-inset-left));
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-content {
|
|
||||||
/* Fläche, in der das Medium maximal wachsen darf */
|
|
||||||
flex: 1 1 auto;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 0; /* verhindert Flex-Überlauf in manchen Browsern */
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-display {
|
|
||||||
/* Maximale Größe ohne Verzerrung; Seitenverhältnis bleibt erhalten */
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
object-fit: contain; /* entscheidend für korrekte Skalierung */
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.65);
|
|
||||||
background: #000; /* falls Letterboxing, wirkt das sauberer */
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-info {
|
|
||||||
margin-top: 12px;
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
|
||||||
/* leichte Lesbarkeit über Video/GIF */
|
|
||||||
text-shadow: 0 2px 6px rgba(0, 0, 0, 0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-event-name {
|
|
||||||
margin-bottom: 6px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-name {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Schließen-Button: im Viewport oben rechts, sicherer Abstand */
|
|
||||||
.gif-close-button {
|
|
||||||
position: fixed;
|
|
||||||
top: calc(16px + env(safe-area-inset-top));
|
|
||||||
right: calc(16px + env(safe-area-inset-right));
|
|
||||||
background: rgba(255, 255, 255, 0.18);
|
|
||||||
color: white !important;
|
|
||||||
backdrop-filter: blur(6px);
|
|
||||||
border-radius: 999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-close-button:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.28);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rating: unten mittig, gut erreichbar */
|
|
||||||
.gif-rating {
|
|
||||||
position: fixed;
|
|
||||||
bottom: calc(24px + env(safe-area-inset-bottom));
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
background: rgba(0, 0, 0, 0.55);
|
|
||||||
padding: 10px 18px;
|
|
||||||
border-radius: 28px;
|
|
||||||
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Optional: bei sehr kleinen Höhen etwas enger layouten */
|
|
||||||
@@media (max-height: 480px) {
|
|
||||||
.gif-info
|
|
||||||
|
|
||||||
{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* Platz sparen */
|
|
||||||
.gif-display {
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private ElementReference _videoElement;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool ShowRating { get; set; } = true;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Guid? UserProfileId { get; set; }
|
|
||||||
|
|
||||||
private bool IsVideo => GifState.Value.CurrentGif?.ContentType.StartsWith("video/") ?? false;
|
|
||||||
|
|
||||||
private void DismissGif()
|
|
||||||
{
|
|
||||||
Dispatcher.Dispatch(new EndGifPlaybackAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RateGif(int value)
|
|
||||||
{
|
|
||||||
if (UserProfileId.HasValue && GifState.Value.CurrentGif != null)
|
|
||||||
{
|
|
||||||
Dispatcher.Dispatch(new RateCurrentGifAction(UserProfileId.Value, value));
|
|
||||||
}
|
|
||||||
DismissGif();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _termNoWood = "PUDEL!";
|
|
||||||
private string _termGutter = "RINNE!";
|
|
||||||
private string _termBell = "GLOCKE!";
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
var noWood = await Terms.GetTermAsync(TermKey.NoWood);
|
|
||||||
_termNoWood = $"{noWood.ToUpper()}!";
|
|
||||||
|
|
||||||
var gutter = await Terms.GetTermAsync(TermKey.Gutter);
|
|
||||||
_termGutter = $"{gutter.ToUpper()}!";
|
|
||||||
|
|
||||||
var bell = await Terms.GetTermAsync(TermKey.Gutter);
|
|
||||||
_termBell = $"{bell.ToUpper()}!";
|
|
||||||
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetEventDisplayName(ThrowEventType? eventType) => eventType switch
|
|
||||||
{
|
|
||||||
ThrowEventType.Strike => "ALLE NEUNE!",
|
|
||||||
ThrowEventType.Circle => "KRANZ!",
|
|
||||||
ThrowEventType.Bell => _termBell,
|
|
||||||
ThrowEventType.Gutter => _termGutter,
|
|
||||||
ThrowEventType.NoWood => _termNoWood,
|
|
||||||
ThrowEventType.Cleared => "ABGERÄUMT!",
|
|
||||||
_ => ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,201 +0,0 @@
|
||||||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
|
||||||
@using Fluxor
|
|
||||||
@using Koogle.Application.DTOs
|
|
||||||
@using Koogle.Application.Interfaces
|
|
||||||
@using Koogle.Domain.Enums
|
|
||||||
@using Koogle.Web.Store.GifState
|
|
||||||
@inject IClubTerminologyService Terms
|
|
||||||
@inject IState<GifPlaybackState> GifState
|
|
||||||
@inject IDispatcher Dispatcher
|
|
||||||
|
|
||||||
@if (GifState.Value.IsPlaying && GifState.Value.CurrentGif != null)
|
|
||||||
{
|
|
||||||
<MudOverlay Visible="true" DarkBackground="true" ZIndex="9999" OnClick="DismissGif" Class="fullscreen-overlay">
|
|
||||||
<div class="gif-player-container" @onclick:stopPropagation="true">
|
|
||||||
|
|
||||||
@* Video/Image Bereich: Nimmt den verfügbaren Platz ein *@
|
|
||||||
<div class="gif-content">
|
|
||||||
@if (IsVideo)
|
|
||||||
{
|
|
||||||
<video autoplay muted loop playsinline class="gif-display" @ref="_videoElement">
|
|
||||||
<source src="@GifState.Value.CurrentGif.Url" type="@GifState.Value.CurrentGif.ContentType" />
|
|
||||||
</video>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<img src="@GifState.Value.CurrentGif.Url" class="gif-display" alt="@GifState.Value.CurrentGif.Name" />
|
|
||||||
}
|
|
||||||
|
|
||||||
@* Overlay-Texte direkt auf dem Medium für maximale Platzersparnis *@
|
|
||||||
<div class="gif-overlay-text">
|
|
||||||
<MudText Typo="Typo.h2" Class="gif-event-name">
|
|
||||||
@GetEventDisplayName(GifState.Value.TriggerEvent)
|
|
||||||
</MudText>
|
|
||||||
<MudText Typo="Typo.h6" Class="gif-name">
|
|
||||||
@GifState.Value.CurrentGif.Name
|
|
||||||
</MudText>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@* Controls *@
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Close"
|
|
||||||
Color="Color.Default"
|
|
||||||
Size="Size.Large"
|
|
||||||
Class="gif-close-button"
|
|
||||||
OnClick="DismissGif" />
|
|
||||||
|
|
||||||
@if (ShowRating)
|
|
||||||
{
|
|
||||||
<div class="gif-rating">
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbUp" Color="Color.Success" OnClick="@(() => RateGif(1))" />
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.ThumbDown" Color="Color.Error" OnClick="@(() => RateGif(-1))" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</MudOverlay>
|
|
||||||
}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Das Overlay muss den gesamten Screen füllen */
|
|
||||||
.fullscreen-overlay {
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-player-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-content {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wichtig: object-fit: contain sorgt für Beibehaltung des Seitenverhältnisses */
|
|
||||||
.gif-display {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
max-width: 100vw;
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Texte über das Video legen, um keinen Platz zu verschwenden */
|
|
||||||
.gif-overlay-text {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 10%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
text-align: center;
|
|
||||||
pointer-events: none; /* Klicks gehen durch auf das Video/Overlay */
|
|
||||||
padding: 20px;
|
|
||||||
background: linear-gradient(transparent, rgba(0,0,0,0.7));
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-event-name {
|
|
||||||
font-weight: 900;
|
|
||||||
text-shadow: 0 0 20px rgba(0,0,0,1), 2px 2px 5px rgba(0,0,0,0.8);
|
|
||||||
color: white;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-name {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
text-shadow: 1px 1px 3px rgba(0,0,0,0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-close-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
background: rgba(0, 0, 0, 0.4) !important;
|
|
||||||
color: white !important;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gif-rating {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
background: rgba(0, 0, 0, 0.6);
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 50px;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private ElementReference _videoElement;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool ShowRating { get; set; } = true;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Guid? UserProfileId { get; set; }
|
|
||||||
|
|
||||||
private bool IsVideo => GifState.Value.CurrentGif?.ContentType.StartsWith("video/") ?? false;
|
|
||||||
|
|
||||||
private void DismissGif()
|
|
||||||
{
|
|
||||||
Dispatcher.Dispatch(new EndGifPlaybackAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RateGif(int value)
|
|
||||||
{
|
|
||||||
if (UserProfileId.HasValue && GifState.Value.CurrentGif != null)
|
|
||||||
{
|
|
||||||
Dispatcher.Dispatch(new RateCurrentGifAction(UserProfileId.Value, value));
|
|
||||||
}
|
|
||||||
DismissGif();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _termNoWood = "PUDEL!";
|
|
||||||
|
|
||||||
private string _termGutter = "RINNE!";
|
|
||||||
|
|
||||||
private string _termBell = "GLOCKE!";
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
var noWood = await Terms.GetTermAsync(TermKey.NoWood);
|
|
||||||
_termNoWood = $"{noWood.ToUpper()}!";
|
|
||||||
|
|
||||||
var gutter = await Terms.GetTermAsync(TermKey.Gutter);
|
|
||||||
_termGutter = $"{gutter.ToUpper()}!";
|
|
||||||
|
|
||||||
var bell = await Terms.GetTermAsync(TermKey.Gutter);
|
|
||||||
_termBell = $"{bell.ToUpper()}!";
|
|
||||||
|
|
||||||
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetEventDisplayName(ThrowEventType? eventType) => eventType switch
|
|
||||||
{
|
|
||||||
ThrowEventType.Strike => "ALLE NEUNE!",
|
|
||||||
ThrowEventType.Circle => "KRANZ!",
|
|
||||||
ThrowEventType.Bell => _termBell,
|
|
||||||
ThrowEventType.Gutter => _termGutter,
|
|
||||||
ThrowEventType.NoWood => _termNoWood,
|
|
||||||
ThrowEventType.Cleared => "ABGERÄUMT!",
|
|
||||||
_ => ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue