mirror of
https://github.com/morbalint/kemkas.git
synced 2026-07-17 19:03:46 +00:00
better isPublic handling
This commit is contained in:
@@ -17,7 +17,7 @@ public class CharacterController(
|
|||||||
: ControllerBase
|
: ControllerBase
|
||||||
{
|
{
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<Guid>> StoreNewCharacter([FromBody] CharacterDto dto, bool isPublic = true)
|
public async Task<ActionResult<Guid>> StoreNewCharacter([FromBody] CharacterDto dto, [FromQuery]bool isPublic = true)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,7 @@ public class CharacterController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:guid}")]
|
[HttpPost("{id:guid}")]
|
||||||
public async Task<ActionResult<Guid>> UpdateCharacter([FromRoute] Guid id, [FromBody] CharacterDto dto, bool isPublic = true)
|
public async Task<ActionResult<Guid>> UpdateCharacter([FromRoute] Guid id, [FromBody] CharacterDto dto, [FromQuery]bool isPublic = true)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
using Kemkas.Web.Config;
|
using Kemkas.Web.Config;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -55,6 +56,7 @@ builder.Services.AddAuthentication()
|
|||||||
|
|
||||||
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
|
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
|
||||||
{
|
{
|
||||||
|
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
|
||||||
options.JsonSerializerOptions.AllowTrailingCommas = true;
|
options.JsonSerializerOptions.AllowTrailingCommas = true;
|
||||||
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
|
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class CharacterDbModelToDtoService : ICharacterDbModelToDtoService
|
|||||||
PancelId = entity.Pancel,
|
PancelId = entity.Pancel,
|
||||||
FegyverIds = entity.Felszereles.Where(x => x.IsFegyver).Select(x => x.Name).ToList(),
|
FegyverIds = entity.Felszereles.Where(x => x.IsFegyver).Select(x => x.Name).ToList(),
|
||||||
},
|
},
|
||||||
|
IsPublic = entity.IsPublic,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,4 +53,6 @@ public struct CharacterDto
|
|||||||
|
|
||||||
[JsonPropertyName("felszereles")]
|
[JsonPropertyName("felszereles")]
|
||||||
public KarakterFelszerelesDto Felszereles { get; set; }
|
public KarakterFelszerelesDto Felszereles { get; set; }
|
||||||
|
|
||||||
|
public bool? IsPublic { get; set; }
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@ import CharacterList from "./first-edition/pages/CharacterList";
|
|||||||
import ErrorBoundary from "./shared/ErrorBoundary";
|
import ErrorBoundary from "./shared/ErrorBoundary";
|
||||||
import Header from "./shared/Header";
|
import Header from "./shared/Header";
|
||||||
import Footer from "./shared/Footer";
|
import Footer from "./shared/Footer";
|
||||||
|
import {useState} from "react";
|
||||||
|
import {UserContext, userDefaults} from './shared/contexts/UserContext';
|
||||||
|
|
||||||
function App(props: {faro?: Faro}) {
|
function App(props: {faro?: Faro}) {
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
@@ -28,16 +30,41 @@ function App(props: {faro?: Faro}) {
|
|||||||
ErrorBoundary: ErrorBoundary,
|
ErrorBoundary: ErrorBoundary,
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const [fetchedUser, setFetchedUser] = useState(userDefaults)
|
||||||
|
|
||||||
|
if (fetchedUser.state === "not-started") {
|
||||||
|
setFetchedUser({...fetchedUser, state: "loading"})
|
||||||
|
fetch('api/User/me')
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
console.log(response)
|
||||||
|
})
|
||||||
|
.then(userNameResponse => {
|
||||||
|
const userName = userNameResponse && userNameResponse.length > 0 ? userNameResponse : null;
|
||||||
|
setFetchedUser({
|
||||||
|
data: userName,
|
||||||
|
state: "finished"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setFetchedUser({
|
||||||
|
data: null,
|
||||||
|
state: "finished"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<UserContext.Provider value={fetchedUser}>
|
||||||
<Header/>
|
<Header/>
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
<RouterProvider router={router}/>
|
<RouterProvider router={router}/>
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</UserContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import {KarakterInputs} from "../domain-models/karakter";
|
import {KarakterInputs} from "../domain-models/karakter";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
export async function StoreNewCharacter(karakter: KarakterInputs) {
|
export async function StoreNewCharacter(karakter: KarakterInputs, isPublic: boolean = false) {
|
||||||
let response = await axios.post(`${window.location.origin}/api/Character/`, karakter, {
|
let response = await axios.post(`${window.location.origin}/api/Character/`, karakter, {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
|
params: {
|
||||||
|
isPublic,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (response.status < 300){
|
if (response.status < 300){
|
||||||
return response.data as string
|
return response.data as string
|
||||||
@@ -13,9 +16,12 @@ export async function StoreNewCharacter(karakter: KarakterInputs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function UpdateCharacter(id: string, karakter: KarakterInputs) {
|
export async function UpdateCharacter(id: string, karakter: KarakterInputs, isPublic: boolean = false) {
|
||||||
let response = await axios.post(`${window.location.origin}/api/Character/${id}`, karakter, {
|
let response = await axios.post(`${window.location.origin}/api/Character/${id}`, karakter, {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
|
params: {
|
||||||
|
isPublic,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (response.status < 300){
|
if (response.status < 300){
|
||||||
return response.data as string
|
return response.data as string
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {useState} from 'react';
|
import React, {useContext, useState} from 'react';
|
||||||
import './CreateCharacter.css'
|
import './CreateCharacter.css'
|
||||||
import {Faj} from "../domain-models/faj";
|
import {Faj} from "../domain-models/faj";
|
||||||
import {Osztaly, SetFelszerelesForChangedOsztaly} from "../domain-models/osztaly"
|
import {Osztaly, SetFelszerelesForChangedOsztaly} from "../domain-models/osztaly"
|
||||||
@@ -31,14 +31,16 @@ import {Faro} from "@grafana/faro-web-sdk";
|
|||||||
import {StoreNewCharacter, UpdateCharacter} from "../api/character.api";
|
import {StoreNewCharacter, UpdateCharacter} from "../api/character.api";
|
||||||
import {useLoaderData, useParams} from "react-router-dom";
|
import {useLoaderData, useParams} from "react-router-dom";
|
||||||
import saveOverlayTooltip from "../components/SaveOverlayTooltip";
|
import saveOverlayTooltip from "../components/SaveOverlayTooltip";
|
||||||
|
import {UserContext} from "../../shared/contexts/UserContext";
|
||||||
|
|
||||||
function CreateCharacterPage(props: {
|
function CreateCharacterPage(props: {
|
||||||
faro?: Faro,
|
faro?: Faro,
|
||||||
}) {
|
}) {
|
||||||
const {faro} = props
|
const {faro} = props
|
||||||
|
|
||||||
const initialKarakterInputs = useLoaderData() as KarakterInputs;
|
const { isPublic: initialIsPublic, ...initialKarakterInputs} = useLoaderData() as KarakterInputs & { isPublic: boolean };
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const fetchedUser = useContext(UserContext);
|
||||||
|
|
||||||
let [karakter, changeKarakter] = useState(initialKarakterInputs ?? KarakterDefaults)
|
let [karakter, changeKarakter] = useState(initialKarakterInputs ?? KarakterDefaults)
|
||||||
|
|
||||||
@@ -58,8 +60,30 @@ function CreateCharacterPage(props: {
|
|||||||
const canLevelUp = CanLevelUp(karakter)
|
const canLevelUp = CanLevelUp(karakter)
|
||||||
|
|
||||||
let [showSaved, setShowSaved] = useState(false);
|
let [showSaved, setShowSaved] = useState(false);
|
||||||
|
let [isPublic, setIsPublic] = useState(initialIsPublic);
|
||||||
|
|
||||||
const hideSaved = () => setShowSaved(false);
|
const hideSaved = () => setShowSaved(false);
|
||||||
|
const onSaveClicked = async () => {
|
||||||
|
faro?.api.pushEvent('character_stored', {
|
||||||
|
osztaly: karakter.osztaly,
|
||||||
|
szint: karakter.szint.toString(),
|
||||||
|
faj: karakter.faj.toString(),
|
||||||
|
is_public: isPublic.toString(),
|
||||||
|
})
|
||||||
|
if (id == null) {
|
||||||
|
let newId = await StoreNewCharacter(karakter, isPublic);
|
||||||
|
const win = window.open(`/${newId}`, '_blank');
|
||||||
|
if (win != null) {
|
||||||
|
win.focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await UpdateCharacter(id, karakter, isPublic);
|
||||||
|
setShowSaved(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
hideSaved();
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -182,32 +206,34 @@ function CreateCharacterPage(props: {
|
|||||||
<Felszereles osztaly={karakter.osztaly} felszereles={karakter.felszereles}
|
<Felszereles osztaly={karakter.osztaly} felszereles={karakter.felszereles}
|
||||||
changeFelszereles={setFelszereles} harcosSpec={karakter.harcosSpecializaciok}/>
|
changeFelszereles={setFelszereles} harcosSpec={karakter.harcosSpecializaciok}/>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
<div className='row'>
|
||||||
|
<h5 className='col align-self-center'>Véglegesítés</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fetchedUser.data != null &&
|
||||||
|
<div className='row m-2'>
|
||||||
|
<label className='col-md-2 col-sm-3 col-form-label'>Publikus karakterlap?</label>
|
||||||
|
<select className="col form-select" value={isPublic.toString()}
|
||||||
|
onChange={(event) => setIsPublic(event.target.value === "true")}>
|
||||||
|
<option value="true">Igen</option>
|
||||||
|
<option value="false">Nem</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-6">
|
<div className="col-6">
|
||||||
<div className='d-grid gap-2 m-5'>
|
<div className='d-grid gap-2 m-5'>
|
||||||
<OverlayTrigger placement='top' overlay={saveOverlayTooltip} delay={0} defaultShow={false} flip={false}>
|
{fetchedUser.data == null
|
||||||
<button className='btn btn-danger btn-lg' type='button' onClick={async () => {
|
? <OverlayTrigger placement='top' overlay={saveOverlayTooltip} delay={0}
|
||||||
faro?.api.pushEvent('public_character_stored', {
|
defaultShow={false} flip={false}>
|
||||||
osztaly: karakter.osztaly,
|
<button className='btn btn-danger btn-lg' type='button'
|
||||||
szint: karakter.szint.toString(),
|
onClick={onSaveClicked}>Mentés
|
||||||
faj: karakter.faj.toString(),
|
</button>
|
||||||
})
|
</OverlayTrigger>
|
||||||
if (id == null) {
|
: <button className='btn btn-danger btn-lg' type='button'
|
||||||
let newId = await StoreNewCharacter(karakter);
|
onClick={onSaveClicked}>Mentés</button>
|
||||||
const win = window.open(`/${newId}`, '_blank');
|
}
|
||||||
if (win != null) {
|
|
||||||
win.focus();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await UpdateCharacter(id, karakter);
|
|
||||||
setShowSaved(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
hideSaved();
|
|
||||||
}, 5000)
|
|
||||||
}
|
|
||||||
}}>Mentés
|
|
||||||
</button>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-6">
|
<div className="col-6">
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useState} from "react";
|
import {useContext} from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {Container, Nav, Navbar} from "react-bootstrap";
|
import {Container, Nav, Navbar} from "react-bootstrap";
|
||||||
|
import { UserContext } from "./contexts/UserContext";
|
||||||
type LoadingState = "not-started" | "loading" | "finished"
|
|
||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
await axios.post("/Identity/Account/Logout")
|
await axios.post("/Identity/Account/Logout")
|
||||||
@@ -11,21 +10,8 @@ async function logout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Header(props: {}) {
|
function Header(props: {}) {
|
||||||
|
|
||||||
let [loading, setLoading] = useState("not-started" as LoadingState);
|
|
||||||
let [userName, setUserName] = useState(null as string | null);
|
|
||||||
|
|
||||||
if (loading === "not-started") {
|
const fetchedUser = useContext(UserContext);
|
||||||
setLoading("loading");
|
|
||||||
fetch('api/User/me')
|
|
||||||
.then(response => {
|
|
||||||
response.ok
|
|
||||||
? response.text()
|
|
||||||
.then(userName => userName?.length > 0 ? setUserName(userName) : setUserName(null))
|
|
||||||
: console.log(response)
|
|
||||||
})
|
|
||||||
.finally(() => setLoading("finished"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return <header>
|
return <header>
|
||||||
<Navbar expand="sm" className="navbar-light bg-white border-bottom box-shadow mb-3">
|
<Navbar expand="sm" className="navbar-light bg-white border-bottom box-shadow mb-3">
|
||||||
@@ -35,8 +21,8 @@ function Header(props: {}) {
|
|||||||
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
||||||
<Navbar.Collapse id="basic-navbar-nav" className="d-sm-inline-flex flex-sm-row-reverse">
|
<Navbar.Collapse id="basic-navbar-nav" className="d-sm-inline-flex flex-sm-row-reverse">
|
||||||
<Nav>
|
<Nav>
|
||||||
{loading !== "finished" ? "Loading..." :
|
{fetchedUser.state !== "finished" ? "Loading..." :
|
||||||
userName == null
|
fetchedUser.data == null
|
||||||
? <>
|
? <>
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<a className="nav-link text-dark" href="/Identity/Account/Register">Register</a>
|
<a className="nav-link text-dark" href="/Identity/Account/Register">Register</a>
|
||||||
@@ -48,7 +34,7 @@ function Header(props: {}) {
|
|||||||
: <>
|
: <>
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<a className="nav-link text-dark"
|
<a className="nav-link text-dark"
|
||||||
href="/Identity/Account/Manage">Hello {userName}!</a>
|
href="/Identity/Account/Manage">Hello {fetchedUser.data}!</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<button className="nav-link nav text-dark" onClick={logout}>Logout</button>
|
<button className="nav-link nav text-dark" onClick={logout}>Logout</button>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Fetched } from "../models/Fetched";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export const userDefaults: Fetched<string | null> = {
|
||||||
|
state: "not-started",
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserContext = React.createContext(userDefaults)
|
||||||
|
UserContext.displayName = "UserEmailContext";
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export type LoadingState = "not-started" | "loading" | "finished"
|
||||||
|
|
||||||
|
export interface Fetched<T> {
|
||||||
|
data: T;
|
||||||
|
state: LoadingState;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user