Änderungen am Msessage System, Datenschutz-Einstellungen hinzugefügt, BDSM und CardLock Game weiterverfeinert
This commit is contained in:
@@ -23,7 +23,9 @@
|
|||||||
"Bash(./gradlew compileJava -q 2>&1 | tail -30)",
|
"Bash(./gradlew compileJava -q 2>&1 | tail -30)",
|
||||||
"Bash(ls -lah /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/*.java)",
|
"Bash(ls -lah /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/*.java)",
|
||||||
"Bash(for f:*)",
|
"Bash(for f:*)",
|
||||||
"Bash(ls:*)"
|
"Bash(ls:*)",
|
||||||
|
"Bash(./gradlew compileJava)",
|
||||||
|
"Bash(./gradlew build:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Tue Mar 17 19:55:50 CET 2026
|
#Wed Mar 18 15:28:58 CET 2026
|
||||||
display=\:0
|
display=\:0
|
||||||
host=Mario-Linux
|
host=Mario-Linux
|
||||||
process-id=148721
|
process-id=26624
|
||||||
user=mario
|
user=mario
|
||||||
|
|||||||
970
.metadata/.log
970
.metadata/.log
@@ -1097,3 +1097,973 @@ Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git
|
|||||||
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
||||||
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
||||||
... 68 more
|
... 68 more
|
||||||
|
|
||||||
|
!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-17 23:01:40.053
|
||||||
|
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-17 23:01:40.298
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
!SESSION 2026-03-18 15:28:52.788 -----------------------------------------------
|
||||||
|
eclipse.buildId=4.39.0.20260305-0817
|
||||||
|
java.version=21.0.10
|
||||||
|
java.vendor=Eclipse Adoptium
|
||||||
|
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=de_DE
|
||||||
|
Framework arguments: -product org.eclipse.epp.package.java.product
|
||||||
|
Command-line arguments: -os linux -ws gtk -arch x86_64 -product org.eclipse.epp.package.java.product
|
||||||
|
|
||||||
|
!ENTRY ch.qos.logback.classic 1 0 2026-03-18 15:28:53.799
|
||||||
|
!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized.
|
||||||
|
|
||||||
|
!ENTRY ch.qos.logback.classic 1 0 2026-03-18 15:28:58.414
|
||||||
|
!MESSAGE Logback config file: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.7.101.20251017-1242.xml
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui 2 0 2026-03-18 15:28:58.572
|
||||||
|
!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points.
|
||||||
|
!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-18 15:28:58.572
|
||||||
|
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui 2 0 2026-03-18 15:28:58.702
|
||||||
|
!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points.
|
||||||
|
!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-18 15:28:58.702
|
||||||
|
!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory'
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 16:50:37.017
|
||||||
|
!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception
|
||||||
|
!STACK 0
|
||||||
|
org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden))
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4950)
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4865)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57)
|
||||||
|
at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194)
|
||||||
|
at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
|
||||||
|
at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
|
||||||
|
at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136)
|
||||||
|
at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950)
|
||||||
|
at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
|
||||||
|
at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
|
||||||
|
at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48)
|
||||||
|
at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61)
|
||||||
|
at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
|
||||||
|
at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111)
|
||||||
|
at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89)
|
||||||
|
at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467)
|
||||||
|
at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493)
|
||||||
|
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297)
|
||||||
|
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
|
||||||
|
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661)
|
||||||
|
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290)
|
||||||
|
at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102)
|
||||||
|
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
|
||||||
|
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
|
||||||
|
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
|
||||||
|
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
|
||||||
|
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
|
||||||
|
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
|
||||||
|
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
|
||||||
|
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
|
||||||
|
Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
||||||
|
... 68 more
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 17:25:44.906
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 17:33:38.648
|
||||||
|
!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception
|
||||||
|
!STACK 0
|
||||||
|
org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden))
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4950)
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4865)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57)
|
||||||
|
at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194)
|
||||||
|
at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
|
||||||
|
at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
|
||||||
|
at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136)
|
||||||
|
at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950)
|
||||||
|
at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
|
||||||
|
at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
|
||||||
|
at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48)
|
||||||
|
at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61)
|
||||||
|
at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
|
||||||
|
at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111)
|
||||||
|
at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89)
|
||||||
|
at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467)
|
||||||
|
at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493)
|
||||||
|
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297)
|
||||||
|
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
|
||||||
|
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661)
|
||||||
|
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290)
|
||||||
|
at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102)
|
||||||
|
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
|
||||||
|
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
|
||||||
|
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
|
||||||
|
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
|
||||||
|
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
|
||||||
|
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
|
||||||
|
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
|
||||||
|
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
|
||||||
|
Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
||||||
|
... 68 more
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 17:35:19.625
|
||||||
|
!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception
|
||||||
|
!STACK 0
|
||||||
|
org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden))
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4950)
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4865)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57)
|
||||||
|
at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194)
|
||||||
|
at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
|
||||||
|
at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
|
||||||
|
at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136)
|
||||||
|
at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950)
|
||||||
|
at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
|
||||||
|
at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
|
||||||
|
at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48)
|
||||||
|
at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61)
|
||||||
|
at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
|
||||||
|
at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111)
|
||||||
|
at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89)
|
||||||
|
at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467)
|
||||||
|
at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493)
|
||||||
|
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297)
|
||||||
|
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
|
||||||
|
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661)
|
||||||
|
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290)
|
||||||
|
at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102)
|
||||||
|
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
|
||||||
|
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
|
||||||
|
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
|
||||||
|
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
|
||||||
|
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
|
||||||
|
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
|
||||||
|
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
|
||||||
|
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
|
||||||
|
Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
||||||
|
... 68 more
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:02:17.714
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jface 2 0 2026-03-18 18:11:03.312
|
||||||
|
!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation.
|
||||||
|
!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-18 18:11:03.312
|
||||||
|
!MESSAGE A conflict occurred for CTRL+SHIFT+T:
|
||||||
|
Binding(CTRL+SHIFT+T,
|
||||||
|
ParameterizedCommand(Command(org.eclipse.jdt.ui.navigate.open.type,Open Type,
|
||||||
|
Open a type in a Java editor,
|
||||||
|
Category(org.eclipse.ui.category.navigate,Navigate,null,true),
|
||||||
|
WorkbenchHandlerServiceHandler("org.eclipse.jdt.ui.navigate.open.type"),
|
||||||
|
,,true),null),
|
||||||
|
org.eclipse.ui.defaultAcceleratorConfiguration,
|
||||||
|
org.eclipse.ui.contexts.window,,,system)
|
||||||
|
Binding(CTRL+SHIFT+T,
|
||||||
|
ParameterizedCommand(Command(org.eclipse.lsp4e.symbolInWorkspace,Go to Symbol in Workspace,
|
||||||
|
,
|
||||||
|
Category(org.eclipse.lsp4e.category,Language Servers,null,true),
|
||||||
|
WorkbenchHandlerServiceHandler("org.eclipse.lsp4e.symbolInWorkspace"),
|
||||||
|
,,true),null),
|
||||||
|
org.eclipse.ui.defaultAcceleratorConfiguration,
|
||||||
|
org.eclipse.ui.contexts.window,,,system)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.lsp4e 2 0 2026-03-18 18:12:46.286
|
||||||
|
!MESSAGE Javadoc unavailable. Failed to obtain it.
|
||||||
|
!STACK 0
|
||||||
|
java.lang.InterruptedException
|
||||||
|
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:386)
|
||||||
|
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096)
|
||||||
|
at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.getHoverInfo2(LSJavaHoverProvider.java:66)
|
||||||
|
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:165)
|
||||||
|
at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:131)
|
||||||
|
at org.eclipse.jdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo2(JavaEditorTextHoverProxy.java:89)
|
||||||
|
at org.eclipse.jface.text.TextViewerHoverManager$1.run(TextViewerHoverManager.java:155)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:12:57.074
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:13:45.585
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:25:16.508
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:46:49.386
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:48:21.541
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:48:31.516
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:49:49.718
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:57:46.512
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:16:53.710
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:30:00.005
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 19:30:35.497
|
||||||
|
!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception
|
||||||
|
!STACK 0
|
||||||
|
org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden))
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4950)
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4865)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57)
|
||||||
|
at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194)
|
||||||
|
at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
|
||||||
|
at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
|
||||||
|
at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136)
|
||||||
|
at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950)
|
||||||
|
at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
|
||||||
|
at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
|
||||||
|
at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48)
|
||||||
|
at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61)
|
||||||
|
at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
|
||||||
|
at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111)
|
||||||
|
at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89)
|
||||||
|
at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467)
|
||||||
|
at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493)
|
||||||
|
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297)
|
||||||
|
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
|
||||||
|
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661)
|
||||||
|
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290)
|
||||||
|
at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102)
|
||||||
|
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
|
||||||
|
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
|
||||||
|
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
|
||||||
|
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
|
||||||
|
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
|
||||||
|
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
|
||||||
|
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
|
||||||
|
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
|
||||||
|
Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
||||||
|
... 68 more
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:32:18.384
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:32:45.429
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 19:33:19.167
|
||||||
|
!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception
|
||||||
|
!STACK 0
|
||||||
|
org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden))
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4950)
|
||||||
|
at org.eclipse.swt.SWT.error(SWT.java:4865)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38)
|
||||||
|
at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83)
|
||||||
|
at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57)
|
||||||
|
at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194)
|
||||||
|
at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804)
|
||||||
|
at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
|
||||||
|
at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183)
|
||||||
|
at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136)
|
||||||
|
at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950)
|
||||||
|
at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34)
|
||||||
|
at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201)
|
||||||
|
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
|
||||||
|
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
|
||||||
|
at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73)
|
||||||
|
at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48)
|
||||||
|
at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61)
|
||||||
|
at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
|
||||||
|
at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243)
|
||||||
|
at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111)
|
||||||
|
at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89)
|
||||||
|
at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467)
|
||||||
|
at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493)
|
||||||
|
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297)
|
||||||
|
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91)
|
||||||
|
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682)
|
||||||
|
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661)
|
||||||
|
at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290)
|
||||||
|
at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102)
|
||||||
|
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051)
|
||||||
|
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153)
|
||||||
|
at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684)
|
||||||
|
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
|
||||||
|
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583)
|
||||||
|
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173)
|
||||||
|
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185)
|
||||||
|
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149)
|
||||||
|
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467)
|
||||||
|
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
|
||||||
|
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615)
|
||||||
|
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563)
|
||||||
|
at org.eclipse.equinox.launcher.Main.run(Main.java:1415)
|
||||||
|
at org.eclipse.equinox.launcher.Main.main(Main.java:1387)
|
||||||
|
Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:106)
|
||||||
|
at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204)
|
||||||
|
... 68 more
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:34:29.072
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:39:46.763
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:46:32.144
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:50:39.778
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:57:10.404
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 20:08:07.916
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 20:08:34.316
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 20:50:39.209
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 21:02:50.177
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 21:02:51.971
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:02:40.121
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:21:25.529
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:34:27.436
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|
||||||
|
!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-18 22:34:35.489
|
||||||
|
!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS
|
||||||
|
|
||||||
|
!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:34:35.761
|
||||||
|
!MESSAGE Failed to save JDT index: Index for /xxxthegame
|
||||||
|
!STACK 0
|
||||||
|
java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden)
|
||||||
|
at java.base/java.io.FileInputStream.open0(Native Method)
|
||||||
|
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
|
||||||
|
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
|
||||||
|
at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633)
|
||||||
|
at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536)
|
||||||
|
at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178)
|
||||||
|
at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822)
|
||||||
|
at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508)
|
||||||
|
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,105 +1,106 @@
|
|||||||
INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core
|
INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core
|
||||||
2628068441.index
|
|
||||||
2403041570.index
|
|
||||||
1704193220.index
|
|
||||||
3051047092.index
|
|
||||||
2466743981.index
|
|
||||||
2852275968.index
|
|
||||||
3371017197.index
|
|
||||||
2376429633.index
|
|
||||||
2236377038.index
|
|
||||||
833027591.index
|
|
||||||
4080887926.index
|
|
||||||
3424266581.index
|
|
||||||
2488355463.index
|
|
||||||
3514351073.index
|
|
||||||
1090991043.index
|
|
||||||
13999064.index
|
|
||||||
352173590.index
|
|
||||||
2799433906.index
|
|
||||||
3718169413.index
|
|
||||||
2157310342.index
|
|
||||||
1730373086.index
|
|
||||||
341080888.index
|
|
||||||
3839581777.index
|
|
||||||
3572667491.index
|
|
||||||
2826242951.index
|
|
||||||
2874180664.index
|
|
||||||
134995224.index
|
|
||||||
2455962971.index
|
|
||||||
1063231598.index
|
|
||||||
3769604005.index
|
|
||||||
794464160.index
|
794464160.index
|
||||||
1067882983.index
|
2655170954.index
|
||||||
3547251881.index
|
176453541.index
|
||||||
1022297761.index
|
677104696.index
|
||||||
|
341080888.index
|
||||||
|
774576701.index
|
||||||
4134502745.index
|
4134502745.index
|
||||||
1780956574.index
|
41199409.index
|
||||||
369020172.index
|
134995224.index
|
||||||
2217896880.index
|
2217896880.index
|
||||||
|
4025319337.index
|
||||||
|
900586112.index
|
||||||
|
2929476459.index
|
||||||
|
2065500052.index
|
||||||
|
3051047092.index
|
||||||
|
815902026.index
|
||||||
|
3718169413.index
|
||||||
|
721517855.index
|
||||||
|
369020172.index
|
||||||
|
3899935016.index
|
||||||
|
2157310342.index
|
||||||
|
2488355463.index
|
||||||
|
3572667491.index
|
||||||
|
2799433906.index
|
||||||
|
675283020.index
|
||||||
|
2032345814.index
|
||||||
|
3839581777.index
|
||||||
|
2466743981.index
|
||||||
|
13999064.index
|
||||||
|
673436610.index
|
||||||
|
3972616808.index
|
||||||
|
1914043487.index
|
||||||
|
3154281632.index
|
||||||
|
1117161889.index
|
||||||
|
983587063.index
|
||||||
|
766461225.index
|
||||||
|
286641703.index
|
||||||
|
3371017197.index
|
||||||
|
4080887926.index
|
||||||
|
2941512597.index
|
||||||
|
1730373086.index
|
||||||
|
3882180612.index
|
||||||
|
4020783879.index
|
||||||
|
2900482015.index
|
||||||
|
3059431983.index
|
||||||
|
833027591.index
|
||||||
|
13156219.index
|
||||||
|
4088356365.index
|
||||||
|
37241354.index
|
||||||
|
1295630681.index
|
||||||
|
2701419231.index
|
||||||
|
3939420913.index
|
||||||
|
1067882983.index
|
||||||
|
1318022262.index
|
||||||
|
773718761.index
|
||||||
|
2311226047.index
|
||||||
|
3539841425.index
|
||||||
|
1865797976.index
|
||||||
|
2455962971.index
|
||||||
|
836138551.index
|
||||||
|
2389383899.index
|
||||||
|
2226615777.index
|
||||||
|
3515611559.index
|
||||||
|
3728851734.index
|
||||||
|
2826242951.index
|
||||||
|
2899155238.index
|
||||||
|
3763224039.index
|
||||||
|
2138052223.index
|
||||||
|
2236377038.index
|
||||||
|
3547251881.index
|
||||||
371677185.index
|
371677185.index
|
||||||
2127778675.index
|
2127778675.index
|
||||||
2389383899.index
|
2519831052.index
|
||||||
2701419231.index
|
1063231598.index
|
||||||
|
2874180664.index
|
||||||
|
2939623059.index
|
||||||
|
2576972120.index
|
||||||
|
2376429633.index
|
||||||
|
2628068441.index
|
||||||
|
1090991043.index
|
||||||
|
1138623861.index
|
||||||
|
1223891870.index
|
||||||
|
3769604005.index
|
||||||
|
3158780236.index
|
||||||
|
2237645717.index
|
||||||
|
2852275968.index
|
||||||
|
2403041570.index
|
||||||
|
1704193220.index
|
||||||
|
2004806901.index
|
||||||
|
3952767374.index
|
||||||
3416862923.index
|
3416862923.index
|
||||||
3912907421.index
|
3912907421.index
|
||||||
1256436118.index
|
|
||||||
815902026.index
|
|
||||||
900586112.index
|
|
||||||
766461225.index
|
|
||||||
1117161889.index
|
|
||||||
675283020.index
|
|
||||||
4088356365.index
|
|
||||||
836138551.index
|
|
||||||
2226615777.index
|
|
||||||
3539841425.index
|
|
||||||
2939623059.index
|
|
||||||
3728851734.index
|
|
||||||
3972616808.index
|
|
||||||
2494834982.index
|
|
||||||
1938594271.index
|
|
||||||
4025319337.index
|
|
||||||
781064456.index
|
781064456.index
|
||||||
2032345814.index
|
352173590.index
|
||||||
2655170954.index
|
|
||||||
983587063.index
|
|
||||||
3939420913.index
|
|
||||||
2247053514.index
|
|
||||||
1138623861.index
|
|
||||||
3882180612.index
|
|
||||||
2237645717.index
|
|
||||||
721517855.index
|
|
||||||
176453541.index
|
|
||||||
4020783879.index
|
|
||||||
3899935016.index
|
|
||||||
2576972120.index
|
|
||||||
1223891870.index
|
|
||||||
3158780236.index
|
|
||||||
677104696.index
|
|
||||||
766439048.index
|
766439048.index
|
||||||
41199409.index
|
3424266581.index
|
||||||
2900482015.index
|
2247053514.index
|
||||||
3952767374.index
|
1765772496.index
|
||||||
773718761.index
|
3514351073.index
|
||||||
2519831052.index
|
|
||||||
286641703.index
|
|
||||||
3515611559.index
|
|
||||||
1865797976.index
|
|
||||||
3059431983.index
|
|
||||||
2929476459.index
|
|
||||||
774576701.index
|
|
||||||
13156219.index
|
|
||||||
2311226047.index
|
|
||||||
2138052223.index
|
|
||||||
3763224039.index
|
|
||||||
3154281632.index
|
|
||||||
1318022262.index
|
|
||||||
2065500052.index
|
|
||||||
37241354.index
|
|
||||||
2899155238.index
|
|
||||||
673436610.index
|
|
||||||
1914043487.index
|
|
||||||
1295630681.index
|
|
||||||
2941512597.index
|
|
||||||
3892622621.index
|
3892622621.index
|
||||||
2004806901.index
|
2494834982.index
|
||||||
|
1780956574.index
|
||||||
|
1022297761.index
|
||||||
|
1938594271.index
|
||||||
|
1256436118.index
|
||||||
|
|||||||
@@ -18,4 +18,5 @@
|
|||||||
<fullyQualifiedTypeName name="lombok.Getter"/>
|
<fullyQualifiedTypeName name="lombok.Getter"/>
|
||||||
<fullyQualifiedTypeName name="lombok.Setter"/>
|
<fullyQualifiedTypeName name="lombok.Setter"/>
|
||||||
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.Test"/>
|
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.Test"/>
|
||||||
|
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.KeyholderCardLock"/>
|
||||||
</qualifiedTypeNameHistroy>
|
</qualifiedTypeNameHistroy>
|
||||||
|
|||||||
@@ -24,6 +24,10 @@
|
|||||||
<section name="RenameInformationPopup">
|
<section name="RenameInformationPopup">
|
||||||
</section>
|
</section>
|
||||||
<section name="org.eclipse.ltk.ui.refactoring.settings">
|
<section name="org.eclipse.ltk.ui.refactoring.settings">
|
||||||
|
<item key="renameSubpackages" value="false"/>
|
||||||
|
<item key="updateTextualMatches" value="false"/>
|
||||||
|
<item key="updateQualifiedNames" value="false"/>
|
||||||
|
<item key="patterns" value="*"/>
|
||||||
</section>
|
</section>
|
||||||
<section name="org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog2">
|
<section name="org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog2">
|
||||||
<item key="ShowStatusLine" value="true"/>
|
<item key="ShowStatusLine" value="true"/>
|
||||||
@@ -68,4 +72,13 @@
|
|||||||
<section name="BuildPathsPropertyPage">
|
<section name="BuildPathsPropertyPage">
|
||||||
<item key="pageIndex" value="3"/>
|
<item key="pageIndex" value="3"/>
|
||||||
</section>
|
</section>
|
||||||
|
<section name="org.eclipse.jdt.internal.ui.typehierarchy.QuickHierarchy">
|
||||||
|
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_WIDTH" value="400"/>
|
||||||
|
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_HEIGHT" value="346"/>
|
||||||
|
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_USE_PERSISTED_SIZE" value="true"/>
|
||||||
|
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_USE_PERSISTED_LOCATION" value="false"/>
|
||||||
|
</section>
|
||||||
|
<section name="NewPackageWizardPage">
|
||||||
|
<item key="create_package_info_java" value="false"/>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -21,3 +21,4 @@
|
|||||||
2026-03-17 19:39:55,931 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
|
2026-03-17 19:39:55,931 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
|
||||||
2026-03-17 19:49:51,508 [Worker-2: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
2026-03-17 19:49:51,508 [Worker-2: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||||
2026-03-17 19:55:53,050 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
2026-03-17 19:55:53,050 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||||
|
2026-03-18 15:29:01,371 [Worker-7: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<item key="DIALOG_X_ORIGIN" value="20"/>
|
<item key="DIALOG_X_ORIGIN" value="20"/>
|
||||||
<item key="DIALOG_Y_ORIGIN" value="20"/>
|
<item key="DIALOG_Y_ORIGIN" value="20"/>
|
||||||
<item key="DIALOG_WIDTH" value="874"/>
|
<item key="DIALOG_WIDTH" value="874"/>
|
||||||
<item key="DIALOG_HEIGHT" value="995"/>
|
<item key="DIALOG_HEIGHT" value="1050"/>
|
||||||
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
|
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
|
||||||
</section>
|
</section>
|
||||||
<section name="ImportExportAction">
|
<section name="ImportExportAction">
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#Tue Mar 17 19:55:50 CET 2026
|
#Wed Mar 18 15:28:58 CET 2026
|
||||||
org.eclipse.core.runtime=2
|
org.eclipse.core.runtime=2
|
||||||
org.eclipse.platform=4.39.0.v20260226-0420
|
org.eclipse.platform=4.39.0.v20260226-0420
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.oaa.xxx.aufgaben;
|
package de.oaa.xxx.aufgaben;
|
||||||
|
|
||||||
import de.oaa.xxx.session.GeschlechtEnum;
|
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ public class AufgabeEntity {
|
|||||||
public Aufgabe toAufgabe() {
|
public Aufgabe toAufgabe() {
|
||||||
Aufgabe aufgabe = new Aufgabe();
|
Aufgabe aufgabe = new Aufgabe();
|
||||||
aufgabe.setAufgabeId(aufgabeId);
|
aufgabe.setAufgabeId(aufgabeId);
|
||||||
aufgabe.setBenoetigtAktiv(benoetigtAktiv);
|
aufgabe.setBenoetigtAktiv(benoetigtAktiv != null ? new ArrayList<>(benoetigtAktiv) : new ArrayList<>());
|
||||||
aufgabe.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList());
|
aufgabe.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>());
|
||||||
aufgabe.setBenoetigtPassiv(benoetigtPassiv);
|
aufgabe.setBenoetigtPassiv(benoetigtPassiv != null ? new ArrayList<>(benoetigtPassiv) : new ArrayList<>());
|
||||||
aufgabe.setGruppeId(aufgabenGruppe.getGruppenId());
|
aufgabe.setGruppeId(aufgabenGruppe.getGruppenId());
|
||||||
aufgabe.setKurzText(kurzText);
|
aufgabe.setKurzText(kurzText);
|
||||||
aufgabe.setLevel(level);
|
aufgabe.setLevel(level);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import java.util.UUID;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "aufgabenGruppe")
|
@Table(name = "aufgaben_gruppe")
|
||||||
public class AufgabenGruppeEntity {
|
public class AufgabenGruppeEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package de.oaa.xxx.aufgaben.entity;
|
|||||||
|
|
||||||
import de.oaa.xxx.aufgaben.Finisher;
|
import de.oaa.xxx.aufgaben.Finisher;
|
||||||
import de.oaa.xxx.aufgaben.Werkzeug;
|
import de.oaa.xxx.aufgaben.Werkzeug;
|
||||||
import de.oaa.xxx.session.GeschlechtEnum;
|
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.CollectionTable;
|
import jakarta.persistence.CollectionTable;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
@@ -67,9 +67,9 @@ public class FinisherEntity {
|
|||||||
finisher.setKurzText(kurzText);
|
finisher.setKurzText(kurzText);
|
||||||
finisher.setText(text);
|
finisher.setText(text);
|
||||||
finisher.setGeschlecht(geschlecht);
|
finisher.setGeschlecht(geschlecht);
|
||||||
finisher.setBenoetigtAktiv(benoetigtAktiv);
|
finisher.setBenoetigtAktiv(benoetigtAktiv != null ? new ArrayList<>(benoetigtAktiv) : new ArrayList<>());
|
||||||
finisher.setBenoetigtPassiv(benoetigtPassiv);
|
finisher.setBenoetigtPassiv(benoetigtPassiv != null ? new ArrayList<>(benoetigtPassiv) : new ArrayList<>());
|
||||||
finisher.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList());
|
finisher.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>());
|
||||||
finisher.setGruppeId(aufgabenGruppe.getGruppenId());
|
finisher.setGruppeId(aufgabenGruppe.getGruppenId());
|
||||||
return finisher;
|
return finisher;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class SperreEntity {
|
|||||||
sperre.setMinutenBis(minutenBis);
|
sperre.setMinutenBis(minutenBis);
|
||||||
sperre.setMinutenVon(minutenVon);
|
sperre.setMinutenVon(minutenVon);
|
||||||
sperre.setReleaseText(releaseText);
|
sperre.setReleaseText(releaseText);
|
||||||
sperre.setSperreFuer(sperreFuer);
|
sperre.setSperreFuer(sperreFuer != null ? new ArrayList<>(sperreFuer) : new ArrayList<>());
|
||||||
sperre.setText(text);
|
sperre.setText(text);
|
||||||
sperre.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>());
|
sperre.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>());
|
||||||
return sperre;
|
return sperre;
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ public class StrafeEntity {
|
|||||||
public Strafe toStrafe() {
|
public Strafe toStrafe() {
|
||||||
Strafe strafe = new Strafe();
|
Strafe strafe = new Strafe();
|
||||||
strafe.setStrafeId(strafeId);
|
strafe.setStrafeId(strafeId);
|
||||||
strafe.setBenoetigtAktiv(benoetigtAktiv);
|
strafe.setBenoetigtAktiv(benoetigtAktiv != null ? new ArrayList<>(benoetigtAktiv) : new ArrayList<>());
|
||||||
strafe.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList());
|
strafe.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>());
|
||||||
strafe.setBenoetigtPassiv(benoetigtPassiv);
|
strafe.setBenoetigtPassiv(benoetigtPassiv != null ? new ArrayList<>(benoetigtPassiv) : new ArrayList<>());
|
||||||
strafe.setGruppeId(aufgabenGruppe.getGruppenId());
|
strafe.setGruppeId(aufgabenGruppe.getGruppenId());
|
||||||
strafe.setKurzText(kurzText);
|
strafe.setKurzText(kurzText);
|
||||||
strafe.setLevel(level);
|
strafe.setLevel(level);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
import java.util.UUID;
|
||||||
@Setter
|
|
||||||
public class AufgabeAnzeige {
|
@Getter
|
||||||
|
@Setter
|
||||||
private String nameAktiverMitspieler;
|
public class AufgabeAnzeige {
|
||||||
private String aufgabeText;
|
|
||||||
private Integer timer;
|
private String nameAktiverMitspieler;
|
||||||
private Callback callback;
|
private String aufgabeText;
|
||||||
private Integer level;
|
private Integer timer;
|
||||||
|
private Callback callback;
|
||||||
@Override
|
private Integer level;
|
||||||
public String toString() {
|
private UUID mitspielerId;
|
||||||
return "AufgabeAnzeige[mitspieler=" + nameAktiverMitspieler + ", level=" + level + ", timer=" + timer
|
private boolean eigenesGeraet;
|
||||||
+ ", callback=" + (callback != null ? callback.getClass().getSimpleName() : null) + "]";
|
|
||||||
}
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
|
return "AufgabeAnzeige[mitspieler=" + nameAktiverMitspieler + ", level=" + level + ", timer=" + timer
|
||||||
|
+ ", callback=" + (callback != null ? callback.getClass().getSimpleName() : null) + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
public enum AufgabeArt {
|
public enum AufgabeArt {
|
||||||
AUFGABE,
|
AUFGABE,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -8,10 +8,11 @@ import java.util.UUID;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class Session {
|
public class BdsmGame {
|
||||||
|
|
||||||
private UUID sessionId;
|
private UUID sessionId;
|
||||||
private UUID userId;
|
private UUID userId;
|
||||||
|
private UUID setupId;
|
||||||
private Integer wahrscheinlichkeitSperre;
|
private Integer wahrscheinlichkeitSperre;
|
||||||
private Integer wahrscheinlichkeitStrafe;
|
private Integer wahrscheinlichkeitStrafe;
|
||||||
private Integer aufgabenProLevel;
|
private Integer aufgabenProLevel;
|
||||||
@@ -1,251 +1,265 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import de.oaa.xxx.session.aufgaben.Aufgabe;
|
import de.oaa.xxx.games.bdsm.aufgaben.Aufgabe;
|
||||||
import de.oaa.xxx.session.aufgaben.AufgabenList;
|
import de.oaa.xxx.games.bdsm.aufgaben.AufgabenList;
|
||||||
import de.oaa.xxx.session.aufgaben.Sperre;
|
import de.oaa.xxx.games.bdsm.aufgaben.Sperre;
|
||||||
import de.oaa.xxx.session.aufgaben.Strafe;
|
import de.oaa.xxx.games.bdsm.aufgaben.Strafe;
|
||||||
import de.oaa.xxx.session.entity.SessionEntity;
|
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
|
||||||
import de.oaa.xxx.session.sperre.SperreCallback;
|
import de.oaa.xxx.games.bdsm.sperre.SperreCallback;
|
||||||
import de.oaa.xxx.session.sperre.SperrenVerlaengernCallback;
|
import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback;
|
||||||
|
|
||||||
public class SessionDurchfuehren {
|
public class BdsmGameDurchfuehren {
|
||||||
|
|
||||||
private final AufgabenList aufgabenList;
|
private final AufgabenList aufgabenList;
|
||||||
private final List<Mitspieler> mitspieler = new ArrayList<>();
|
private final List<Mitspieler> mitspieler = new ArrayList<>();
|
||||||
private final List<AktiveSperre> aktiveSperren = new ArrayList<>();
|
private final List<AktiveSperre> aktiveSperren = new ArrayList<>();
|
||||||
|
|
||||||
private final Integer wahrscheinlichkeitSperre;
|
private final Integer wahrscheinlichkeitSperre;
|
||||||
private final Integer wahrscheinlichkeitStrafe;
|
private final Integer wahrscheinlichkeitStrafe;
|
||||||
|
|
||||||
private int aufgabenProLevel;
|
private int aufgabenProLevel;
|
||||||
private int level;
|
private int level;
|
||||||
private int aufgabenAufAktuellemLevel;
|
private int aufgabenAufAktuellemLevel;
|
||||||
|
|
||||||
public SessionDurchfuehren(SessionEntity entity) throws Exception {
|
public BdsmGameDurchfuehren(BdsmGameEntity entity) throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
aufgabenList = objectMapper.readValue(entity.getAufgaben(), AufgabenList.class);
|
aufgabenList = objectMapper.readValue(entity.getAufgaben(), AufgabenList.class);
|
||||||
entity.getMitspieler().forEach(mitspielerEntity -> mitspieler.add(mitspielerEntity.toMitspieler()));
|
entity.getMitspieler().forEach(mitspielerEntity -> mitspieler.add(mitspielerEntity.toMitspieler()));
|
||||||
entity.getAktiveSperren().forEach(sperreEntity -> aktiveSperren.add(sperreEntity.toSperre(mitspieler)));
|
entity.getAktiveSperren().forEach(sperreEntity -> aktiveSperren.add(sperreEntity.toSperre(mitspieler)));
|
||||||
|
|
||||||
wahrscheinlichkeitSperre = entity.getWahrscheinlichkeitSperre();
|
wahrscheinlichkeitSperre = entity.getWahrscheinlichkeitSperre();
|
||||||
wahrscheinlichkeitStrafe = entity.getWahrscheinlichkeitStrafe();
|
wahrscheinlichkeitStrafe = entity.getWahrscheinlichkeitStrafe();
|
||||||
|
|
||||||
this.aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5;
|
this.aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5;
|
||||||
this.level = entity.getLevel() != null ? entity.getLevel() : 1;
|
this.level = entity.getLevel() != null ? entity.getLevel() : 1;
|
||||||
this.aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0;
|
this.aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AufgabeAnzeige getNext() {
|
public AufgabeAnzeige getNext() {
|
||||||
checkLevel();
|
checkLevel();
|
||||||
if (level == 6) {
|
if (level == 6) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
AufgabeAnzeige anzeige = null;
|
AufgabeAnzeige anzeige = null;
|
||||||
int nextInt = new Random().nextInt(1, 100);
|
int nextInt = new Random().nextInt(1, 100);
|
||||||
if (nextInt == 1) {
|
if (nextInt == 1) {
|
||||||
anzeige = findUltimativeStrafe();
|
anzeige = findUltimativeStrafe();
|
||||||
} else if (nextInt == 2) {
|
} else if (nextInt == 2) {
|
||||||
anzeige = findSperreVerlaengern();
|
anzeige = findSperreVerlaengern();
|
||||||
} else if (nextInt > wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe + 2) {
|
} else if (nextInt > wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe + 2) {
|
||||||
anzeige = findeAufgabe();
|
anzeige = findeAufgabe();
|
||||||
} else if (nextInt > wahrscheinlichkeitSperre + 2) {
|
} else if (nextInt > wahrscheinlichkeitSperre + 2) {
|
||||||
anzeige = findeStrafe();
|
anzeige = findeStrafe();
|
||||||
} else {
|
} else {
|
||||||
anzeige = findeSperre();
|
anzeige = findeSperre();
|
||||||
}
|
}
|
||||||
if (anzeige == null) {
|
if (anzeige == null) {
|
||||||
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
|
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
|
||||||
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
|
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
|
||||||
String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting.";
|
String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting.";
|
||||||
anzeige = new AufgabeAnzeige();
|
anzeige = new AufgabeAnzeige();
|
||||||
anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : "");
|
anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : "");
|
||||||
anzeige.setAufgabeText(getAnzeigeText(text, aktiv != null ? aktiv.getName() : "?", passiv != null ? passiv.getName() : "?"));
|
setMitspielerInfo(anzeige, aktiv);
|
||||||
anzeige.setTimer(120);
|
anzeige.setAufgabeText(getAnzeigeText(text, aktiv != null ? aktiv.getName() : "?", passiv != null ? passiv.getName() : "?"));
|
||||||
}
|
anzeige.setTimer(120);
|
||||||
return anzeige;
|
}
|
||||||
}
|
return anzeige;
|
||||||
|
}
|
||||||
public void backToLvl5() {
|
|
||||||
this.level = 5;
|
public void backToLvl5() {
|
||||||
this.aufgabenAufAktuellemLevel = 0;
|
this.level = 5;
|
||||||
}
|
this.aufgabenAufAktuellemLevel = 0;
|
||||||
|
}
|
||||||
public List<AufgabeAnzeige> getFinisher() {
|
|
||||||
var list = new ArrayList<AufgabeAnzeige>();
|
public List<AufgabeAnzeige> getFinisher() {
|
||||||
List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> {
|
var list = new ArrayList<AufgabeAnzeige>();
|
||||||
mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> {
|
List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> {
|
||||||
var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming);
|
mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> {
|
||||||
var finishers = aufgabenList.getFinisher().stream()
|
var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming);
|
||||||
.filter(finisher -> geschlecht == finisher.getGeschlecht())
|
var finishers = aufgabenList.getFinisher().stream()
|
||||||
.toList();
|
.filter(finisher -> geschlecht == finisher.getGeschlecht())
|
||||||
if (!finishers.isEmpty()) {
|
.toList();
|
||||||
var aufgabe = finishers.get(new Random().nextInt(list.size()));
|
if (!finishers.isEmpty()) {
|
||||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
var aufgabe = finishers.get(new Random().nextInt(list.size()));
|
||||||
anzeige.setNameAktiverMitspieler(cumming.getName());
|
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||||
anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(),
|
anzeige.setNameAktiverMitspieler(cumming.getName());
|
||||||
cumming.getName(), partner != null ? partner.getName() : ""));
|
setMitspielerInfo(anzeige, cumming);
|
||||||
list.add(anzeige);
|
anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(),
|
||||||
}
|
cumming.getName(), partner != null ? partner.getName() : ""));
|
||||||
});
|
list.add(anzeige);
|
||||||
});
|
}
|
||||||
return list;
|
});
|
||||||
}
|
});
|
||||||
|
return list;
|
||||||
private void checkLevel() {
|
}
|
||||||
if (++aufgabenAufAktuellemLevel >= 1 + aufgabenProLevel) {
|
|
||||||
aufgabenAufAktuellemLevel = 0;
|
private void checkLevel() {
|
||||||
level++;
|
if (++aufgabenAufAktuellemLevel >= 1 + aufgabenProLevel) {
|
||||||
}
|
aufgabenAufAktuellemLevel = 0;
|
||||||
}
|
level++;
|
||||||
|
}
|
||||||
private AufgabeAnzeige findUltimativeStrafe() {
|
}
|
||||||
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
|
|
||||||
if (aktiv != null) {
|
private void setMitspielerInfo(AufgabeAnzeige anzeige, Mitspieler aktiv) {
|
||||||
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
|
if (aktiv != null) {
|
||||||
if (passiv != null) {
|
anzeige.setMitspielerId(aktiv.getId());
|
||||||
String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest.";
|
anzeige.setEigenesGeraet(aktiv.isEigenesGeraet());
|
||||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
}
|
||||||
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
}
|
||||||
anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName()));
|
|
||||||
anzeige.setTimer(new Random().nextInt(1800, 7200));
|
private AufgabeAnzeige findUltimativeStrafe() {
|
||||||
return anzeige;
|
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
|
||||||
}
|
if (aktiv != null) {
|
||||||
}
|
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
|
||||||
return findeStrafe();
|
if (passiv != null) {
|
||||||
}
|
String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest.";
|
||||||
|
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||||
private AufgabeAnzeige findSperreVerlaengern() {
|
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
||||||
if (!aktiveSperren.isEmpty()) {
|
setMitspielerInfo(anzeige, aktiv);
|
||||||
AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size()));
|
anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName()));
|
||||||
Mitspieler passiv = sperre.getMitspieler();
|
anzeige.setTimer(new Random().nextInt(1800, 7200));
|
||||||
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv);
|
return anzeige;
|
||||||
if (aktiv != null) {
|
}
|
||||||
String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?";
|
}
|
||||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
return findeStrafe();
|
||||||
anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName()));
|
}
|
||||||
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
|
||||||
SperrenVerlaengernCallback callback = new SperrenVerlaengernCallback();
|
private AufgabeAnzeige findSperreVerlaengern() {
|
||||||
callback.setFaktor(new Random().nextInt(2, 4));
|
if (!aktiveSperren.isEmpty()) {
|
||||||
callback.setSpielerId(passiv.getId());
|
AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size()));
|
||||||
anzeige.setCallback(callback);
|
Mitspieler passiv = sperre.getMitspieler();
|
||||||
return anzeige;
|
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv);
|
||||||
}
|
if (aktiv != null) {
|
||||||
}
|
String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?";
|
||||||
return findeSperre();
|
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||||
}
|
anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName()));
|
||||||
|
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
||||||
private AufgabeAnzeige findeAufgabe() {
|
setMitspielerInfo(anzeige, aktiv);
|
||||||
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
|
SperrenVerlaengernCallback callback = new SperrenVerlaengernCallback();
|
||||||
if (aktiv != null) {
|
callback.setFaktor(new Random().nextInt(2, 4));
|
||||||
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
|
callback.setSpielerId(passiv.getId());
|
||||||
if (passiv != null) {
|
anzeige.setCallback(callback);
|
||||||
List<Aufgabe> list = aufgabenList.getAufgaben().stream()
|
return anzeige;
|
||||||
.filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv))
|
}
|
||||||
.collect(Collectors.toList());
|
}
|
||||||
if (!list.isEmpty()) {
|
return findeSperre();
|
||||||
Aufgabe aufgabe = list.get(new Random().nextInt(list.size()));
|
}
|
||||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
|
||||||
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
private AufgabeAnzeige findeAufgabe() {
|
||||||
anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), aktiv.getName(), passiv.getName()));
|
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
|
||||||
if (aufgabe.getSekundenVon() != null) {
|
if (aktiv != null) {
|
||||||
if (aufgabe.getSekundenBis() != null) {
|
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
|
||||||
anzeige.setTimer(new Random().nextInt(aufgabe.getSekundenVon(), aufgabe.getSekundenBis()));
|
if (passiv != null) {
|
||||||
} else {
|
List<Aufgabe> list = aufgabenList.getAufgaben().stream()
|
||||||
anzeige.setTimer(aufgabe.getSekundenVon());
|
.filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv))
|
||||||
}
|
.collect(Collectors.toList());
|
||||||
}
|
if (!list.isEmpty()) {
|
||||||
return anzeige;
|
Aufgabe aufgabe = list.get(new Random().nextInt(list.size()));
|
||||||
}
|
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||||
}
|
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
||||||
}
|
setMitspielerInfo(anzeige, aktiv);
|
||||||
return findeStrafe();
|
anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), aktiv.getName(), passiv.getName()));
|
||||||
}
|
if (aufgabe.getSekundenVon() != null) {
|
||||||
|
if (aufgabe.getSekundenBis() != null) {
|
||||||
private AufgabeAnzeige findeStrafe() {
|
anzeige.setTimer(new Random().nextInt(aufgabe.getSekundenVon(), aufgabe.getSekundenBis()));
|
||||||
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
|
} else {
|
||||||
if (aktiv != null) {
|
anzeige.setTimer(aufgabe.getSekundenVon());
|
||||||
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
|
}
|
||||||
if (passiv != null) {
|
}
|
||||||
List<Strafe> list = aufgabenList.getStrafen().stream()
|
return anzeige;
|
||||||
.filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv))
|
}
|
||||||
.collect(Collectors.toList());
|
}
|
||||||
if (!list.isEmpty()) {
|
}
|
||||||
Strafe strafe = list.get(new Random().nextInt(list.size()));
|
return findeStrafe();
|
||||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
}
|
||||||
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
|
||||||
anzeige.setAufgabeText(getAnzeigeText(strafe.getText(), aktiv.getName(), passiv.getName()));
|
private AufgabeAnzeige findeStrafe() {
|
||||||
if (strafe.getSekundenVon() != null) {
|
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
|
||||||
if (strafe.getSekundenBis() != null) {
|
if (aktiv != null) {
|
||||||
anzeige.setTimer(new Random().nextInt(strafe.getSekundenVon(), strafe.getSekundenBis()));
|
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
|
||||||
} else {
|
if (passiv != null) {
|
||||||
anzeige.setTimer(strafe.getSekundenVon());
|
List<Strafe> list = aufgabenList.getStrafen().stream()
|
||||||
}
|
.filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv))
|
||||||
}
|
.collect(Collectors.toList());
|
||||||
return anzeige;
|
if (!list.isEmpty()) {
|
||||||
}
|
Strafe strafe = list.get(new Random().nextInt(list.size()));
|
||||||
}
|
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||||
}
|
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
||||||
return findeSperre();
|
setMitspielerInfo(anzeige, aktiv);
|
||||||
}
|
anzeige.setAufgabeText(getAnzeigeText(strafe.getText(), aktiv.getName(), passiv.getName()));
|
||||||
|
if (strafe.getSekundenVon() != null) {
|
||||||
private AufgabeAnzeige findeSperre() {
|
if (strafe.getSekundenBis() != null) {
|
||||||
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
|
anzeige.setTimer(new Random().nextInt(strafe.getSekundenVon(), strafe.getSekundenBis()));
|
||||||
if (aktiv != null) {
|
} else {
|
||||||
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
|
anzeige.setTimer(strafe.getSekundenVon());
|
||||||
if (passiv != null) {
|
}
|
||||||
List<Sperre> list = aufgabenList.getSperren().stream()
|
}
|
||||||
.filter(sperre -> sperre.isAufgabePassend(passiv))
|
return anzeige;
|
||||||
.collect(Collectors.toList());
|
}
|
||||||
if (!list.isEmpty()) {
|
}
|
||||||
Sperre sperre = list.get(new Random().nextInt(list.size()));
|
}
|
||||||
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
return findeSperre();
|
||||||
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
}
|
||||||
anzeige.setAufgabeText(getAnzeigeText(sperre.getText(), aktiv.getName(), passiv.getName()));
|
|
||||||
SperreCallback callback = new SperreCallback();
|
private AufgabeAnzeige findeSperre() {
|
||||||
callback.setSperreId(sperre.getSperreId());
|
Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
|
||||||
callback.setSpielerId(passiv.getId());
|
if (aktiv != null) {
|
||||||
callback.setReleaseText(getAnzeigeText(sperre.getReleaseText(), aktiv.getName(), passiv.getName()));
|
Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
|
||||||
anzeige.setCallback(callback);
|
if (passiv != null) {
|
||||||
return anzeige;
|
List<Sperre> list = aufgabenList.getSperren().stream()
|
||||||
}
|
.filter(sperre -> sperre.isAufgabePassend(passiv))
|
||||||
}
|
.collect(Collectors.toList());
|
||||||
}
|
if (!list.isEmpty()) {
|
||||||
return null;
|
Sperre sperre = list.get(new Random().nextInt(list.size()));
|
||||||
}
|
AufgabeAnzeige anzeige = new AufgabeAnzeige();
|
||||||
|
anzeige.setNameAktiverMitspieler(aktiv.getName());
|
||||||
private String getAnzeigeText(String textMitPlatzhaltern, String nameAktiv, String namePassiv) {
|
setMitspielerInfo(anzeige, aktiv);
|
||||||
return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv);
|
anzeige.setAufgabeText(getAnzeigeText(sperre.getText(), aktiv.getName(), passiv.getName()));
|
||||||
}
|
SperreCallback callback = new SperreCallback();
|
||||||
|
callback.setSperreId(sperre.getSperreId());
|
||||||
private Mitspieler findeMitspielerMitRolle(RolleEnum rolle) {
|
callback.setSpielerId(passiv.getId());
|
||||||
List<Mitspieler> list = mitspieler.stream()
|
callback.setReleaseText(getAnzeigeText(sperre.getReleaseText(), aktiv.getName(), passiv.getName()));
|
||||||
.filter(m -> m.getRollen().contains(rolle))
|
anzeige.setCallback(callback);
|
||||||
.toList();
|
return anzeige;
|
||||||
return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private Mitspieler findeMitspielerMitRolle(RolleEnum rolle, Mitspieler gegenspieler) {
|
return null;
|
||||||
if (gegenspieler == null) return findeMitspielerMitRolle(rolle);
|
}
|
||||||
List<Mitspieler> list = mitspieler.stream()
|
|
||||||
.filter(m -> m != gegenspieler)
|
private String getAnzeigeText(String textMitPlatzhaltern, String nameAktiv, String namePassiv) {
|
||||||
.filter(m -> m.isPassenderSpielpartner(gegenspieler))
|
return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv);
|
||||||
.filter(m -> m.getRollen().contains(rolle))
|
}
|
||||||
.toList();
|
|
||||||
return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
|
private Mitspieler findeMitspielerMitRolle(RolleEnum rolle) {
|
||||||
}
|
List<Mitspieler> list = mitspieler.stream()
|
||||||
|
.filter(m -> m.getRollen().contains(rolle))
|
||||||
public int getAufgabenAufAktuellemLevel() {
|
.toList();
|
||||||
return aufgabenAufAktuellemLevel;
|
return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLevel() {
|
private Mitspieler findeMitspielerMitRolle(RolleEnum rolle, Mitspieler gegenspieler) {
|
||||||
return level;
|
if (gegenspieler == null) return findeMitspielerMitRolle(rolle);
|
||||||
}
|
List<Mitspieler> list = mitspieler.stream()
|
||||||
}
|
.filter(m -> m != gegenspieler)
|
||||||
|
.filter(m -> m.isPassenderSpielpartner(gegenspieler))
|
||||||
|
.filter(m -> m.getRollen().contains(rolle))
|
||||||
|
.toList();
|
||||||
|
return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAufgabenAufAktuellemLevel() {
|
||||||
|
return aufgabenAufAktuellemLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
public enum GeschlechtEnum {
|
public enum GeschlechtEnum {
|
||||||
WEIBLICH,
|
WEIBLICH,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -11,6 +11,8 @@ import java.util.UUID;
|
|||||||
public class Mitspieler {
|
public class Mitspieler {
|
||||||
|
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
private UUID userId;
|
||||||
|
private boolean eigenesGeraet;
|
||||||
private String name;
|
private String name;
|
||||||
private GeschlechtEnum geschlecht;
|
private GeschlechtEnum geschlecht;
|
||||||
private List<GeschlechtEnum> spieltMit;
|
private List<GeschlechtEnum> spieltMit;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
public enum RolleEnum {
|
public enum RolleEnum {
|
||||||
BESTRAFUNG_AKTIV,
|
BESTRAFUNG_AKTIV,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session;
|
package de.oaa.xxx.games.bdsm;
|
||||||
|
|
||||||
public enum Werkzeug {
|
public enum Werkzeug {
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.oaa.xxx.session.aufgaben;
|
package de.oaa.xxx.games.bdsm.aufgaben;
|
||||||
|
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.Werkzeug;
|
import de.oaa.xxx.games.bdsm.Werkzeug;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session.aufgaben;
|
package de.oaa.xxx.games.bdsm.aufgaben;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.oaa.xxx.session.aufgaben;
|
package de.oaa.xxx.games.bdsm.aufgaben;
|
||||||
|
|
||||||
import de.oaa.xxx.session.GeschlechtEnum;
|
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
|
||||||
import de.oaa.xxx.session.Werkzeug;
|
import de.oaa.xxx.games.bdsm.Werkzeug;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.oaa.xxx.session.aufgaben;
|
package de.oaa.xxx.games.bdsm.aufgaben;
|
||||||
|
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.Werkzeug;
|
import de.oaa.xxx.games.bdsm.Werkzeug;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.oaa.xxx.session.aufgaben;
|
package de.oaa.xxx.games.bdsm.aufgaben;
|
||||||
|
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.Werkzeug;
|
import de.oaa.xxx.games.bdsm.Werkzeug;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package de.oaa.xxx.games.bdsm.controller;
|
||||||
|
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity;
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository;
|
||||||
|
import de.oaa.xxx.social.SystemMessageService;
|
||||||
|
import de.oaa.xxx.social.entity.MessageCause;
|
||||||
|
import de.oaa.xxx.social.repository.FriendshipRepository;
|
||||||
|
import de.oaa.xxx.user.UserRepository;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/bdsm/einladung")
|
||||||
|
@Transactional
|
||||||
|
public class BdsmEinladungController {
|
||||||
|
|
||||||
|
private final BdsmEinladungRepository einladungRepository;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final FriendshipRepository friendshipRepository;
|
||||||
|
private final SystemMessageService systemMessageService;
|
||||||
|
|
||||||
|
public BdsmEinladungController(BdsmEinladungRepository einladungRepository,
|
||||||
|
UserRepository userRepository,
|
||||||
|
FriendshipRepository friendshipRepository,
|
||||||
|
SystemMessageService systemMessageService) {
|
||||||
|
this.einladungRepository = einladungRepository;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.friendshipRepository = friendshipRepository;
|
||||||
|
this.systemMessageService = systemMessageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
record EinladungRequest(UUID setupId, int slotIndex, UUID inviteeId) {}
|
||||||
|
record AntwortRequest(boolean accepted, String mode) {} // mode: OWN_DEVICE | HOST_DEVICE
|
||||||
|
|
||||||
|
private UUID currentUserId(Principal principal) {
|
||||||
|
return userRepository.findByEmail(principal.getName())
|
||||||
|
.map(u -> u.getUserId()).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<Map<String, Object>> sendEinladung(@RequestBody EinladungRequest req, Principal principal) {
|
||||||
|
UUID inviterId = currentUserId(principal);
|
||||||
|
if (inviterId == null) return ResponseEntity.status(401).build();
|
||||||
|
|
||||||
|
// Freundschaft prüfen
|
||||||
|
var friendship = friendshipRepository.findExisting(inviterId, req.inviteeId());
|
||||||
|
if (friendship.isEmpty() || friendship.get().getStatus() != de.oaa.xxx.social.entity.FriendshipEntity.Status.ACCEPTED) {
|
||||||
|
return ResponseEntity.status(403).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alte Einladung für diesen Slot canceln
|
||||||
|
einladungRepository.findBySetupId(req.setupId()).stream()
|
||||||
|
.filter(e -> e.getSlotIndex() == req.slotIndex() && e.getStatus() == Status.PENDING)
|
||||||
|
.forEach(e -> e.setStatus(Status.CANCELLED));
|
||||||
|
|
||||||
|
BdsmEinladungEntity entity = new BdsmEinladungEntity();
|
||||||
|
entity.setEinladungId(UUID.randomUUID());
|
||||||
|
entity.setSetupId(req.setupId());
|
||||||
|
entity.setInviterId(inviterId);
|
||||||
|
entity.setInviteeId(req.inviteeId());
|
||||||
|
entity.setSlotIndex(req.slotIndex());
|
||||||
|
entity.setStatus(Status.PENDING);
|
||||||
|
entity.setCreatedAt(LocalDateTime.now());
|
||||||
|
einladungRepository.save(entity);
|
||||||
|
|
||||||
|
String inviterName = userRepository.findById(inviterId).map(u -> u.getName()).orElse("Jemand");
|
||||||
|
systemMessageService.send(
|
||||||
|
inviterId, req.inviteeId(),
|
||||||
|
inviterName + " hat dich zum BDSM Game eingeladen.",
|
||||||
|
"/einladungen.html",
|
||||||
|
MessageCause.INVITATION
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("einladungId", entity.getEinladungId());
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> cancelEinladung(@PathVariable UUID id, Principal principal) {
|
||||||
|
UUID userId = currentUserId(principal);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
BdsmEinladungEntity e = einladungRepository.findById(id).orElse(null);
|
||||||
|
if (e == null) return ResponseEntity.notFound().build();
|
||||||
|
if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build();
|
||||||
|
e.setStatus(Status.CANCELLED);
|
||||||
|
return ResponseEntity.accepted().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<Map<String, Object>>> getBySetupId(@RequestParam UUID setupId, Principal principal) {
|
||||||
|
UUID userId = currentUserId(principal);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
List<Map<String, Object>> list = einladungRepository.findBySetupId(setupId).stream()
|
||||||
|
.map(this::toMap).toList();
|
||||||
|
return ResponseEntity.ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/pending")
|
||||||
|
public ResponseEntity<List<Map<String, Object>>> getPending(Principal principal) {
|
||||||
|
UUID userId = currentUserId(principal);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
List<Map<String, Object>> list = einladungRepository.findByInviteeIdAndStatus(userId, Status.PENDING)
|
||||||
|
.stream().map(e -> {
|
||||||
|
Map<String, Object> m = toMap(e);
|
||||||
|
userRepository.findById(e.getInviterId()).ifPresent(u -> {
|
||||||
|
m.put("inviterName", u.getName());
|
||||||
|
m.put("inviterAvatar", u.getProfilePicture() != null ? u.getProfilePicture() : "");
|
||||||
|
});
|
||||||
|
return m;
|
||||||
|
}).toList();
|
||||||
|
return ResponseEntity.ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/sent")
|
||||||
|
public ResponseEntity<List<Map<String, Object>>> getSent(Principal principal) {
|
||||||
|
UUID userId = currentUserId(principal);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
List<Map<String, Object>> list = einladungRepository.findByInviterIdAndStatus(userId, Status.PENDING)
|
||||||
|
.stream().map(e -> {
|
||||||
|
Map<String, Object> m = toMap(e);
|
||||||
|
userRepository.findById(e.getInviteeId()).ifPresent(u -> {
|
||||||
|
m.put("inviteeName", u.getName());
|
||||||
|
m.put("inviteeAvatar", u.getProfilePicture() != null ? u.getProfilePicture() : "");
|
||||||
|
});
|
||||||
|
return m;
|
||||||
|
}).toList();
|
||||||
|
return ResponseEntity.ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseEntity<Map<String, Object>> getById(@PathVariable UUID id, Principal principal) {
|
||||||
|
UUID userId = currentUserId(principal);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
BdsmEinladungEntity e = einladungRepository.findById(id).orElse(null);
|
||||||
|
if (e == null) return ResponseEntity.notFound().build();
|
||||||
|
if (!e.getInviteeId().equals(userId) && !e.getInviterId().equals(userId)) {
|
||||||
|
return ResponseEntity.status(403).build();
|
||||||
|
}
|
||||||
|
Map<String, Object> m = toMap(e);
|
||||||
|
userRepository.findById(e.getInviterId()).ifPresent(u -> {
|
||||||
|
m.put("inviterName", u.getName());
|
||||||
|
m.put("inviterAvatar", u.getProfilePicture() != null ? u.getProfilePicture() : "");
|
||||||
|
});
|
||||||
|
return ResponseEntity.ok(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}/antwort")
|
||||||
|
public ResponseEntity<Void> antwort(@PathVariable UUID id, @RequestBody AntwortRequest req, Principal principal) {
|
||||||
|
UUID userId = currentUserId(principal);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
BdsmEinladungEntity e = einladungRepository.findById(id).orElse(null);
|
||||||
|
if (e == null) return ResponseEntity.notFound().build();
|
||||||
|
if (!e.getInviteeId().equals(userId)) return ResponseEntity.status(403).build();
|
||||||
|
if (e.getStatus() != Status.PENDING) return ResponseEntity.badRequest().build();
|
||||||
|
if (!req.accepted()) {
|
||||||
|
e.setStatus(Status.DECLINED);
|
||||||
|
} else if ("OWN_DEVICE".equals(req.mode())) {
|
||||||
|
e.setStatus(Status.ACCEPTED_OWN);
|
||||||
|
} else {
|
||||||
|
e.setStatus(Status.ACCEPTED_HOST);
|
||||||
|
}
|
||||||
|
return ResponseEntity.accepted().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> toMap(BdsmEinladungEntity e) {
|
||||||
|
Map<String, Object> m = new LinkedHashMap<>();
|
||||||
|
m.put("einladungId", e.getEinladungId());
|
||||||
|
m.put("setupId", e.getSetupId());
|
||||||
|
m.put("slotIndex", e.getSlotIndex());
|
||||||
|
m.put("inviteeId", e.getInviteeId());
|
||||||
|
m.put("inviterId", e.getInviterId());
|
||||||
|
m.put("status", e.getStatus().name());
|
||||||
|
m.put("sessionId", e.getSessionId());
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,22 @@
|
|||||||
package de.oaa.xxx.session.controller;
|
package de.oaa.xxx.games.bdsm.controller;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import de.oaa.xxx.session.AufgabeAnzeige;
|
import de.oaa.xxx.games.bdsm.AufgabeAnzeige;
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.Session;
|
import de.oaa.xxx.games.bdsm.BdsmGame;
|
||||||
import de.oaa.xxx.session.SessionDurchfuehren;
|
import de.oaa.xxx.games.bdsm.BdsmGameDurchfuehren;
|
||||||
import de.oaa.xxx.session.aufgaben.AufgabenList;
|
import de.oaa.xxx.games.bdsm.aufgaben.AufgabenList;
|
||||||
import de.oaa.xxx.session.entity.MitspielerEntity;
|
import de.oaa.xxx.games.bdsm.entity.MitspielerEntity;
|
||||||
import de.oaa.xxx.session.entity.SessionEntity;
|
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
|
||||||
import de.oaa.xxx.session.repository.AktiveSperreRepository;
|
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity;
|
||||||
import de.oaa.xxx.session.repository.MitspielerRepository;
|
import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository;
|
||||||
import de.oaa.xxx.session.repository.SessionRepository;
|
import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.MitspielerRepository;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository;
|
||||||
|
import de.oaa.xxx.games.history.GameHistoryEntity;
|
||||||
|
import de.oaa.xxx.games.history.GameHistoryRepository;
|
||||||
|
import de.oaa.xxx.games.history.GameRole;
|
||||||
|
import de.oaa.xxx.games.history.GameType;
|
||||||
import de.oaa.xxx.user.UserRepository;
|
import de.oaa.xxx.user.UserRepository;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -21,59 +27,69 @@ import org.springframework.web.bind.annotation.DeleteMapping;
|
|||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/session")
|
@RequestMapping("/bdsm")
|
||||||
@Transactional
|
@Transactional
|
||||||
public class SessionController {
|
public class BdsmGameController {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(SessionController.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(BdsmGameController.class);
|
||||||
|
|
||||||
private final SessionRepository sessionRepository;
|
private final BdsmGameRepository sessionRepository;
|
||||||
private final MitspielerRepository mitspielerRepository;
|
private final MitspielerRepository mitspielerRepository;
|
||||||
private final AktiveSperreRepository aktiveSperreRepository;
|
private final AktiveSperreRepository aktiveSperreRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final GameHistoryRepository gameHistoryRepository;
|
||||||
|
private final BdsmEinladungRepository einladungRepository;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public SessionController(SessionRepository sessionRepository, MitspielerRepository mitspielerRepository,
|
public BdsmGameController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository,
|
||||||
AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository,
|
AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository,
|
||||||
|
GameHistoryRepository gameHistoryRepository, BdsmEinladungRepository einladungRepository,
|
||||||
ObjectMapper objectMapper) {
|
ObjectMapper objectMapper) {
|
||||||
this.sessionRepository = sessionRepository;
|
this.sessionRepository = sessionRepository;
|
||||||
this.mitspielerRepository = mitspielerRepository;
|
this.mitspielerRepository = mitspielerRepository;
|
||||||
this.aktiveSperreRepository = aktiveSperreRepository;
|
this.aktiveSperreRepository = aktiveSperreRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
|
this.gameHistoryRepository = gameHistoryRepository;
|
||||||
|
this.einladungRepository = einladungRepository;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{sessionId}")
|
@GetMapping("/{sessionId}")
|
||||||
public ResponseEntity<Session> getBySessionId(@PathVariable UUID sessionId) {
|
public ResponseEntity<BdsmGame> getBySessionId(@PathVariable UUID sessionId) {
|
||||||
return sessionRepository.findById(sessionId)
|
return sessionRepository.findById(sessionId)
|
||||||
.map(entity -> ResponseEntity.ok(toSession(entity)))
|
.map(entity -> ResponseEntity.ok(toSession(entity)))
|
||||||
.orElse(ResponseEntity.noContent().build());
|
.orElse(ResponseEntity.noContent().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<Session> getByUserId(@RequestParam UUID userId) {
|
public ResponseEntity<BdsmGame> getByUserId(@RequestParam UUID userId) {
|
||||||
return sessionRepository.findByUserId(userId)
|
return sessionRepository.findByUserId(userId)
|
||||||
.map(entity -> ResponseEntity.ok(toSession(entity)))
|
.map(entity -> ResponseEntity.ok(toSession(entity)))
|
||||||
.orElse(ResponseEntity.noContent().build());
|
.orElse(ResponseEntity.noContent().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<Void> create(@RequestBody Session session) {
|
public ResponseEntity<Void> create(@RequestBody BdsmGame session) {
|
||||||
String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
UUID userId = userRepository.findByEmail(email).map(u -> u.getUserId()).orElse(null);
|
UUID userId = userRepository.findByEmail(email).map(u -> u.getUserId()).orElse(null);
|
||||||
if (userId == null) return ResponseEntity.status(401).build();
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
SessionEntity entity = new SessionEntity();
|
BdsmGameEntity entity = new BdsmGameEntity();
|
||||||
entity.setSessionId(UUID.randomUUID());
|
entity.setSessionId(UUID.randomUUID());
|
||||||
entity.setUserId(userId);
|
entity.setUserId(userId);
|
||||||
entity.setAufgabenAufAktuellemLevel(0);
|
entity.setAufgabenAufAktuellemLevel(0);
|
||||||
@@ -85,8 +101,16 @@ public class SessionController {
|
|||||||
entity.setWahrscheinlichkeitStrafe(session.getWahrscheinlichkeitStrafe() != null ? session.getWahrscheinlichkeitStrafe() : 10);
|
entity.setWahrscheinlichkeitStrafe(session.getWahrscheinlichkeitStrafe() != null ? session.getWahrscheinlichkeitStrafe() : 10);
|
||||||
entity.setZeitfaktorZeitstrafen(session.getZeitfaktorZeitstrafen() != null ? session.getZeitfaktorZeitstrafen() : 1.0);
|
entity.setZeitfaktorZeitstrafen(session.getZeitfaktorZeitstrafen() != null ? session.getZeitfaktorZeitstrafen() : 1.0);
|
||||||
entity.setLevel(1);
|
entity.setLevel(1);
|
||||||
|
entity.setSetupId(session.getSetupId());
|
||||||
sessionRepository.save(entity);
|
sessionRepository.save(entity);
|
||||||
LOGGER.debug("Session gestartet [sessionId={}, userId={}, aufgabenProLevel={}, wahrscheinlichkeitStrafe={}%, wahrscheinlichkeitSperre={}%, zeitfaktorZeitstrafen={}]",
|
// Akzeptierte Einladungen mit der neuen Session verknüpfen
|
||||||
|
if (session.getSetupId() != null) {
|
||||||
|
einladungRepository.findBySetupId(session.getSetupId()).stream()
|
||||||
|
.filter(e -> e.getStatus() == BdsmEinladungEntity.Status.ACCEPTED_OWN
|
||||||
|
|| e.getStatus() == BdsmEinladungEntity.Status.ACCEPTED_HOST)
|
||||||
|
.forEach(e -> e.setSessionId(entity.getSessionId()));
|
||||||
|
}
|
||||||
|
LOGGER.debug("BdsmGame gestartet [sessionId={}, userId={}, aufgabenProLevel={}, wahrscheinlichkeitStrafe={}%, wahrscheinlichkeitSperre={}%, zeitfaktorZeitstrafen={}]",
|
||||||
entity.getSessionId(), entity.getUserId(), entity.getAufgabenProLevel(),
|
entity.getSessionId(), entity.getUserId(), entity.getAufgabenProLevel(),
|
||||||
entity.getWahrscheinlichkeitStrafe(), entity.getWahrscheinlichkeitSperre(),
|
entity.getWahrscheinlichkeitStrafe(), entity.getWahrscheinlichkeitSperre(),
|
||||||
entity.getZeitfaktorZeitstrafen());
|
entity.getZeitfaktorZeitstrafen());
|
||||||
@@ -96,9 +120,20 @@ public class SessionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping
|
@DeleteMapping
|
||||||
public ResponseEntity<Void> deleteSession(@RequestBody Session session) {
|
public ResponseEntity<Void> deleteSession(@RequestBody BdsmGame session) {
|
||||||
return sessionRepository.findById(session.getSessionId())
|
return sessionRepository.findById(session.getSessionId())
|
||||||
.map(entity -> {
|
.map(entity -> {
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
long durationMinutes = Duration.between(entity.getStartZeit(), endTime).toMinutes();
|
||||||
|
|
||||||
|
GameHistoryEntity entry = new GameHistoryEntity();
|
||||||
|
entry.setGameType(GameType.BDSM);
|
||||||
|
entry.setStartTime(entity.getStartZeit());
|
||||||
|
entry.setEndTime(endTime);
|
||||||
|
entry.setDurationMinutes(durationMinutes);
|
||||||
|
entry.addParticipant(entity.getUserId(), GameRole.PLAYER);
|
||||||
|
gameHistoryRepository.save(entry);
|
||||||
|
|
||||||
aktiveSperreRepository.deleteAll(entity.getAktiveSperren());
|
aktiveSperreRepository.deleteAll(entity.getAktiveSperren());
|
||||||
mitspielerRepository.deleteAll(entity.getMitspieler());
|
mitspielerRepository.deleteAll(entity.getMitspieler());
|
||||||
sessionRepository.delete(entity);
|
sessionRepository.delete(entity);
|
||||||
@@ -114,7 +149,7 @@ public class SessionController {
|
|||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
String aufgaben = objectMapper.writeValueAsString(list);
|
String aufgaben = objectMapper.writeValueAsString(list);
|
||||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
@@ -130,12 +165,12 @@ public class SessionController {
|
|||||||
@GetMapping("/{sessionId}/aufgaben/next")
|
@GetMapping("/{sessionId}/aufgaben/next")
|
||||||
public ResponseEntity<AufgabeAnzeige> getNextAufgabe(@PathVariable UUID sessionId) {
|
public ResponseEntity<AufgabeAnzeige> getNextAufgabe(@PathVariable UUID sessionId) {
|
||||||
try {
|
try {
|
||||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
session.setLetzteAktivitaet(LocalDateTime.now());
|
session.setLetzteAktivitaet(LocalDateTime.now());
|
||||||
SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session);
|
BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session);
|
||||||
AufgabeAnzeige next = durchfuehren.getNext();
|
AufgabeAnzeige next = durchfuehren.getNext();
|
||||||
session.setLevel(durchfuehren.getLevel());
|
session.setLevel(durchfuehren.getLevel());
|
||||||
session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
|
session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
|
||||||
@@ -165,7 +200,7 @@ public class SessionController {
|
|||||||
|| mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
|
|| mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
@@ -176,6 +211,8 @@ public class SessionController {
|
|||||||
entity.setRollen(mitspieler.getRollen());
|
entity.setRollen(mitspieler.getRollen());
|
||||||
entity.setSpieltMit(mitspieler.getSpieltMit());
|
entity.setSpieltMit(mitspieler.getSpieltMit());
|
||||||
entity.setWerkzeuge(mitspieler.getVerfuegbareWerkzeuge());
|
entity.setWerkzeuge(mitspieler.getVerfuegbareWerkzeuge());
|
||||||
|
entity.setUserId(mitspieler.getUserId());
|
||||||
|
entity.setEigenesGeraet(mitspieler.isEigenesGeraet());
|
||||||
entity.setSession(session);
|
entity.setSession(session);
|
||||||
mitspielerRepository.save(entity);
|
mitspielerRepository.save(entity);
|
||||||
return ResponseEntity.accepted().build();
|
return ResponseEntity.accepted().build();
|
||||||
@@ -184,9 +221,9 @@ public class SessionController {
|
|||||||
@GetMapping("/{sessionId}/finisher")
|
@GetMapping("/{sessionId}/finisher")
|
||||||
public ResponseEntity<List<AufgabeAnzeige>> getFinisher(@PathVariable UUID sessionId) {
|
public ResponseEntity<List<AufgabeAnzeige>> getFinisher(@PathVariable UUID sessionId) {
|
||||||
try {
|
try {
|
||||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
if (session == null) return ResponseEntity.badRequest().build();
|
if (session == null) return ResponseEntity.badRequest().build();
|
||||||
SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session);
|
BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session);
|
||||||
return ResponseEntity.ok(durchfuehren.getFinisher());
|
return ResponseEntity.ok(durchfuehren.getFinisher());
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
LOGGER.error(exception.getMessage(), exception);
|
LOGGER.error(exception.getMessage(), exception);
|
||||||
@@ -197,9 +234,9 @@ public class SessionController {
|
|||||||
@PostMapping("/{sessionId}/backToLevel5")
|
@PostMapping("/{sessionId}/backToLevel5")
|
||||||
public ResponseEntity<Void> backToLevel5(@PathVariable UUID sessionId) {
|
public ResponseEntity<Void> backToLevel5(@PathVariable UUID sessionId) {
|
||||||
try {
|
try {
|
||||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
if (session == null) return ResponseEntity.badRequest().build();
|
if (session == null) return ResponseEntity.badRequest().build();
|
||||||
SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session);
|
BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session);
|
||||||
durchfuehren.backToLvl5();
|
durchfuehren.backToLvl5();
|
||||||
session.setLevel(durchfuehren.getLevel());
|
session.setLevel(durchfuehren.getLevel());
|
||||||
session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
|
session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel());
|
||||||
@@ -211,8 +248,62 @@ public class SessionController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Session toSession(SessionEntity entity) {
|
@GetMapping("/{sessionId}/mitspieler/me")
|
||||||
Session session = new Session();
|
public ResponseEntity<Map<String, Object>> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) {
|
||||||
|
UUID userId = userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null);
|
||||||
|
if (userId == null) return ResponseEntity.status(401).build();
|
||||||
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
|
if (session == null) return ResponseEntity.notFound().build();
|
||||||
|
return session.getMitspieler().stream()
|
||||||
|
.filter(m -> userId.equals(m.getUserId()))
|
||||||
|
.findFirst()
|
||||||
|
.map(m -> {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("mitspielerId", m.getMitspielerId());
|
||||||
|
result.put("name", m.getName());
|
||||||
|
result.put("eigenesGeraet", m.isEigenesGeraet());
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
})
|
||||||
|
.orElse(ResponseEntity.noContent().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
record ActiveTaskRequest(String taskJson, LocalDateTime timerStartedAt) {}
|
||||||
|
record ActiveTaskResponse(String taskJson, Long elapsedSeconds) {}
|
||||||
|
|
||||||
|
@PutMapping("/{sessionId}/active-task")
|
||||||
|
public ResponseEntity<Void> setActiveTask(@PathVariable UUID sessionId, @RequestBody ActiveTaskRequest req) {
|
||||||
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
|
if (session == null) return ResponseEntity.notFound().build();
|
||||||
|
session.setActiveTaskJson(req.taskJson());
|
||||||
|
session.setTaskStartedAt(req.timerStartedAt());
|
||||||
|
sessionRepository.save(session);
|
||||||
|
return ResponseEntity.accepted().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{sessionId}/active-task")
|
||||||
|
public ResponseEntity<Void> clearActiveTask(@PathVariable UUID sessionId) {
|
||||||
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
|
if (session == null) return ResponseEntity.notFound().build();
|
||||||
|
session.setActiveTaskJson(null);
|
||||||
|
session.setTaskStartedAt(null);
|
||||||
|
sessionRepository.save(session);
|
||||||
|
return ResponseEntity.accepted().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{sessionId}/active-task")
|
||||||
|
public ResponseEntity<ActiveTaskResponse> getActiveTask(@PathVariable UUID sessionId) {
|
||||||
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
|
if (session == null) return ResponseEntity.notFound().build();
|
||||||
|
if (session.getActiveTaskJson() == null) return ResponseEntity.noContent().build();
|
||||||
|
Long elapsed = null;
|
||||||
|
if (session.getTaskStartedAt() != null) {
|
||||||
|
elapsed = Duration.between(session.getTaskStartedAt(), LocalDateTime.now()).getSeconds();
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(new ActiveTaskResponse(session.getActiveTaskJson(), elapsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdsmGame toSession(BdsmGameEntity entity) {
|
||||||
|
BdsmGame session = new BdsmGame();
|
||||||
session.setSessionId(entity.getSessionId());
|
session.setSessionId(entity.getSessionId());
|
||||||
session.setUserId(entity.getUserId());
|
session.setUserId(entity.getUserId());
|
||||||
session.setAufgabenProLevel(entity.getAufgabenProLevel());
|
session.setAufgabenProLevel(entity.getAufgabenProLevel());
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package de.oaa.xxx.session.controller;
|
package de.oaa.xxx.games.bdsm.controller;
|
||||||
|
|
||||||
import de.oaa.xxx.session.AktiveSperre;
|
import de.oaa.xxx.games.bdsm.AktiveSperre;
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.entity.AktiveSperreEntity;
|
import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity;
|
||||||
import de.oaa.xxx.session.entity.SessionEntity;
|
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
|
||||||
import de.oaa.xxx.session.repository.AktiveSperreRepository;
|
import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository;
|
||||||
import de.oaa.xxx.session.repository.MitspielerRepository;
|
import de.oaa.xxx.games.bdsm.repository.MitspielerRepository;
|
||||||
import de.oaa.xxx.session.repository.SessionRepository;
|
import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository;
|
||||||
import de.oaa.xxx.session.sperre.SperreCallback;
|
import de.oaa.xxx.games.bdsm.sperre.SperreCallback;
|
||||||
import de.oaa.xxx.session.sperre.SperreVerarbeiten;
|
import de.oaa.xxx.games.bdsm.sperre.SperreVerarbeiten;
|
||||||
import de.oaa.xxx.session.sperre.SperrenVerlaengernCallback;
|
import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -26,18 +26,18 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@RestController("sessionSperreController")
|
@RestController("bdsmSperreController")
|
||||||
@RequestMapping("/session/sperre")
|
@RequestMapping("/bdsm/sperre")
|
||||||
@Transactional
|
@Transactional
|
||||||
public class SperreController {
|
public class SperreController {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(SperreController.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(SperreController.class);
|
||||||
|
|
||||||
private final SessionRepository sessionRepository;
|
private final BdsmGameRepository sessionRepository;
|
||||||
private final MitspielerRepository mitspielerRepository;
|
private final MitspielerRepository mitspielerRepository;
|
||||||
private final AktiveSperreRepository aktiveSperreRepository;
|
private final AktiveSperreRepository aktiveSperreRepository;
|
||||||
|
|
||||||
public SperreController(SessionRepository sessionRepository, MitspielerRepository mitspielerRepository,
|
public SperreController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository,
|
||||||
AktiveSperreRepository aktiveSperreRepository) {
|
AktiveSperreRepository aktiveSperreRepository) {
|
||||||
this.sessionRepository = sessionRepository;
|
this.sessionRepository = sessionRepository;
|
||||||
this.mitspielerRepository = mitspielerRepository;
|
this.mitspielerRepository = mitspielerRepository;
|
||||||
@@ -78,7 +78,7 @@ public class SperreController {
|
|||||||
@GetMapping("/aktive")
|
@GetMapping("/aktive")
|
||||||
public ResponseEntity<List<AktiveSperre>> getAktiveSperren(@RequestParam UUID sessionId) {
|
public ResponseEntity<List<AktiveSperre>> getAktiveSperren(@RequestParam UUID sessionId) {
|
||||||
try {
|
try {
|
||||||
SessionEntity session = sessionRepository.findById(sessionId).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null);
|
||||||
if (session == null) return ResponseEntity.noContent().build();
|
if (session == null) return ResponseEntity.noContent().build();
|
||||||
List<Mitspieler> mitspielerList = session.getMitspieler().stream()
|
List<Mitspieler> mitspielerList = session.getMitspieler().stream()
|
||||||
.map(m -> m.toMitspieler())
|
.map(m -> m.toMitspieler())
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package de.oaa.xxx.session.entity;
|
package de.oaa.xxx.games.bdsm.entity;
|
||||||
|
|
||||||
import de.oaa.xxx.session.AktiveSperre;
|
import de.oaa.xxx.games.bdsm.AktiveSperre;
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.Werkzeug;
|
import de.oaa.xxx.games.bdsm.Werkzeug;
|
||||||
import jakarta.persistence.CollectionTable;
|
import jakarta.persistence.CollectionTable;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.ElementCollection;
|
import jakarta.persistence.ElementCollection;
|
||||||
@@ -50,7 +50,7 @@ public class AktiveSperreEntity {
|
|||||||
private String releaseText;
|
private String releaseText;
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "sessionId", nullable = false)
|
@JoinColumn(name = "sessionId", nullable = false)
|
||||||
private SessionEntity session;
|
private BdsmGameEntity session;
|
||||||
|
|
||||||
public AktiveSperre toSperre(List<Mitspieler> mitspielerList) {
|
public AktiveSperre toSperre(List<Mitspieler> mitspielerList) {
|
||||||
AktiveSperre sperre = new AktiveSperre();
|
AktiveSperre sperre = new AktiveSperre();
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package de.oaa.xxx.games.bdsm.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "bdsm_defaults")
|
||||||
|
public class BdsmDefaultsEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "user_id")
|
||||||
|
private UUID userId;
|
||||||
|
|
||||||
|
@Column(length = 100)
|
||||||
|
private String spieltMit;
|
||||||
|
|
||||||
|
@Column(length = 200)
|
||||||
|
private String rollen;
|
||||||
|
|
||||||
|
@Column(length = 200)
|
||||||
|
private String werkzeuge;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package de.oaa.xxx.games.bdsm.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "bdsm_einladung")
|
||||||
|
public class BdsmEinladungEntity {
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
PENDING, ACCEPTED_OWN, ACCEPTED_HOST, DECLINED, CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column
|
||||||
|
private UUID einladungId;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private UUID setupId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private UUID sessionId;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private UUID inviterId;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private UUID inviteeId;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private int slotIndex;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 20)
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.oaa.xxx.session.entity;
|
package de.oaa.xxx.games.bdsm.entity;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
@@ -18,7 +18,7 @@ import java.util.UUID;
|
|||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "session")
|
@Table(name = "session")
|
||||||
public class SessionEntity {
|
public class BdsmGameEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Column
|
@Column
|
||||||
@@ -47,10 +47,16 @@ public class SessionEntity {
|
|||||||
private String aufgaben;
|
private String aufgaben;
|
||||||
@Column
|
@Column
|
||||||
private Double zeitfaktorZeitstrafen;
|
private Double zeitfaktorZeitstrafen;
|
||||||
|
@Column(columnDefinition = "TEXT")
|
||||||
|
private String activeTaskJson;
|
||||||
|
@Column
|
||||||
|
private LocalDateTime taskStartedAt;
|
||||||
|
@Column
|
||||||
|
private UUID setupId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SessionEntity[sessionId=" + sessionId + ", userId=" + userId
|
return "BdsmGameEntity[sessionId=" + sessionId + ", userId=" + userId
|
||||||
+ ", level=" + level + ", aufgaben=" + aufgabenAufAktuellemLevel + "/" + aufgabenProLevel
|
+ ", level=" + level + ", aufgaben=" + aufgabenAufAktuellemLevel + "/" + aufgabenProLevel
|
||||||
+ ", pStrafe=" + wahrscheinlichkeitStrafe + "%, pSperre=" + wahrscheinlichkeitSperre + "%"
|
+ ", pStrafe=" + wahrscheinlichkeitStrafe + "%, pSperre=" + wahrscheinlichkeitSperre + "%"
|
||||||
+ ", zeitfaktor=" + zeitfaktorZeitstrafen + ", start=" + startZeit + "]";
|
+ ", zeitfaktor=" + zeitfaktorZeitstrafen + ", start=" + startZeit + "]";
|
||||||
@@ -1,77 +1,83 @@
|
|||||||
package de.oaa.xxx.session.entity;
|
package de.oaa.xxx.games.bdsm.entity;
|
||||||
|
|
||||||
import de.oaa.xxx.session.GeschlechtEnum;
|
import de.oaa.xxx.games.bdsm.GeschlechtEnum;
|
||||||
import de.oaa.xxx.session.Mitspieler;
|
import de.oaa.xxx.games.bdsm.Mitspieler;
|
||||||
import de.oaa.xxx.session.RolleEnum;
|
import de.oaa.xxx.games.bdsm.RolleEnum;
|
||||||
import de.oaa.xxx.session.Werkzeug;
|
import de.oaa.xxx.games.bdsm.Werkzeug;
|
||||||
import jakarta.persistence.CollectionTable;
|
import jakarta.persistence.CollectionTable;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.ElementCollection;
|
import jakarta.persistence.ElementCollection;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EnumType;
|
import jakarta.persistence.EnumType;
|
||||||
import jakarta.persistence.Enumerated;
|
import jakarta.persistence.Enumerated;
|
||||||
import jakarta.persistence.FetchType;
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "mitspieler")
|
@Table(name = "mitspieler")
|
||||||
public class MitspielerEntity {
|
public class MitspielerEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Column
|
@Column
|
||||||
private UUID mitspielerId;
|
private UUID mitspielerId;
|
||||||
@Column
|
@Column
|
||||||
private String name;
|
private UUID userId;
|
||||||
@Enumerated(EnumType.STRING)
|
@Column
|
||||||
@Column
|
private boolean eigenesGeraet;
|
||||||
private GeschlechtEnum geschlecht;
|
@Column
|
||||||
@Enumerated(EnumType.STRING)
|
private String name;
|
||||||
@ElementCollection(targetClass = Werkzeug.class, fetch = FetchType.EAGER)
|
@Enumerated(EnumType.STRING)
|
||||||
@CollectionTable(name = "mitspieler_werkzeuge", joinColumns = @JoinColumn(name = "mitspielerId"))
|
@Column
|
||||||
@Column(name = "werkzeug")
|
private GeschlechtEnum geschlecht;
|
||||||
private List<Werkzeug> werkzeuge = new ArrayList<>();
|
@Enumerated(EnumType.STRING)
|
||||||
@Enumerated(EnumType.STRING)
|
@ElementCollection(targetClass = Werkzeug.class, fetch = FetchType.EAGER)
|
||||||
@ElementCollection(targetClass = GeschlechtEnum.class, fetch = FetchType.EAGER)
|
@CollectionTable(name = "mitspieler_werkzeuge", joinColumns = @JoinColumn(name = "mitspielerId"))
|
||||||
@CollectionTable(name = "mitspieler_spieltMit", joinColumns = @JoinColumn(name = "mitspielerId"))
|
@Column(name = "werkzeug")
|
||||||
@Column(name = "geschlecht")
|
private List<Werkzeug> werkzeuge = new ArrayList<>();
|
||||||
private List<GeschlechtEnum> spieltMit = new ArrayList<>();
|
@Enumerated(EnumType.STRING)
|
||||||
@Enumerated(EnumType.STRING)
|
@ElementCollection(targetClass = GeschlechtEnum.class, fetch = FetchType.EAGER)
|
||||||
@ElementCollection(targetClass = RolleEnum.class, fetch = FetchType.EAGER)
|
@CollectionTable(name = "mitspieler_spieltMit", joinColumns = @JoinColumn(name = "mitspielerId"))
|
||||||
@CollectionTable(name = "mitspieler_rollen", joinColumns = @JoinColumn(name = "mitspielerId"))
|
@Column(name = "geschlecht")
|
||||||
@Column(name = "rolle")
|
private List<GeschlechtEnum> spieltMit = new ArrayList<>();
|
||||||
private List<RolleEnum> rollen = new ArrayList<>();
|
@Enumerated(EnumType.STRING)
|
||||||
@ManyToOne
|
@ElementCollection(targetClass = RolleEnum.class, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "sessionId", nullable = false)
|
@CollectionTable(name = "mitspieler_rollen", joinColumns = @JoinColumn(name = "mitspielerId"))
|
||||||
private SessionEntity session;
|
@Column(name = "rolle")
|
||||||
@OneToMany(mappedBy = "mitspieler", fetch = FetchType.EAGER)
|
private List<RolleEnum> rollen = new ArrayList<>();
|
||||||
private List<AktiveSperreEntity> aktiveSperren = new ArrayList<>();
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "sessionId", nullable = false)
|
||||||
@Override
|
private BdsmGameEntity session;
|
||||||
public String toString() {
|
@OneToMany(mappedBy = "mitspieler", fetch = FetchType.EAGER)
|
||||||
return "MitspielerEntity[mitspielerId=" + mitspielerId + ", name=" + name
|
private List<AktiveSperreEntity> aktiveSperren = new ArrayList<>();
|
||||||
+ ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]";
|
|
||||||
}
|
@Override
|
||||||
|
public String toString() {
|
||||||
public Mitspieler toMitspieler() {
|
return "MitspielerEntity[mitspielerId=" + mitspielerId + ", name=" + name
|
||||||
Mitspieler mitspieler = new Mitspieler();
|
+ ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]";
|
||||||
mitspieler.setGeschlecht(geschlecht);
|
}
|
||||||
mitspieler.setId(mitspielerId);
|
|
||||||
mitspieler.setName(name);
|
public Mitspieler toMitspieler() {
|
||||||
mitspieler.setRollen(rollen);
|
Mitspieler mitspieler = new Mitspieler();
|
||||||
mitspieler.setSpieltMit(spieltMit);
|
mitspieler.setGeschlecht(geschlecht);
|
||||||
mitspieler.setVerfuegbareWerkzeuge(new ArrayList<>(werkzeuge));
|
mitspieler.setId(mitspielerId);
|
||||||
return mitspieler;
|
mitspieler.setUserId(userId);
|
||||||
}
|
mitspieler.setEigenesGeraet(eigenesGeraet);
|
||||||
}
|
mitspieler.setName(name);
|
||||||
|
mitspieler.setRollen(rollen);
|
||||||
|
mitspieler.setSpieltMit(spieltMit);
|
||||||
|
mitspieler.setVerfuegbareWerkzeuge(new ArrayList<>(werkzeuge));
|
||||||
|
return mitspieler;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.oaa.xxx.session.repository;
|
package de.oaa.xxx.games.bdsm.repository;
|
||||||
|
|
||||||
import de.oaa.xxx.session.entity.AktiveSperreEntity;
|
import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.oaa.xxx.games.bdsm.repository;
|
||||||
|
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface BdsmDefaultsRepository extends JpaRepository<BdsmDefaultsEntity, UUID> {
|
||||||
|
Optional<BdsmDefaultsEntity> findByUserId(UUID userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.oaa.xxx.games.bdsm.repository;
|
||||||
|
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity;
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface BdsmEinladungRepository extends JpaRepository<BdsmEinladungEntity, UUID> {
|
||||||
|
|
||||||
|
List<BdsmEinladungEntity> findBySetupId(UUID setupId);
|
||||||
|
|
||||||
|
List<BdsmEinladungEntity> findByInviteeIdAndStatus(UUID inviteeId, Status status);
|
||||||
|
|
||||||
|
List<BdsmEinladungEntity> findByInviterIdAndStatus(UUID inviterId, Status status);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.oaa.xxx.games.bdsm.repository;
|
||||||
|
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface BdsmGameRepository extends JpaRepository<BdsmGameEntity, UUID> {
|
||||||
|
|
||||||
|
Optional<BdsmGameEntity> findByUserId(UUID userId);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.oaa.xxx.session.repository;
|
package de.oaa.xxx.games.bdsm.repository;
|
||||||
|
|
||||||
import de.oaa.xxx.session.entity.MitspielerEntity;
|
import de.oaa.xxx.games.bdsm.entity.MitspielerEntity;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.oaa.xxx.session.sperre;
|
package de.oaa.xxx.games.bdsm.sperre;
|
||||||
|
|
||||||
import de.oaa.xxx.session.Callback;
|
import de.oaa.xxx.games.bdsm.Callback;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
package de.oaa.xxx.session.sperre;
|
package de.oaa.xxx.games.bdsm.sperre;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import de.oaa.xxx.session.aufgaben.AufgabenList;
|
import de.oaa.xxx.games.bdsm.aufgaben.AufgabenList;
|
||||||
import de.oaa.xxx.session.aufgaben.Sperre;
|
import de.oaa.xxx.games.bdsm.aufgaben.Sperre;
|
||||||
import de.oaa.xxx.session.entity.AktiveSperreEntity;
|
import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity;
|
||||||
import de.oaa.xxx.session.entity.MitspielerEntity;
|
import de.oaa.xxx.games.bdsm.entity.MitspielerEntity;
|
||||||
import de.oaa.xxx.session.entity.SessionEntity;
|
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
|
||||||
import de.oaa.xxx.session.repository.AktiveSperreRepository;
|
import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository;
|
||||||
import de.oaa.xxx.session.repository.MitspielerRepository;
|
import de.oaa.xxx.games.bdsm.repository.MitspielerRepository;
|
||||||
import de.oaa.xxx.session.repository.SessionRepository;
|
import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -19,9 +19,9 @@ public class SperreVerarbeiten {
|
|||||||
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
public void sperreAnwenden(SperreCallback callback, SessionRepository sessionRepository,
|
public void sperreAnwenden(SperreCallback callback, BdsmGameRepository sessionRepository,
|
||||||
MitspielerRepository mitspielerRepository, AktiveSperreRepository sperreRepository) throws Exception {
|
MitspielerRepository mitspielerRepository, AktiveSperreRepository sperreRepository) throws Exception {
|
||||||
SessionEntity session = sessionRepository.findById(callback.getSessionId()).orElse(null);
|
BdsmGameEntity session = sessionRepository.findById(callback.getSessionId()).orElse(null);
|
||||||
MitspielerEntity mitspieler = mitspielerRepository.findById(callback.getSpielerId()).orElse(null);
|
MitspielerEntity mitspieler = mitspielerRepository.findById(callback.getSpielerId()).orElse(null);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
AufgabenList aufgaben = objectMapper.readValue(session.getAufgaben(), AufgabenList.class);
|
AufgabenList aufgaben = objectMapper.readValue(session.getAufgaben(), AufgabenList.class);
|
||||||
@@ -56,7 +56,7 @@ public class SperreVerarbeiten {
|
|||||||
sperreRepository.save(verlaengern);
|
sperreRepository.save(verlaengern);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fill(SperreCallback callback, SessionEntity session, MitspielerEntity mitspieler,
|
private void fill(SperreCallback callback, BdsmGameEntity session, MitspielerEntity mitspieler,
|
||||||
Sperre sperre, AktiveSperreEntity aktiv) {
|
Sperre sperre, AktiveSperreEntity aktiv) {
|
||||||
aktiv.setAktiveSperreId(UUID.randomUUID());
|
aktiv.setAktiveSperreId(UUID.randomUUID());
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
@@ -70,7 +70,7 @@ public class SperreVerarbeiten {
|
|||||||
aktiv.setReleaseText(callback.getReleaseText());
|
aktiv.setReleaseText(callback.getReleaseText());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer berechneDauer(SessionEntity session, Sperre sperre) {
|
private Integer berechneDauer(BdsmGameEntity session, Sperre sperre) {
|
||||||
Integer minuten = 30;
|
Integer minuten = 30;
|
||||||
if (sperre.getMinutenVon() != null) {
|
if (sperre.getMinutenVon() != null) {
|
||||||
if (sperre.getMinutenBis() != null) {
|
if (sperre.getMinutenBis() != null) {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.oaa.xxx.session.sperre;
|
package de.oaa.xxx.games.bdsm.sperre;
|
||||||
|
|
||||||
import de.oaa.xxx.session.Callback;
|
import de.oaa.xxx.games.bdsm.Callback;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -22,9 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import de.oaa.xxx.games.chastity.cardlock.CardlockRepository;
|
import de.oaa.xxx.games.chastity.cardlock.CardlockRepository;
|
||||||
import de.oaa.xxx.social.SseService;
|
import de.oaa.xxx.social.SystemMessageService;
|
||||||
import de.oaa.xxx.social.entity.MessageEntity;
|
|
||||||
import de.oaa.xxx.social.repository.MessageRepository;
|
|
||||||
import de.oaa.xxx.user.UserRepository;
|
import de.oaa.xxx.user.UserRepository;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -34,8 +32,7 @@ public class LockeeInvitationController {
|
|||||||
private final LockeeInvitationRepository lockeeInvitationRepository;
|
private final LockeeInvitationRepository lockeeInvitationRepository;
|
||||||
private final CardlockRepository cardlockRepository;
|
private final CardlockRepository cardlockRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final MessageRepository messageRepository;
|
private final SystemMessageService systemMessageService;
|
||||||
private final SseService sseService;
|
|
||||||
|
|
||||||
@Value("${app.base-url:http://localhost:8080}")
|
@Value("${app.base-url:http://localhost:8080}")
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
@@ -45,27 +42,15 @@ public class LockeeInvitationController {
|
|||||||
public LockeeInvitationController(LockeeInvitationRepository lockeeInvitationRepository,
|
public LockeeInvitationController(LockeeInvitationRepository lockeeInvitationRepository,
|
||||||
CardlockRepository cardlockRepository,
|
CardlockRepository cardlockRepository,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
MessageRepository messageRepository,
|
SystemMessageService systemMessageService) {
|
||||||
SseService sseService) {
|
|
||||||
this.lockeeInvitationRepository = lockeeInvitationRepository;
|
this.lockeeInvitationRepository = lockeeInvitationRepository;
|
||||||
this.cardlockRepository = cardlockRepository;
|
this.cardlockRepository = cardlockRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.messageRepository = messageRepository;
|
this.systemMessageService = systemMessageService;
|
||||||
this.sseService = sseService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl) {
|
private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, de.oaa.xxx.social.entity.MessageCause cause) {
|
||||||
MessageEntity msg = new MessageEntity();
|
systemMessageService.send(senderId, receiverId, text, targetUrl, cause);
|
||||||
msg.setMessageId(UUID.randomUUID());
|
|
||||||
msg.setSenderId(senderId);
|
|
||||||
msg.setReceiverId(receiverId);
|
|
||||||
msg.setText(text);
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
if (targetUrl != null) msg.setTargetUrl(targetUrl);
|
|
||||||
messageRepository.save(msg);
|
|
||||||
long unread = messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(receiverId, true);
|
|
||||||
sseService.push(receiverId, "NOTIFICATION", java.util.Map.of("unreadCount", unread, "text", text));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateUnlockCode(int lines) {
|
private String generateUnlockCode(int lines) {
|
||||||
@@ -154,7 +139,7 @@ public class LockeeInvitationController {
|
|||||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||||
sendMessage(myId, inv.getLockeeUserId(),
|
sendMessage(myId, inv.getLockeeUserId(),
|
||||||
me.getName() + " hat die Lockee-Einladung für das Lock „" + lockName + "\" zurückgezogen.",
|
me.getName() + " hat die Lockee-Einladung für das Lock „" + lockName + "\" zurückgezogen.",
|
||||||
null);
|
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
@@ -248,7 +233,7 @@ public class LockeeInvitationController {
|
|||||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||||
sendMessage(myId, inv.getKeyholderUserId(),
|
sendMessage(myId, inv.getKeyholderUserId(),
|
||||||
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" angenommen.",
|
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" angenommen.",
|
||||||
"/keyholder.html");
|
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
|
|
||||||
return ResponseEntity.ok(Map.of(
|
return ResponseEntity.ok(Map.of(
|
||||||
"lockId", lock.getLockId().toString(),
|
"lockId", lock.getLockId().toString(),
|
||||||
@@ -278,7 +263,7 @@ public class LockeeInvitationController {
|
|||||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||||
sendMessage(myId, inv.getKeyholderUserId(),
|
sendMessage(myId, inv.getKeyholderUserId(),
|
||||||
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" abgelehnt.",
|
me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" abgelehnt.",
|
||||||
null);
|
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import de.oaa.xxx.games.chastity.KeyholderInvitationEntity;
|
|||||||
import de.oaa.xxx.games.chastity.KeyholderInvitationRepository;
|
import de.oaa.xxx.games.chastity.KeyholderInvitationRepository;
|
||||||
import de.oaa.xxx.games.chastity.LockeeInvitationEntity;
|
import de.oaa.xxx.games.chastity.LockeeInvitationEntity;
|
||||||
import de.oaa.xxx.games.chastity.LockeeInvitationRepository;
|
import de.oaa.xxx.games.chastity.LockeeInvitationRepository;
|
||||||
import de.oaa.xxx.games.chastity.history.LockHistoryRepository;
|
import de.oaa.xxx.games.history.GameHistoryRepository;
|
||||||
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
|
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
|
||||||
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
|
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
|
||||||
import de.oaa.xxx.games.chastity.tasks.Task;
|
import de.oaa.xxx.games.chastity.tasks.Task;
|
||||||
@@ -49,9 +49,7 @@ import de.oaa.xxx.games.chastity.verification.VerificationEntity;
|
|||||||
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
|
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
|
||||||
import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity;
|
import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity;
|
||||||
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
|
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
|
||||||
import de.oaa.xxx.social.SseService;
|
import de.oaa.xxx.social.SystemMessageService;
|
||||||
import de.oaa.xxx.social.entity.MessageEntity;
|
|
||||||
import de.oaa.xxx.social.repository.MessageRepository;
|
|
||||||
import de.oaa.xxx.user.UserRepository;
|
import de.oaa.xxx.user.UserRepository;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -65,15 +63,14 @@ public class CardLockController {
|
|||||||
private final VerificationRepository verificationRepository;
|
private final VerificationRepository verificationRepository;
|
||||||
private final VerificationVoteRepository verificationVoteRepository;
|
private final VerificationVoteRepository verificationVoteRepository;
|
||||||
private final HygieneViolationRepository hygieneViolationRepository;
|
private final HygieneViolationRepository hygieneViolationRepository;
|
||||||
private final MessageRepository messageRepository;
|
|
||||||
private final LockeeInvitationRepository lockeeInvitationRepository;
|
private final LockeeInvitationRepository lockeeInvitationRepository;
|
||||||
private final AssignedTaskRepository assignedTaskRepository;
|
private final AssignedTaskRepository assignedTaskRepository;
|
||||||
private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository;
|
private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository;
|
||||||
private final CommunityTaskVoteRepository communityTaskVoteRepository;
|
private final CommunityTaskVoteRepository communityTaskVoteRepository;
|
||||||
private final UnlockCodeHistoryRepository unlockCodeHistoryRepository;
|
private final UnlockCodeHistoryRepository unlockCodeHistoryRepository;
|
||||||
private final UnlockCodeHistoryService unlockCodeHistoryService;
|
private final UnlockCodeHistoryService unlockCodeHistoryService;
|
||||||
private final LockHistoryRepository lockHistoryRepository;
|
private final GameHistoryRepository gameHistoryRepository;
|
||||||
private final SseService sseService;
|
private final SystemMessageService systemMessageService;
|
||||||
|
|
||||||
@Value("${app.base-url:http://localhost:8080}")
|
@Value("${app.base-url:http://localhost:8080}")
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
@@ -85,15 +82,14 @@ public class CardLockController {
|
|||||||
VerificationRepository verificationRepository,
|
VerificationRepository verificationRepository,
|
||||||
VerificationVoteRepository verificationVoteRepository,
|
VerificationVoteRepository verificationVoteRepository,
|
||||||
HygieneViolationRepository hygieneViolationRepository,
|
HygieneViolationRepository hygieneViolationRepository,
|
||||||
MessageRepository messageRepository,
|
|
||||||
LockeeInvitationRepository lockeeInvitationRepository,
|
LockeeInvitationRepository lockeeInvitationRepository,
|
||||||
AssignedTaskRepository assignedTaskRepository,
|
AssignedTaskRepository assignedTaskRepository,
|
||||||
KeyholderTaskChoiceRepository keyholderTaskChoiceRepository,
|
KeyholderTaskChoiceRepository keyholderTaskChoiceRepository,
|
||||||
CommunityTaskVoteRepository communityTaskVoteRepository,
|
CommunityTaskVoteRepository communityTaskVoteRepository,
|
||||||
UnlockCodeHistoryRepository unlockCodeHistoryRepository,
|
UnlockCodeHistoryRepository unlockCodeHistoryRepository,
|
||||||
UnlockCodeHistoryService unlockCodeHistoryService,
|
UnlockCodeHistoryService unlockCodeHistoryService,
|
||||||
LockHistoryRepository lockHistoryRepository,
|
GameHistoryRepository gameHistoryRepository,
|
||||||
SseService sseService) {
|
SystemMessageService systemMessageService) {
|
||||||
this.cardlockRepository = cardlockRepository;
|
this.cardlockRepository = cardlockRepository;
|
||||||
this.cardLockRepository = cardLockRepository;
|
this.cardLockRepository = cardLockRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
@@ -101,15 +97,14 @@ public class CardLockController {
|
|||||||
this.verificationRepository = verificationRepository;
|
this.verificationRepository = verificationRepository;
|
||||||
this.verificationVoteRepository = verificationVoteRepository;
|
this.verificationVoteRepository = verificationVoteRepository;
|
||||||
this.hygieneViolationRepository = hygieneViolationRepository;
|
this.hygieneViolationRepository = hygieneViolationRepository;
|
||||||
this.messageRepository = messageRepository;
|
|
||||||
this.lockeeInvitationRepository = lockeeInvitationRepository;
|
this.lockeeInvitationRepository = lockeeInvitationRepository;
|
||||||
this.assignedTaskRepository = assignedTaskRepository;
|
this.assignedTaskRepository = assignedTaskRepository;
|
||||||
this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository;
|
this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository;
|
||||||
this.communityTaskVoteRepository = communityTaskVoteRepository;
|
this.communityTaskVoteRepository = communityTaskVoteRepository;
|
||||||
this.unlockCodeHistoryRepository = unlockCodeHistoryRepository;
|
this.unlockCodeHistoryRepository = unlockCodeHistoryRepository;
|
||||||
this.unlockCodeHistoryService = unlockCodeHistoryService;
|
this.unlockCodeHistoryService = unlockCodeHistoryService;
|
||||||
this.lockHistoryRepository = lockHistoryRepository;
|
this.gameHistoryRepository = gameHistoryRepository;
|
||||||
this.sseService = sseService;
|
this.systemMessageService = systemMessageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
record CreateCardLockRequest(
|
record CreateCardLockRequest(
|
||||||
@@ -194,7 +189,7 @@ public class CardLockController {
|
|||||||
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock";
|
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock";
|
||||||
sendMessage(myId, lockee.getUserId(),
|
sendMessage(myId, lockee.getUserId(),
|
||||||
me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.",
|
me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.",
|
||||||
"/einladungen.html");
|
"/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
|
|
||||||
return ResponseEntity.ok(Map.of(
|
return ResponseEntity.ok(Map.of(
|
||||||
"lockId", lock.getLockId().toString(),
|
"lockId", lock.getLockId().toString(),
|
||||||
@@ -257,7 +252,7 @@ public class CardLockController {
|
|||||||
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock";
|
String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock";
|
||||||
sendMessage(me.getUserId(), kh.getUserId(),
|
sendMessage(me.getUserId(), kh.getUserId(),
|
||||||
me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.",
|
me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.",
|
||||||
"/einladungen.html");
|
"/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
|
|
||||||
keyholderPending = true;
|
keyholderPending = true;
|
||||||
}
|
}
|
||||||
@@ -282,7 +277,7 @@ public class CardLockController {
|
|||||||
var l = lockOpt.get();
|
var l = lockOpt.get();
|
||||||
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
||||||
|
|
||||||
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository);
|
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository);
|
||||||
CardDTO dto = service.getNextCard();
|
CardDTO dto = service.getNextCard();
|
||||||
if (dto == null) return ResponseEntity.status(409).body(Map.of("error", "Keine Karte verfügbar"));
|
if (dto == null) return ResponseEntity.status(409).body(Map.of("error", "Keine Karte verfügbar"));
|
||||||
|
|
||||||
@@ -300,7 +295,7 @@ public class CardLockController {
|
|||||||
userRepository.findById(l.getKeyholder()).ifPresent(kh ->
|
userRepository.findById(l.getKeyholder()).ifPresent(kh ->
|
||||||
sendMessage(l.getLockee(), kh.getUserId(),
|
sendMessage(l.getLockee(), kh.getUserId(),
|
||||||
"Deine Lockee hat eine Aufgaben-Karte gezogen – wähle eine Aufgabe aus.",
|
"Deine Lockee hat eine Aufgaben-Karte gezogen – wähle eine Aufgabe aus.",
|
||||||
"/keyholder.html"));
|
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE));
|
||||||
taskPending = "KEYHOLDER";
|
taskPending = "KEYHOLDER";
|
||||||
|
|
||||||
} else if ("COMMUNITY".equals(l.getTaskCardMode())) {
|
} else if ("COMMUNITY".equals(l.getTaskCardMode())) {
|
||||||
@@ -320,9 +315,14 @@ public class CardLockController {
|
|||||||
result.put("unlockCode", dto.unlockCode() != null ? dto.unlockCode() : "");
|
result.put("unlockCode", dto.unlockCode() != null ? dto.unlockCode() : "");
|
||||||
if (taskPending != null) result.put("taskPending", taskPending);
|
if (taskPending != null) result.put("taskPending", taskPending);
|
||||||
|
|
||||||
// Grüne Karte → Entsperrcode-Historie speichern
|
// Grüne Karte → Entsperrcode-Historie speichern + Keyholder benachrichtigen
|
||||||
if (dto.unlockCode() != null && !dto.unlockCode().isBlank()) {
|
if (dto.unlockCode() != null && !dto.unlockCode().isBlank()) {
|
||||||
unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), dto.unlockCode(), "GREEN_CARD");
|
unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), dto.unlockCode(), "GREEN_CARD");
|
||||||
|
if (l.getKeyholder() != null) {
|
||||||
|
sendMessage(myId, l.getKeyholder(),
|
||||||
|
meOpt.get().getName() + " hat die grüne Karte gezogen! Der Entsperrcode wurde angezeigt.",
|
||||||
|
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok(result);
|
return ResponseEntity.ok(result);
|
||||||
@@ -417,7 +417,7 @@ public class CardLockController {
|
|||||||
var l = lockOpt.get();
|
var l = lockOpt.get();
|
||||||
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
||||||
|
|
||||||
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository);
|
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository);
|
||||||
service.clearTask();
|
service.clearTask();
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -434,8 +434,16 @@ public class CardLockController {
|
|||||||
var l = lockOpt.get();
|
var l = lockOpt.get();
|
||||||
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
||||||
|
|
||||||
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository);
|
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository);
|
||||||
service.putBackGreen();
|
service.putBackGreen();
|
||||||
|
|
||||||
|
// Grüne Karte zurückgelegt → Keyholder benachrichtigen
|
||||||
|
if (l.getKeyholder() != null) {
|
||||||
|
sendMessage(myId, l.getKeyholder(),
|
||||||
|
meOpt.get().getName() + " hat die grüne Karte zurückgelegt und bleibt im Lock.",
|
||||||
|
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +565,7 @@ public class CardLockController {
|
|||||||
lockDirty = true;
|
lockDirty = true;
|
||||||
sendMessage(l.getKeyholder(), l.getLockee(),
|
sendMessage(l.getKeyholder(), l.getLockee(),
|
||||||
"Die dir gestellte Aufgabe ist abgelaufen, ohne dass du reagiert hast. Die Strafe wurde automatisch angewendet.",
|
"Die dir gestellte Aufgabe ist abgelaufen, ohne dass du reagiert hast. Die Strafe wurde automatisch angewendet.",
|
||||||
"/activelock.html?lockId=" + l.getLockId());
|
"/activelock.html?lockId=" + l.getLockId(), de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
}
|
}
|
||||||
if (lockDirty) cardlockRepository.save(l);
|
if (lockDirty) cardlockRepository.save(l);
|
||||||
|
|
||||||
@@ -695,7 +703,7 @@ public class CardLockController {
|
|||||||
var lockee = meOpt.get();
|
var lockee = meOpt.get();
|
||||||
sendMessage(myId, lock.getKeyholder(),
|
sendMessage(myId, lock.getKeyholder(),
|
||||||
"📸 " + lockee.getName() + " hat eine Verifikation eingereicht.",
|
"📸 " + lockee.getName() + " hat eine Verifikation eingereicht.",
|
||||||
"/keyholder.html");
|
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
@@ -793,14 +801,9 @@ public class CardLockController {
|
|||||||
if (lockOpt.isPresent()) {
|
if (lockOpt.isPresent()) {
|
||||||
var lock = lockOpt.get();
|
var lock = lockOpt.get();
|
||||||
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock";
|
||||||
MessageEntity msg = new MessageEntity();
|
sendMessage(myId, lock.getLockee(),
|
||||||
msg.setMessageId(UUID.randomUUID());
|
me.getName() + " hat die Einladung als Keyholder*In für das Lock „" + lockName + "\" abgelehnt.",
|
||||||
msg.setSenderId(myId);
|
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
msg.setReceiverId(lock.getLockee());
|
|
||||||
msg.setText(me.getName() + " hat die Einladung als Keyholder*In für das Lock „" + lockName + "\" abgelehnt.");
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
messageRepository.save(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
@@ -855,14 +858,9 @@ public class CardLockController {
|
|||||||
|
|
||||||
String lockName = lockOpt.get().getName() != null && !lockOpt.get().getName().isBlank()
|
String lockName = lockOpt.get().getName() != null && !lockOpt.get().getName().isBlank()
|
||||||
? lockOpt.get().getName() : "Unbenanntes Lock";
|
? lockOpt.get().getName() : "Unbenanntes Lock";
|
||||||
MessageEntity msg = new MessageEntity();
|
sendMessage(myId, inv.getKeyholderUserId(),
|
||||||
msg.setMessageId(UUID.randomUUID());
|
me.getName() + " hat die Keyholder-Einladung für das Lock „" + lockName + "\" zurückgezogen.",
|
||||||
msg.setSenderId(myId);
|
null, de.oaa.xxx.social.entity.MessageCause.INVITATION);
|
||||||
msg.setReceiverId(inv.getKeyholderUserId());
|
|
||||||
msg.setText(me.getName() + " hat die Keyholder-Einladung für das Lock „" + lockName + "\" zurückgezogen.");
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
messageRepository.save(msg);
|
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1069,7 +1067,7 @@ public class CardLockController {
|
|||||||
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build();
|
||||||
|
|
||||||
// Entsperrung protokollieren (History + XP) – gültig nur wenn Keyholder vorhanden und kein Auto-Notfall
|
// Entsperrung protokollieren (History + XP) – gültig nur wenn Keyholder vorhanden und kein Auto-Notfall
|
||||||
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository);
|
CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository);
|
||||||
service.unlock(l.getUnlockCode());
|
service.unlock(l.getUnlockCode());
|
||||||
|
|
||||||
var verifications = verificationRepository.findByLockId(lockId);
|
var verifications = verificationRepository.findByLockId(lockId);
|
||||||
@@ -1124,14 +1122,8 @@ public class CardLockController {
|
|||||||
? me.getName() + " hat " + toAdd.size() + " Karte(n) zu deinem Lock hinzugefügt: " + detail + "."
|
? me.getName() + " hat " + toAdd.size() + " Karte(n) zu deinem Lock hinzugefügt: " + detail + "."
|
||||||
: me.getName() + " hat Karten zu deinem Lock hinzugefügt.";
|
: me.getName() + " hat Karten zu deinem Lock hinzugefügt.";
|
||||||
|
|
||||||
MessageEntity msg = new MessageEntity();
|
sendMessage(myId, l.getLockee(), msgText, "/activelock.html?lockId=" + lockId,
|
||||||
msg.setMessageId(UUID.randomUUID());
|
de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
msg.setSenderId(myId);
|
|
||||||
msg.setReceiverId(l.getLockee());
|
|
||||||
msg.setText(msgText);
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
messageRepository.save(msg);
|
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1189,14 +1181,8 @@ public class CardLockController {
|
|||||||
? me.getName() + " hat " + removed.size() + " Karte(n) aus deinem Lock entfernt: " + detail + "."
|
? me.getName() + " hat " + removed.size() + " Karte(n) aus deinem Lock entfernt: " + detail + "."
|
||||||
: me.getName() + " hat Karten aus deinem Lock entfernt.";
|
: me.getName() + " hat Karten aus deinem Lock entfernt.";
|
||||||
|
|
||||||
MessageEntity msg = new MessageEntity();
|
sendMessage(myId, l.getLockee(), msgText, "/activelock.html?lockId=" + lockId,
|
||||||
msg.setMessageId(UUID.randomUUID());
|
de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
msg.setSenderId(myId);
|
|
||||||
msg.setReceiverId(l.getLockee());
|
|
||||||
msg.setText(msgText);
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
messageRepository.save(msg);
|
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1221,19 +1207,8 @@ public class CardLockController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl) {
|
private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, de.oaa.xxx.social.entity.MessageCause cause) {
|
||||||
if (senderId == null || receiverId == null) return;
|
systemMessageService.send(senderId, receiverId, text, targetUrl, cause);
|
||||||
MessageEntity msg = new MessageEntity();
|
|
||||||
msg.setMessageId(UUID.randomUUID());
|
|
||||||
msg.setSenderId(senderId);
|
|
||||||
msg.setReceiverId(receiverId);
|
|
||||||
msg.setText(text);
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
if (targetUrl != null) msg.setTargetUrl(targetUrl);
|
|
||||||
messageRepository.save(msg);
|
|
||||||
long unread = messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(receiverId, true);
|
|
||||||
sseService.push(receiverId, "NOTIFICATION", java.util.Map.of("unreadCount", unread, "text", text));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/cardlock/unlock-history")
|
@GetMapping("/cardlock/unlock-history")
|
||||||
@@ -1305,7 +1280,7 @@ public class CardLockController {
|
|||||||
sendMessage(me.getUserId(), l.getLockee(),
|
sendMessage(me.getUserId(), l.getLockee(),
|
||||||
me.getName() + " hat dir eine Aufgabe gestellt. Du hast " +
|
me.getName() + " hat dir eine Aufgabe gestellt. Du hast " +
|
||||||
req.acceptDeadlineMinutes() + " Minuten, um sie anzunehmen.",
|
req.acceptDeadlineMinutes() + " Minuten, um sie anzunehmen.",
|
||||||
"/activelock.html?lockId=" + lockId);
|
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1365,7 +1340,7 @@ public class CardLockController {
|
|||||||
assignedTaskRepository.save(task);
|
assignedTaskRepository.save(task);
|
||||||
cardlockRepository.save(l);
|
cardlockRepository.save(l);
|
||||||
|
|
||||||
sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe angenommen.", "/keyholder.html");
|
sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe angenommen.", "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1397,7 +1372,7 @@ public class CardLockController {
|
|||||||
assignedTaskRepository.save(task);
|
assignedTaskRepository.save(task);
|
||||||
cardlockRepository.save(l);
|
cardlockRepository.save(l);
|
||||||
|
|
||||||
sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.", "/keyholder.html");
|
sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.", "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1465,7 +1440,7 @@ public class CardLockController {
|
|||||||
until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) +
|
until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) +
|
||||||
" " + until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm")) +
|
" " + until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm")) +
|
||||||
" Uhr eingefroren.",
|
" Uhr eingefroren.",
|
||||||
"/activelock.html?lockId=" + lockId);
|
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1490,7 +1465,7 @@ public class CardLockController {
|
|||||||
cardlockRepository.save(l);
|
cardlockRepository.save(l);
|
||||||
|
|
||||||
sendMessage(myId, l.getLockee(), me.getName() + " hat dein Lock wieder entfroren.",
|
sendMessage(myId, l.getLockee(), me.getName() + " hat dein Lock wieder entfroren.",
|
||||||
"/activelock.html?lockId=" + lockId);
|
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1512,7 +1487,7 @@ public class CardLockController {
|
|||||||
|
|
||||||
sendMessage(myId, l.getLockee(),
|
sendMessage(myId, l.getLockee(),
|
||||||
"Dein Keyholder hat das Lock freigeschaltet. Du erhältst beim nächsten Laden deinen Entsperrcode.",
|
"Dein Keyholder hat das Lock freigeschaltet. Du erhältst beim nächsten Laden deinen Entsperrcode.",
|
||||||
"/activelock.html?lockId=" + lockId);
|
"/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
@@ -1542,7 +1517,7 @@ public class CardLockController {
|
|||||||
// Keyholderin benachrichtigen
|
// Keyholderin benachrichtigen
|
||||||
sendMessage(myId, l.getKeyholder(),
|
sendMessage(myId, l.getKeyholder(),
|
||||||
"⚠️ NOTFALL: " + me.getName() + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde, sonst öffnet sich das Lock automatisch.",
|
"⚠️ NOTFALL: " + me.getName() + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde, sonst öffnet sich das Lock automatisch.",
|
||||||
"/keyholder.html");
|
"/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY);
|
||||||
}
|
}
|
||||||
cardlockRepository.save(l);
|
cardlockRepository.save(l);
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
|
|||||||
@@ -11,10 +11,9 @@ import java.util.stream.Collectors;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import de.oaa.xxx.games.chastity.LockType;
|
|
||||||
import de.oaa.xxx.games.chastity.ProcessLock;
|
import de.oaa.xxx.games.chastity.ProcessLock;
|
||||||
import de.oaa.xxx.games.chastity.history.LockHistoryEntity;
|
import de.oaa.xxx.games.history.GameHistoryEntity;
|
||||||
import de.oaa.xxx.games.chastity.history.LockHistoryRepository;
|
import de.oaa.xxx.games.history.GameHistoryRepository;
|
||||||
import de.oaa.xxx.games.chastity.tasks.Task;
|
import de.oaa.xxx.games.chastity.tasks.Task;
|
||||||
import de.oaa.xxx.games.chastity.verification.VerificationEntity;
|
import de.oaa.xxx.games.chastity.verification.VerificationEntity;
|
||||||
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
|
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
|
||||||
@@ -29,15 +28,15 @@ public class CardLockService extends ProcessLock {
|
|||||||
private VerificationRepository verificationRepository;
|
private VerificationRepository verificationRepository;
|
||||||
private VerificationVoteRepository verificationVoteRepository;
|
private VerificationVoteRepository verificationVoteRepository;
|
||||||
private CardLockRepository cardLockRepository;
|
private CardLockRepository cardLockRepository;
|
||||||
private LockHistoryRepository lockHistoryRepository;
|
private GameHistoryRepository gameHistoryRepository;
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository, VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository, LockHistoryRepository lockHistoryRepository, UserRepository userRepository) {
|
public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository, VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository, GameHistoryRepository gameHistoryRepository, UserRepository userRepository) {
|
||||||
this.lock = lock;
|
this.lock = lock;
|
||||||
this.verificationRepository = verificationRepository;
|
this.verificationRepository = verificationRepository;
|
||||||
this.verificationVoteRepository = verificationVoteRepository;
|
this.verificationVoteRepository = verificationVoteRepository;
|
||||||
this.cardLockRepository = cardLockRepository;
|
this.cardLockRepository = cardLockRepository;
|
||||||
this.lockHistoryRepository = lockHistoryRepository;
|
this.gameHistoryRepository = gameHistoryRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,28 +100,30 @@ public class CardLockService extends ProcessLock {
|
|||||||
|
|
||||||
public void unlock(String unlockCode) {
|
public void unlock(String unlockCode) {
|
||||||
this.lock.setUnlockTime(LocalDateTime.now());
|
this.lock.setUnlockTime(LocalDateTime.now());
|
||||||
// Self-Lock oder automatische Entsperrung ohne Keyholder-Zustimmung → ungültig
|
boolean valid = true;
|
||||||
boolean valid = lock.getKeyholder() != null && !lock.isEmergencyAutoUnlocked();
|
if (lock.isEmergencyAutoUnlocked()) {
|
||||||
if (!this.lock.isTestLock()) {
|
valid = false;
|
||||||
if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) {
|
LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)");
|
||||||
Set<LocalDate> verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream()
|
}
|
||||||
.filter(verification -> isValid(verification))
|
if (lock.isTestLock()) {
|
||||||
.map(verification -> verification.getVerificationTime().toLocalDate())
|
valid = false;
|
||||||
.collect(Collectors.toSet());
|
} else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) {
|
||||||
|
Set<LocalDate> verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream()
|
||||||
LocalDate current = this.lock.getStartTime().toLocalDate();
|
.filter(verification -> isValid(verification))
|
||||||
LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1);
|
.map(verification -> verification.getVerificationTime().toLocalDate())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
while (!current.isAfter(last)) {
|
|
||||||
if (!verifications.contains(current)) {
|
LocalDate current = this.lock.getStartTime().toLocalDate();
|
||||||
valid = false;
|
LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1);
|
||||||
break;
|
|
||||||
}
|
while (!current.isAfter(last)) {
|
||||||
current = current.plusDays(1);
|
if (!verifications.contains(current)) {
|
||||||
|
valid = false;
|
||||||
|
LOGGER.debug("Lock invalid - no daily verification on %s", current.toString());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
current = current.plusDays(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.setUnlockTime(LocalDateTime.now());
|
lock.setUnlockTime(LocalDateTime.now());
|
||||||
@@ -132,31 +133,18 @@ public class CardLockService extends ProcessLock {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes();
|
long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes();
|
||||||
|
|
||||||
// Eintrag für den Lockee
|
// Gemeinsamer History-Eintrag mit Teilnehmerliste
|
||||||
LockHistoryEntity lockeeEntry = new LockHistoryEntity();
|
GameHistoryEntity entry = new GameHistoryEntity();
|
||||||
lockeeEntry.setUserId(lock.getLockee());
|
entry.setGameType(de.oaa.xxx.games.history.GameType.CARDLOCK);
|
||||||
lockeeEntry.setLockedBy(lock.getKeyholder());
|
entry.setGameName(lock.getName());
|
||||||
lockeeEntry.setLockName(lock.getName());
|
entry.setStartTime(lock.getStartTime());
|
||||||
lockeeEntry.setStartTime(lock.getStartTime());
|
entry.setEndTime(lock.getUnlockTime());
|
||||||
lockeeEntry.setEndTime(lock.getUnlockTime());
|
entry.setDurationMinutes(durationMinutes);
|
||||||
lockeeEntry.setType(LockType.CARD);
|
entry.addParticipant(lock.getLockee(), de.oaa.xxx.games.history.GameRole.LOCKEE);
|
||||||
lockeeEntry.setDurationMinutes(durationMinutes);
|
|
||||||
lockeeEntry.setRole("LOCKEE");
|
|
||||||
lockHistoryRepository.save(lockeeEntry);
|
|
||||||
|
|
||||||
// Eintrag für die Keyholderin
|
|
||||||
if (lock.getKeyholder() != null) {
|
if (lock.getKeyholder() != null) {
|
||||||
LockHistoryEntity khEntry = new LockHistoryEntity();
|
entry.addParticipant(lock.getKeyholder(), de.oaa.xxx.games.history.GameRole.KEYHOLDER);
|
||||||
khEntry.setUserId(lock.getKeyholder());
|
|
||||||
khEntry.setLockedBy(lock.getLockee());
|
|
||||||
khEntry.setLockName(lock.getName());
|
|
||||||
khEntry.setStartTime(lock.getStartTime());
|
|
||||||
khEntry.setEndTime(lock.getUnlockTime());
|
|
||||||
khEntry.setType(LockType.CARD);
|
|
||||||
khEntry.setDurationMinutes(durationMinutes);
|
|
||||||
khEntry.setRole("KEYHOLDER");
|
|
||||||
lockHistoryRepository.save(khEntry);
|
|
||||||
}
|
}
|
||||||
|
gameHistoryRepository.save(entry);
|
||||||
|
|
||||||
int minutes = (int) durationMinutes;
|
int minutes = (int) durationMinutes;
|
||||||
userRepository.findById(lock.getLockee()).ifPresent(u -> {
|
userRepository.findById(lock.getLockee()).ifPresent(u -> {
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ package de.oaa.xxx.games.chastity.cardlock;
|
|||||||
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
|
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
|
||||||
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
|
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
|
||||||
import de.oaa.xxx.games.chastity.tasks.Task;
|
import de.oaa.xxx.games.chastity.tasks.Task;
|
||||||
import de.oaa.xxx.social.SseService;
|
import de.oaa.xxx.social.SystemMessageService;
|
||||||
import de.oaa.xxx.user.UserRepository;
|
import de.oaa.xxx.user.UserRepository;
|
||||||
import de.oaa.xxx.social.entity.MessageEntity;
|
|
||||||
import de.oaa.xxx.social.repository.MessageRepository;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -25,8 +23,7 @@ public class TaskCardController {
|
|||||||
private final CommunityTaskVoteRepository communityTaskVoteRepository;
|
private final CommunityTaskVoteRepository communityTaskVoteRepository;
|
||||||
private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository;
|
private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository;
|
||||||
private final AssignedTaskRepository assignedTaskRepository;
|
private final AssignedTaskRepository assignedTaskRepository;
|
||||||
private final MessageRepository messageRepository;
|
private final SystemMessageService systemMessageService;
|
||||||
private final SseService sseService;
|
|
||||||
|
|
||||||
public TaskCardController(CardlockRepository cardlockRepository,
|
public TaskCardController(CardlockRepository cardlockRepository,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
@@ -34,16 +31,14 @@ public class TaskCardController {
|
|||||||
CommunityTaskVoteRepository communityTaskVoteRepository,
|
CommunityTaskVoteRepository communityTaskVoteRepository,
|
||||||
CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository,
|
CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository,
|
||||||
AssignedTaskRepository assignedTaskRepository,
|
AssignedTaskRepository assignedTaskRepository,
|
||||||
MessageRepository messageRepository,
|
SystemMessageService systemMessageService) {
|
||||||
SseService sseService) {
|
|
||||||
this.cardlockRepository = cardlockRepository;
|
this.cardlockRepository = cardlockRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository;
|
this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository;
|
||||||
this.communityTaskVoteRepository = communityTaskVoteRepository;
|
this.communityTaskVoteRepository = communityTaskVoteRepository;
|
||||||
this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository;
|
this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository;
|
||||||
this.assignedTaskRepository = assignedTaskRepository;
|
this.assignedTaskRepository = assignedTaskRepository;
|
||||||
this.messageRepository = messageRepository;
|
this.systemMessageService = systemMessageService;
|
||||||
this.sseService = sseService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Keyholder: ausstehende Aufgaben-Karten-Entscheidungen ─────────────────
|
// ── Keyholder: ausstehende Aufgaben-Karten-Entscheidungen ─────────────────
|
||||||
@@ -240,16 +235,6 @@ public class TaskCardController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(UUID fromId, UUID toId, String text, String targetUrl) {
|
private void sendMessage(UUID fromId, UUID toId, String text, String targetUrl) {
|
||||||
if (toId == null) return;
|
systemMessageService.send(fromId, toId, text, targetUrl, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
MessageEntity msg = new MessageEntity();
|
|
||||||
msg.setMessageId(java.util.UUID.randomUUID());
|
|
||||||
msg.setSenderId(fromId);
|
|
||||||
msg.setReceiverId(toId);
|
|
||||||
msg.setText(text);
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
msg.setTargetUrl(targetUrl);
|
|
||||||
msg.setSentAt(java.time.LocalDateTime.now());
|
|
||||||
messageRepository.save(msg);
|
|
||||||
sseService.push(toId, "notification", Map.of("text", text));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -17,9 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
|
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
|
||||||
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
|
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
|
||||||
import de.oaa.xxx.games.chastity.tasks.Task;
|
import de.oaa.xxx.games.chastity.tasks.Task;
|
||||||
import de.oaa.xxx.social.SseService;
|
import de.oaa.xxx.social.SystemMessageService;
|
||||||
import de.oaa.xxx.social.entity.MessageEntity;
|
|
||||||
import de.oaa.xxx.social.repository.MessageRepository;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class TaskVoteScheduler {
|
public class TaskVoteScheduler {
|
||||||
@@ -30,21 +27,18 @@ public class TaskVoteScheduler {
|
|||||||
private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository;
|
private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository;
|
||||||
private final CardlockRepository cardlockRepository;
|
private final CardlockRepository cardlockRepository;
|
||||||
private final AssignedTaskRepository assignedTaskRepository;
|
private final AssignedTaskRepository assignedTaskRepository;
|
||||||
private final MessageRepository messageRepository;
|
private final SystemMessageService systemMessageService;
|
||||||
private final SseService sseService;
|
|
||||||
|
|
||||||
public TaskVoteScheduler(CommunityTaskVoteRepository communityTaskVoteRepository,
|
public TaskVoteScheduler(CommunityTaskVoteRepository communityTaskVoteRepository,
|
||||||
CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository,
|
CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository,
|
||||||
CardlockRepository cardlockRepository,
|
CardlockRepository cardlockRepository,
|
||||||
AssignedTaskRepository assignedTaskRepository,
|
AssignedTaskRepository assignedTaskRepository,
|
||||||
MessageRepository messageRepository,
|
SystemMessageService systemMessageService) {
|
||||||
SseService sseService) {
|
|
||||||
this.communityTaskVoteRepository = communityTaskVoteRepository;
|
this.communityTaskVoteRepository = communityTaskVoteRepository;
|
||||||
this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository;
|
this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository;
|
||||||
this.cardlockRepository = cardlockRepository;
|
this.cardlockRepository = cardlockRepository;
|
||||||
this.assignedTaskRepository = assignedTaskRepository;
|
this.assignedTaskRepository = assignedTaskRepository;
|
||||||
this.messageRepository = messageRepository;
|
this.systemMessageService = systemMessageService;
|
||||||
this.sseService = sseService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(fixedDelay = 60_000)
|
@Scheduled(fixedDelay = 60_000)
|
||||||
@@ -117,16 +111,6 @@ public class TaskVoteScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(UUID toId, String text, String targetUrl) {
|
private void sendMessage(UUID toId, String text, String targetUrl) {
|
||||||
if (toId == null) return;
|
systemMessageService.send(toId, toId, text, targetUrl, de.oaa.xxx.social.entity.MessageCause.GAME_STATE);
|
||||||
MessageEntity msg = new MessageEntity();
|
|
||||||
msg.setMessageId(UUID.randomUUID());
|
|
||||||
msg.setSenderId(toId); // System-Nachricht, kein echter Sender
|
|
||||||
msg.setReceiverId(toId);
|
|
||||||
msg.setText(text);
|
|
||||||
msg.setSystemMessage(true);
|
|
||||||
msg.setTargetUrl(targetUrl);
|
|
||||||
msg.setSentAt(LocalDateTime.now());
|
|
||||||
messageRepository.save(msg);
|
|
||||||
sseService.push(toId, "notification", Map.of("text", text));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
package de.oaa.xxx.games.chastity.history;
|
|
||||||
|
|
||||||
import de.oaa.xxx.user.UserRepository;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/lockhistory")
|
|
||||||
public class LockHistoryController {
|
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
|
||||||
private final LockHistoryRepository lockHistoryRepository;
|
|
||||||
|
|
||||||
public LockHistoryController(UserRepository userRepository, LockHistoryRepository lockHistoryRepository) {
|
|
||||||
this.userRepository = userRepository;
|
|
||||||
this.lockHistoryRepository = lockHistoryRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping
|
|
||||||
public ResponseEntity<List<Map<String, Object>>> get(@RequestParam UUID userId, Principal principal) {
|
|
||||||
var meOpt = userRepository.findByEmail(principal.getName());
|
|
||||||
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
|
||||||
|
|
||||||
var result = lockHistoryRepository.findByUserIdOrderByEndTimeDesc(userId).stream()
|
|
||||||
.map(e -> {
|
|
||||||
Map<String, Object> item = new LinkedHashMap<>();
|
|
||||||
item.put("role", e.getRole());
|
|
||||||
item.put("lockName", e.getLockName() != null ? e.getLockName() : "");
|
|
||||||
item.put("startTime", e.getStartTime().toString());
|
|
||||||
item.put("unlockTime", e.getEndTime().toString());
|
|
||||||
item.put("durationMinutes", e.getDurationMinutes());
|
|
||||||
if (e.getLockedBy() != null) {
|
|
||||||
userRepository.findById(e.getLockedBy()).ifPresent(u -> {
|
|
||||||
if ("LOCKEE".equals(e.getRole())) {
|
|
||||||
item.put("keyholderName", u.getName());
|
|
||||||
} else {
|
|
||||||
item.put("lockeeName", u.getName());
|
|
||||||
}
|
|
||||||
if (u.getProfilePicture() != null) {
|
|
||||||
item.put("partnerPic", u.getProfilePicture());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}).toList();
|
|
||||||
return ResponseEntity.ok(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package de.oaa.xxx.games.chastity.history;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import de.oaa.xxx.games.chastity.LockType;
|
|
||||||
|
|
||||||
public record LockHistoryDTO (UUID historyId, UUID userId, LocalDateTime startTime, LocalDateTime endTime, LockType type, UUID lockedBy, String lockName, long durationMinutes, String role) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package de.oaa.xxx.games.chastity.history;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import de.oaa.xxx.games.chastity.LockType;
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
|
||||||
import jakarta.persistence.GeneratedValue;
|
|
||||||
import jakarta.persistence.GenerationType;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@Entity
|
|
||||||
@Table(name = "lock_history")
|
|
||||||
public class LockHistoryEntity {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
|
||||||
@Column
|
|
||||||
private UUID historyId;
|
|
||||||
@Column(nullable = false)
|
|
||||||
private UUID userId;
|
|
||||||
@Column(nullable = false)
|
|
||||||
private LocalDateTime startTime;
|
|
||||||
@Column(nullable = false)
|
|
||||||
private LocalDateTime endTime;
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
@Column(nullable = false)
|
|
||||||
private LockType type;
|
|
||||||
@Column
|
|
||||||
private UUID lockedBy;
|
|
||||||
|
|
||||||
@Column
|
|
||||||
private String lockName;
|
|
||||||
|
|
||||||
@Column(nullable = false, columnDefinition = "BIGINT DEFAULT 0")
|
|
||||||
private long durationMinutes;
|
|
||||||
|
|
||||||
// LOCKEE oder KEYHOLDER
|
|
||||||
@Column(nullable = false, length = 20)
|
|
||||||
private String role;
|
|
||||||
|
|
||||||
public LockHistoryDTO toLockHistory() {
|
|
||||||
return new LockHistoryDTO(historyId, userId, startTime, endTime, type, lockedBy, lockName, durationMinutes, role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package de.oaa.xxx.games.chastity.history;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
public interface LockHistoryRepository extends JpaRepository<LockHistoryEntity, UUID> {
|
|
||||||
|
|
||||||
List<LockHistoryEntity> findByUserIdOrderByEndTimeDesc(UUID userId);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
import de.oaa.xxx.user.UserRepository;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/gamehistory")
|
||||||
|
public class GameHistoryController {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final GameHistoryRepository gameHistoryRepository;
|
||||||
|
|
||||||
|
public GameHistoryController(UserRepository userRepository, GameHistoryRepository gameHistoryRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.gameHistoryRepository = gameHistoryRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseEntity<List<Map<String, Object>>> get(@RequestParam UUID userId, Principal principal) {
|
||||||
|
var meOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (meOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
|
||||||
|
var result = gameHistoryRepository.findByParticipantUserId(userId).stream()
|
||||||
|
.map(e -> {
|
||||||
|
Map<String, Object> item = new LinkedHashMap<>();
|
||||||
|
item.put("historyId", e.getHistoryId());
|
||||||
|
item.put("gameType", e.getGameType());
|
||||||
|
item.put("gameName", e.getGameName() != null ? e.getGameName() : "");
|
||||||
|
item.put("lockName", e.getGameName() != null ? e.getGameName() : "");
|
||||||
|
item.put("startTime", e.getStartTime().toString());
|
||||||
|
item.put("unlockTime", e.getEndTime().toString());
|
||||||
|
item.put("durationMinutes", e.getDurationMinutes());
|
||||||
|
|
||||||
|
List<Map<String, Object>> participants = e.getParticipants().stream()
|
||||||
|
.map(p -> {
|
||||||
|
Map<String, Object> pm = new LinkedHashMap<>();
|
||||||
|
pm.put("userId", p.getUserId());
|
||||||
|
pm.put("role", p.getRole());
|
||||||
|
userRepository.findById(p.getUserId()).ifPresent(u -> {
|
||||||
|
pm.put("name", u.getName());
|
||||||
|
pm.put("picture", u.getProfilePicture());
|
||||||
|
});
|
||||||
|
return pm;
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
item.put("participants", participants);
|
||||||
|
|
||||||
|
// Abwärtskompatible Felder für benutzer.html (wird später angepasst)
|
||||||
|
e.getParticipants().stream()
|
||||||
|
.filter(p -> p.getUserId().equals(userId))
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(own -> item.put("role", own.getRole().name()));
|
||||||
|
|
||||||
|
e.getParticipants().stream()
|
||||||
|
.filter(p -> !p.getUserId().equals(userId))
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(partner -> userRepository.findById(partner.getUserId()).ifPresent(u -> {
|
||||||
|
if (GameRole.LOCKEE == partner.getRole()) {
|
||||||
|
item.put("lockeeName", u.getName());
|
||||||
|
} else if (GameRole.KEYHOLDER == partner.getRole()) {
|
||||||
|
item.put("keyholderName", u.getName());
|
||||||
|
}
|
||||||
|
item.put("partnerPic", u.getProfilePicture());
|
||||||
|
}));
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}).toList();
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record GameHistoryDTO(
|
||||||
|
UUID historyId,
|
||||||
|
GameType gameType,
|
||||||
|
LocalDateTime startTime,
|
||||||
|
LocalDateTime endTime,
|
||||||
|
String gameName,
|
||||||
|
long durationMinutes,
|
||||||
|
List<ParticipantDTO> participants
|
||||||
|
) {
|
||||||
|
public record ParticipantDTO(UUID userId, GameRole role) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "game_history")
|
||||||
|
public class GameHistoryEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Column
|
||||||
|
private UUID historyId;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 20)
|
||||||
|
private GameType gameType;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String gameName;
|
||||||
|
|
||||||
|
@Column(nullable = false, columnDefinition = "BIGINT DEFAULT 0")
|
||||||
|
private long durationMinutes;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "history", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
|
private List<GameHistoryParticipantEntity> participants = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addParticipant(UUID userId, GameRole role) {
|
||||||
|
GameHistoryParticipantEntity p = new GameHistoryParticipantEntity();
|
||||||
|
p.setUserId(userId);
|
||||||
|
p.setRole(role);
|
||||||
|
p.setHistory(this);
|
||||||
|
participants.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "game_history_participant")
|
||||||
|
public class GameHistoryParticipantEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Column
|
||||||
|
private UUID participantId;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private UUID userId;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 20)
|
||||||
|
private GameRole role;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "history_id", nullable = false)
|
||||||
|
private GameHistoryEntity history;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface GameHistoryParticipantRepository extends JpaRepository<GameHistoryParticipantEntity, UUID> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface GameHistoryRepository extends JpaRepository<GameHistoryEntity, UUID> {
|
||||||
|
|
||||||
|
@Query("SELECT DISTINCT h FROM GameHistoryEntity h JOIN h.participants p WHERE p.userId = :userId ORDER BY h.endTime DESC")
|
||||||
|
List<GameHistoryEntity> findByParticipantUserId(@Param("userId") UUID userId);
|
||||||
|
|
||||||
|
@Query("SELECT DISTINCT h FROM GameHistoryEntity h JOIN h.participants p WHERE p.userId = :userId AND h.gameType = :gameType ORDER BY h.endTime DESC")
|
||||||
|
List<GameHistoryEntity> findByParticipantUserIdAndGameType(@Param("userId") UUID userId, @Param("gameType") GameType gameType);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
public enum GameRole {
|
||||||
|
LOCKEE,
|
||||||
|
KEYHOLDER,
|
||||||
|
PLAYER
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.oaa.xxx.games.history;
|
||||||
|
|
||||||
|
public enum GameType {
|
||||||
|
CARDLOCK,
|
||||||
|
TIMELOCK,
|
||||||
|
BDSM,
|
||||||
|
VANILLA
|
||||||
|
}
|
||||||
@@ -165,6 +165,56 @@ public class MailTemplateService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String buildNotificationMail(String name, String text, String targetUrl, String baseUrl) {
|
||||||
|
String actionButton = targetUrl != null
|
||||||
|
? """
|
||||||
|
<div style="text-align:center; margin:0 0 2rem 0;">
|
||||||
|
<a href="%s%s"
|
||||||
|
style="display:inline-block; padding:0.75rem 2.5rem; background:%s; color:#ffffff;
|
||||||
|
border-radius:6px; text-decoration:none; font-weight:600; font-size:1rem;">
|
||||||
|
Zur Anwendung
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
""".formatted(baseUrl, targetUrl, colorPrimary)
|
||||||
|
: "<div style=\"margin:0 0 2rem 0;\"></div>";
|
||||||
|
|
||||||
|
String settingsUrl = baseUrl + "/einstellungen.html#sec-benachrichtigungen";
|
||||||
|
|
||||||
|
return """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<body style="margin:0; padding:2rem; background:%s; font-family:'Segoe UI',Arial,sans-serif; color:%s;">
|
||||||
|
<div style="max-width:460px; margin:0 auto; background:%s; border:1px solid %s; border-radius:12px; padding:2.5rem; box-shadow:0 8px 32px rgba(0,0,0,0.5);">
|
||||||
|
|
||||||
|
<h1 style="color:%s; text-align:center; margin:0 0 1.5rem 0; font-size:1.6rem;">XXX The Game</h1>
|
||||||
|
|
||||||
|
<p style="color:%s; margin:0 0 0.75rem 0;">Hallo %s,</p>
|
||||||
|
<p style="color:%s; margin:0 0 2rem 0;">%s</p>
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
<hr style="border:none; border-top:1px solid %s; margin:0 0 1.5rem 0;">
|
||||||
|
|
||||||
|
<p style="color:%s; font-size:0.85em; margin:0;">
|
||||||
|
Du erhältst diese E-Mail, weil du E-Mail-Benachrichtigungen für diese Kategorie aktiviert hast.
|
||||||
|
Du kannst deine Einstellungen jederzeit unter
|
||||||
|
<a href="%s" style="color:%s;">Einstellungen → Benachrichtigungen</a> anpassen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""".formatted(
|
||||||
|
colorBg, colorText,
|
||||||
|
colorCard, colorSecondary,
|
||||||
|
colorPrimary,
|
||||||
|
colorText, name,
|
||||||
|
colorText, text,
|
||||||
|
actionButton,
|
||||||
|
colorSecondary,
|
||||||
|
colorMuted, settingsUrl, colorPrimary
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public String buildActivationMail(String name, String activationLink, String activatePageUrl, String uuid) {
|
public String buildActivationMail(String name, String activationLink, String activatePageUrl, String uuid) {
|
||||||
return """
|
return """
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import de.oaa.xxx.user.Registration;
|
|
||||||
import de.oaa.xxx.user.UserController;
|
import de.oaa.xxx.user.UserController;
|
||||||
|
|
||||||
|
|
||||||
@@ -29,12 +28,7 @@ public class ActivationController {
|
|||||||
public ResponseEntity<Void> activate(@PathVariable String uuid) {
|
public ResponseEntity<Void> activate(@PathVariable String uuid) {
|
||||||
RegistrationEntity registration = registrationRepository.findById(UUID.fromString(uuid)).orElse(null);
|
RegistrationEntity registration = registrationRepository.findById(UUID.fromString(uuid)).orElse(null);
|
||||||
if (registration != null && !Boolean.TRUE.equals(registration.getActivated())) {
|
if (registration != null && !Boolean.TRUE.equals(registration.getActivated())) {
|
||||||
Registration reg = new Registration();
|
ResponseEntity<Void> response = userController.userAnlegen(registration.toRegistration());
|
||||||
reg.setEmail(registration.getEmail());
|
|
||||||
reg.setName(registration.getName());
|
|
||||||
reg.setPasswordHash(registration.getPassword());
|
|
||||||
|
|
||||||
ResponseEntity<Void> response = userController.userAnlegen(reg);
|
|
||||||
if (response.getStatusCode().is2xxSuccessful()) {
|
if (response.getStatusCode().is2xxSuccessful()) {
|
||||||
registration.setActivated(Boolean.TRUE);
|
registration.setActivated(Boolean.TRUE);
|
||||||
registrationRepository.save(registration);
|
registrationRepository.save(registration);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package de.oaa.xxx.registration;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -13,6 +14,7 @@ public class Registration {
|
|||||||
private String name;
|
private String name;
|
||||||
private String email;
|
private String email;
|
||||||
private String passwordHash;
|
private String passwordHash;
|
||||||
|
private LocalDate geburtsdatum;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import de.oaa.xxx.mail.MailService;
|
|||||||
import de.oaa.xxx.mail.MailTemplateService;
|
import de.oaa.xxx.mail.MailTemplateService;
|
||||||
import de.oaa.xxx.user.UserRepository;
|
import de.oaa.xxx.user.UserRepository;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.Period;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/registration")
|
@RequestMapping("/registration")
|
||||||
public class RegistrationController {
|
public class RegistrationController {
|
||||||
@@ -39,6 +42,11 @@ public class RegistrationController {
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<String> create(@RequestBody Registration registration) {
|
public ResponseEntity<String> create(@RequestBody Registration registration) {
|
||||||
LOGGER.info("POST {}: {}", getClass().getName(), registration);
|
LOGGER.info("POST {}: {}", getClass().getName(), registration);
|
||||||
|
if (registration.getGeburtsdatum() == null
|
||||||
|
|| Period.between(registration.getGeburtsdatum(), LocalDate.now()).getYears() < 18) {
|
||||||
|
LOGGER.warn("Registrierung abgelehnt – Mindestalter nicht erreicht");
|
||||||
|
return ResponseEntity.status(422).build();
|
||||||
|
}
|
||||||
if (registrationRepository.findByEmail(registration.getEmail()).isPresent()
|
if (registrationRepository.findByEmail(registration.getEmail()).isPresent()
|
||||||
|| userRepository.findByEmail(registration.getEmail()).isPresent()) {
|
|| userRepository.findByEmail(registration.getEmail()).isPresent()) {
|
||||||
LOGGER.warn("User mit E-Mail {} bereits vorhanden", registration.getEmail());
|
LOGGER.warn("User mit E-Mail {} bereits vorhanden", registration.getEmail());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import jakarta.persistence.Table;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -26,6 +27,8 @@ public class RegistrationEntity {
|
|||||||
private String password;
|
private String password;
|
||||||
@Column
|
@Column
|
||||||
private Boolean activated;
|
private Boolean activated;
|
||||||
|
@Column
|
||||||
|
private LocalDate geburtsdatum;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@@ -38,6 +41,7 @@ public class RegistrationEntity {
|
|||||||
registration.setEmail(email);
|
registration.setEmail(email);
|
||||||
registration.setName(name);
|
registration.setName(name);
|
||||||
registration.setPasswordHash(password);
|
registration.setPasswordHash(password);
|
||||||
|
registration.setGeburtsdatum(geburtsdatum);
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +52,7 @@ public class RegistrationEntity {
|
|||||||
entity.setActivated(Boolean.FALSE);
|
entity.setActivated(Boolean.FALSE);
|
||||||
entity.setName(registration.getName());
|
entity.setName(registration.getName());
|
||||||
entity.setPassword(registration.getPasswordHash());
|
entity.setPassword(registration.getPasswordHash());
|
||||||
|
entity.setGeburtsdatum(registration.getGeburtsdatum());
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package de.oaa.xxx.session.repository;
|
|
||||||
|
|
||||||
import de.oaa.xxx.session.entity.SessionEntity;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public interface SessionRepository extends JpaRepository<SessionEntity, UUID> {
|
|
||||||
|
|
||||||
Optional<SessionEntity> findByUserId(UUID userId);
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import de.oaa.xxx.social.dto.MessageDto;
|
|||||||
import de.oaa.xxx.social.dto.UserProfile;
|
import de.oaa.xxx.social.dto.UserProfile;
|
||||||
import de.oaa.xxx.social.entity.FriendshipEntity;
|
import de.oaa.xxx.social.entity.FriendshipEntity;
|
||||||
import de.oaa.xxx.social.entity.FriendshipEntity.Status;
|
import de.oaa.xxx.social.entity.FriendshipEntity.Status;
|
||||||
|
import de.oaa.xxx.social.entity.MessageCause;
|
||||||
import de.oaa.xxx.social.entity.MessageEntity;
|
import de.oaa.xxx.social.entity.MessageEntity;
|
||||||
import de.oaa.xxx.social.repository.FriendshipRepository;
|
import de.oaa.xxx.social.repository.FriendshipRepository;
|
||||||
import de.oaa.xxx.social.repository.MessageRepository;
|
import de.oaa.xxx.social.repository.MessageRepository;
|
||||||
@@ -31,15 +32,18 @@ public class SocialController {
|
|||||||
private final FriendshipRepository friendshipRepository;
|
private final FriendshipRepository friendshipRepository;
|
||||||
private final MessageRepository messageRepository;
|
private final MessageRepository messageRepository;
|
||||||
private final SseService sseService;
|
private final SseService sseService;
|
||||||
|
private final SystemMessageService systemMessageService;
|
||||||
|
|
||||||
public SocialController(UserRepository userRepository,
|
public SocialController(UserRepository userRepository,
|
||||||
FriendshipRepository friendshipRepository,
|
FriendshipRepository friendshipRepository,
|
||||||
MessageRepository messageRepository,
|
MessageRepository messageRepository,
|
||||||
SseService sseService) {
|
SseService sseService,
|
||||||
|
SystemMessageService systemMessageService) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.friendshipRepository = friendshipRepository;
|
this.friendshipRepository = friendshipRepository;
|
||||||
this.messageRepository = messageRepository;
|
this.messageRepository = messageRepository;
|
||||||
this.sseService = sseService;
|
this.sseService = sseService;
|
||||||
|
this.systemMessageService = systemMessageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
record FriendRequestBody(UUID receiverId) {}
|
record FriendRequestBody(UUID receiverId) {}
|
||||||
@@ -94,6 +98,13 @@ public class SocialController {
|
|||||||
f.setCreatedAt(LocalDateTime.now());
|
f.setCreatedAt(LocalDateTime.now());
|
||||||
friendshipRepository.save(f);
|
friendshipRepository.save(f);
|
||||||
LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId());
|
LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId());
|
||||||
|
|
||||||
|
String senderName = meOpt.get().getName();
|
||||||
|
systemMessageService.send(myId, body.receiverId(),
|
||||||
|
senderName + " hat dir eine Freundschaftsanfrage gesendet.",
|
||||||
|
"/benutzer.html?userId=" + myId,
|
||||||
|
MessageCause.FRIENDREQUEST);
|
||||||
|
|
||||||
return ResponseEntity.status(201).build();
|
return ResponseEntity.status(201).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,22 +310,52 @@ public class SocialController {
|
|||||||
// ── Helpers ──
|
// ── Helpers ──
|
||||||
|
|
||||||
private UserProfile toUserProfileWithStatus(UserEntity user, UUID myId) {
|
private UserProfile toUserProfileWithStatus(UserEntity user, UUID myId) {
|
||||||
|
boolean isOwn = user.getUserId().equals(myId);
|
||||||
String status = "NONE";
|
String status = "NONE";
|
||||||
var existing = friendshipRepository.findExisting(myId, user.getUserId());
|
if (!isOwn) {
|
||||||
if (existing.isPresent()) {
|
var existing = friendshipRepository.findExisting(myId, user.getUserId());
|
||||||
FriendshipEntity f = existing.get();
|
if (existing.isPresent()) {
|
||||||
if (f.getStatus() == Status.ACCEPTED) {
|
FriendshipEntity f = existing.get();
|
||||||
status = "FRIEND";
|
if (f.getStatus() == Status.ACCEPTED) {
|
||||||
} else if (f.getSenderId().equals(myId)) {
|
status = "FRIEND";
|
||||||
status = "PENDING_SENT";
|
} else if (f.getSenderId().equals(myId)) {
|
||||||
} else {
|
status = "PENDING_SENT";
|
||||||
status = "PENDING_RECEIVED";
|
} else {
|
||||||
|
status = "PENDING_RECEIVED";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new UserProfile(user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(),
|
boolean isFriend = isOwn || "FRIEND".equals(status);
|
||||||
status, user.getAlter(), user.getGroesse(), user.getGewicht(),
|
|
||||||
user.getGeschlecht(), user.getNeigung(), user.getBeziehungsstatus(), user.getBeschreibung(),
|
// Grunddaten nur zurückgeben wenn berechtigt
|
||||||
user.getLockeeXp(), user.getKeyholderXp());
|
de.oaa.xxx.user.Sichtbarkeit svGd = user.getSichtbarkeitGrunddaten();
|
||||||
|
boolean showGrunddaten = isOwn || svGd == de.oaa.xxx.user.Sichtbarkeit.ALLE
|
||||||
|
|| (svGd == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend);
|
||||||
|
|
||||||
|
// XP nur zurückgeben wenn berechtigt
|
||||||
|
de.oaa.xxx.user.Sichtbarkeit svXp = user.getSichtbarkeitXp();
|
||||||
|
boolean showXp = isOwn || svXp == de.oaa.xxx.user.Sichtbarkeit.ALLE
|
||||||
|
|| (svXp == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend);
|
||||||
|
|
||||||
|
return new UserProfile(
|
||||||
|
user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(),
|
||||||
|
status,
|
||||||
|
showGrunddaten ? user.getAlter() : null,
|
||||||
|
showGrunddaten ? user.getGroesse() : null,
|
||||||
|
showGrunddaten ? user.getGewicht() : null,
|
||||||
|
showGrunddaten ? user.getGeschlecht() : null,
|
||||||
|
showGrunddaten ? user.getNeigung() : null,
|
||||||
|
showGrunddaten ? user.getBeziehungsstatus() : null,
|
||||||
|
showGrunddaten ? user.getBeschreibung() : null,
|
||||||
|
showXp ? user.getLockeeXp() : 0,
|
||||||
|
showXp ? user.getKeyholderXp() : 0,
|
||||||
|
user.getSichtbarkeitGrunddaten(),
|
||||||
|
user.getSichtbarkeitGalerie(),
|
||||||
|
user.getSichtbarkeitFreunde(),
|
||||||
|
user.getSichtbarkeitFeed(),
|
||||||
|
user.getSichtbarkeitPinnwand(),
|
||||||
|
user.getSichtbarkeitXp(),
|
||||||
|
user.getSichtbarkeitLockhistorie());
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageDto toMessageDto(MessageEntity m) {
|
private MessageDto toMessageDto(MessageEntity m) {
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package de.oaa.xxx.social;
|
||||||
|
|
||||||
|
import de.oaa.xxx.mail.Email;
|
||||||
|
import de.oaa.xxx.mail.MailService;
|
||||||
|
import de.oaa.xxx.mail.MailTemplateService;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import de.oaa.xxx.social.entity.MessageCause;
|
||||||
|
import de.oaa.xxx.social.entity.MessageEntity;
|
||||||
|
import de.oaa.xxx.social.entity.NotificationPreferenceEntity;
|
||||||
|
import de.oaa.xxx.social.repository.MessageRepository;
|
||||||
|
import de.oaa.xxx.social.repository.NotificationPreferenceRepository;
|
||||||
|
import de.oaa.xxx.user.UserRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SystemMessageService {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SystemMessageService.class);
|
||||||
|
|
||||||
|
private final MessageRepository messageRepository;
|
||||||
|
private final NotificationPreferenceRepository preferenceRepository;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final SseService sseService;
|
||||||
|
private final MailService mailService;
|
||||||
|
private final MailTemplateService mailTemplateService;
|
||||||
|
|
||||||
|
@Value("${app.base-url:http://localhost:8080}")
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
public SystemMessageService(MessageRepository messageRepository,
|
||||||
|
NotificationPreferenceRepository preferenceRepository,
|
||||||
|
UserRepository userRepository,
|
||||||
|
SseService sseService,
|
||||||
|
MailService mailService,
|
||||||
|
MailTemplateService mailTemplateService) {
|
||||||
|
this.messageRepository = messageRepository;
|
||||||
|
this.preferenceRepository = preferenceRepository;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.sseService = sseService;
|
||||||
|
this.mailService = mailService;
|
||||||
|
this.mailTemplateService = mailTemplateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sendet eine Systemnachricht unter Berücksichtigung der Benachrichtigungseinstellungen des Empfängers.
|
||||||
|
*/
|
||||||
|
public void send(UUID senderId, UUID receiverId, String text, String targetUrl, MessageCause cause) {
|
||||||
|
if (senderId == null || receiverId == null) return;
|
||||||
|
|
||||||
|
NotificationPreferenceEntity pref = preferenceRepository
|
||||||
|
.findByUserIdAndCause(receiverId, cause)
|
||||||
|
.orElseGet(() -> NotificationPreferenceEntity.defaultFor(receiverId, cause));
|
||||||
|
|
||||||
|
// FRIENDREQUEST ist immer in-app, unabhängig von der Einstellung
|
||||||
|
boolean sendInApp = cause == MessageCause.FRIENDREQUEST || pref.isInApp();
|
||||||
|
|
||||||
|
if (sendInApp) {
|
||||||
|
MessageEntity msg = new MessageEntity();
|
||||||
|
msg.setMessageId(UUID.randomUUID());
|
||||||
|
msg.setSenderId(senderId);
|
||||||
|
msg.setReceiverId(receiverId);
|
||||||
|
msg.setText(text);
|
||||||
|
msg.setSentAt(LocalDateTime.now());
|
||||||
|
msg.setSystemMessage(true);
|
||||||
|
msg.setMessageCause(cause);
|
||||||
|
if (targetUrl != null) msg.setTargetUrl(targetUrl);
|
||||||
|
messageRepository.save(msg);
|
||||||
|
|
||||||
|
long unread = messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(receiverId, true);
|
||||||
|
sseService.push(receiverId, "NOTIFICATION", Map.of("unreadCount", unread, "text", text));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pref.isEmail()) {
|
||||||
|
userRepository.findById(receiverId).ifPresent(user -> {
|
||||||
|
try {
|
||||||
|
Email email = new Email();
|
||||||
|
email.setEmailAdresse(user.getEmail());
|
||||||
|
email.setTitel(causeTitel(cause));
|
||||||
|
email.setText(mailTemplateService.buildNotificationMail(user.getName(), text, targetUrl, baseUrl));
|
||||||
|
mailService.send(email);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("E-Mail-Benachrichtigung fehlgeschlagen für userId={}: {}", receiverId, e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String causeTitel(MessageCause cause) {
|
||||||
|
return switch (cause) {
|
||||||
|
case INVITATION -> "XXX The Game – Neue Einladung";
|
||||||
|
case GAME_STATE -> "XXX The Game – Spielstatus-Änderung";
|
||||||
|
case EMERGENCY -> "XXX The Game – ⚠️ Notfall";
|
||||||
|
case FRIENDREQUEST -> "XXX The Game – Neue Freundschaftsanfrage";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package de.oaa.xxx.social.dto;
|
|||||||
import de.oaa.xxx.user.Beziehungsstatus;
|
import de.oaa.xxx.user.Beziehungsstatus;
|
||||||
import de.oaa.xxx.user.Geschlecht;
|
import de.oaa.xxx.user.Geschlecht;
|
||||||
import de.oaa.xxx.user.Neigung;
|
import de.oaa.xxx.user.Neigung;
|
||||||
|
import de.oaa.xxx.user.Sichtbarkeit;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -20,10 +21,20 @@ public record UserProfile(
|
|||||||
Beziehungsstatus beziehungsstatus,
|
Beziehungsstatus beziehungsstatus,
|
||||||
String beschreibung,
|
String beschreibung,
|
||||||
int lockeeXp,
|
int lockeeXp,
|
||||||
int keyholderXp
|
int keyholderXp,
|
||||||
|
// Datenschutz-Einstellungen
|
||||||
|
Sichtbarkeit sichtbarkeitGrunddaten,
|
||||||
|
Sichtbarkeit sichtbarkeitGalerie,
|
||||||
|
Sichtbarkeit sichtbarkeitFreunde,
|
||||||
|
Sichtbarkeit sichtbarkeitFeed,
|
||||||
|
Sichtbarkeit sichtbarkeitPinnwand,
|
||||||
|
Sichtbarkeit sichtbarkeitXp,
|
||||||
|
Sichtbarkeit sichtbarkeitLockhistorie
|
||||||
) {
|
) {
|
||||||
/** Compact constructor for contexts where profile details are not needed (friend list etc.) */
|
/** Compact constructor for contexts where profile details are not needed (friend list etc.) */
|
||||||
public UserProfile(UUID userId, String name, String profilePicture, String profilePictureHq, String friendStatus) {
|
public UserProfile(UUID userId, String name, String profilePicture, String profilePictureHq, String friendStatus) {
|
||||||
this(userId, name, profilePicture, profilePictureHq, friendStatus, null, null, null, null, null, null, null, 0, 0);
|
this(userId, name, profilePicture, profilePictureHq, friendStatus,
|
||||||
|
null, null, null, null, null, null, null, 0, 0,
|
||||||
|
null, null, null, null, null, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.oaa.xxx.social.entity;
|
||||||
|
|
||||||
|
public enum MessageCause {
|
||||||
|
INVITATION,
|
||||||
|
GAME_STATE,
|
||||||
|
EMERGENCY,
|
||||||
|
FRIENDREQUEST
|
||||||
|
}
|
||||||
@@ -35,6 +35,10 @@ public class MessageEntity {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private boolean systemMessage = false;
|
private boolean systemMessage = false;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20)
|
||||||
|
private MessageCause messageCause;
|
||||||
|
|
||||||
@Column(length = 500)
|
@Column(length = 500)
|
||||||
private String targetUrl;
|
private String targetUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package de.oaa.xxx.social.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Entity
|
||||||
|
@Table(name = "notification_preference", uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "cause"}))
|
||||||
|
public class NotificationPreferenceEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private UUID id;
|
||||||
|
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
private UUID userId;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(nullable = false, length = 20)
|
||||||
|
private MessageCause cause;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean inApp = true;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean email = false;
|
||||||
|
|
||||||
|
/** Erzeugt eine nicht persistierte Standardpräferenz für unbekannte/neue Causes. */
|
||||||
|
public static NotificationPreferenceEntity defaultFor(UUID userId, MessageCause cause) {
|
||||||
|
NotificationPreferenceEntity p = new NotificationPreferenceEntity();
|
||||||
|
p.setUserId(userId);
|
||||||
|
p.setCause(cause);
|
||||||
|
// inApp=true und email=false sind bereits die Java-Felddefaults
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package de.oaa.xxx.social.repository;
|
||||||
|
|
||||||
|
import de.oaa.xxx.social.entity.MessageCause;
|
||||||
|
import de.oaa.xxx.social.entity.NotificationPreferenceEntity;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface NotificationPreferenceRepository extends JpaRepository<NotificationPreferenceEntity, UUID> {
|
||||||
|
|
||||||
|
List<NotificationPreferenceEntity> findByUserId(UUID userId);
|
||||||
|
|
||||||
|
Optional<NotificationPreferenceEntity> findByUserIdAndCause(UUID userId, MessageCause cause);
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package de.oaa.xxx.user;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
public class Registration {
|
|
||||||
|
|
||||||
private UUID id;
|
|
||||||
private String name;
|
|
||||||
private String email;
|
|
||||||
private String passwordHash;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Registration [id=" + id + ", name=" + name + ", email=" + email + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package de.oaa.xxx.user;
|
||||||
|
|
||||||
|
public enum Sichtbarkeit {
|
||||||
|
ALLE,
|
||||||
|
NUR_FREUNDE,
|
||||||
|
NUR_ICH
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package de.oaa.xxx.user;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.Period;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -14,7 +16,7 @@ public class User {
|
|||||||
private String email;
|
private String email;
|
||||||
private String password;
|
private String password;
|
||||||
private String profilePicture;
|
private String profilePicture;
|
||||||
private Integer alter;
|
private LocalDate geburtsdatum;
|
||||||
private Integer groesse;
|
private Integer groesse;
|
||||||
private Integer gewicht;
|
private Integer gewicht;
|
||||||
private Geschlecht geschlecht;
|
private Geschlecht geschlecht;
|
||||||
@@ -22,6 +24,10 @@ public class User {
|
|||||||
private Beziehungsstatus beziehungsstatus;
|
private Beziehungsstatus beziehungsstatus;
|
||||||
private String beschreibung;
|
private String beschreibung;
|
||||||
|
|
||||||
|
public Integer getAlter() {
|
||||||
|
return geburtsdatum != null ? Period.between(geburtsdatum, LocalDate.now()).getYears() : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "User[userId=" + userId + ", name=" + name + ", email=" + email + "]";
|
return "User[userId=" + userId + ", name=" + name + ", email=" + email + "]";
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package de.oaa.xxx.user;
|
package de.oaa.xxx.user;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.Period;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -10,6 +15,7 @@ import org.springframework.http.HttpHeaders;
|
|||||||
import org.springframework.http.ResponseCookie;
|
import org.springframework.http.ResponseCookie;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@@ -17,6 +23,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import de.oaa.xxx.aufgaben.repository.AufgabeRepository;
|
import de.oaa.xxx.aufgaben.repository.AufgabeRepository;
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.BdsmDefaultsRepository;
|
||||||
import de.oaa.xxx.aufgaben.repository.AufgabenGruppeRepository;
|
import de.oaa.xxx.aufgaben.repository.AufgabenGruppeRepository;
|
||||||
import de.oaa.xxx.aufgaben.repository.FavoritRepository;
|
import de.oaa.xxx.aufgaben.repository.FavoritRepository;
|
||||||
import de.oaa.xxx.aufgaben.repository.GruppenAboRepository;
|
import de.oaa.xxx.aufgaben.repository.GruppenAboRepository;
|
||||||
@@ -24,19 +32,23 @@ import de.oaa.xxx.aufgaben.repository.SperreRepository;
|
|||||||
import de.oaa.xxx.aufgaben.repository.StrafeRepository;
|
import de.oaa.xxx.aufgaben.repository.StrafeRepository;
|
||||||
import de.oaa.xxx.aufgaben.repository.ToyRepository;
|
import de.oaa.xxx.aufgaben.repository.ToyRepository;
|
||||||
import de.oaa.xxx.emailchange.EmailChangeRepository;
|
import de.oaa.xxx.emailchange.EmailChangeRepository;
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity;
|
||||||
|
import de.oaa.xxx.games.bdsm.entity.MitspielerEntity;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository;
|
||||||
|
import de.oaa.xxx.games.bdsm.repository.MitspielerRepository;
|
||||||
import de.oaa.xxx.passwordreset.PasswordResetRepository;
|
import de.oaa.xxx.passwordreset.PasswordResetRepository;
|
||||||
|
import de.oaa.xxx.registration.Registration;
|
||||||
import de.oaa.xxx.registration.RegistrationRepository;
|
import de.oaa.xxx.registration.RegistrationRepository;
|
||||||
import de.oaa.xxx.session.entity.AktiveSperreEntity;
|
import de.oaa.xxx.social.entity.MessageCause;
|
||||||
import de.oaa.xxx.session.entity.MitspielerEntity;
|
import de.oaa.xxx.social.entity.NotificationPreferenceEntity;
|
||||||
import de.oaa.xxx.session.repository.AktiveSperreRepository;
|
import de.oaa.xxx.social.repository.KommentarLikeRepository;
|
||||||
import de.oaa.xxx.session.repository.MitspielerRepository;
|
import de.oaa.xxx.social.repository.KommentarRepository;
|
||||||
import de.oaa.xxx.session.repository.SessionRepository;
|
import de.oaa.xxx.social.repository.NotificationPreferenceRepository;
|
||||||
import de.oaa.xxx.social.repository.ProfileImageLikeRepository;
|
|
||||||
import de.oaa.xxx.social.repository.ProfileImageRepository;
|
|
||||||
import de.oaa.xxx.social.repository.PinnwandEintragRepository;
|
import de.oaa.xxx.social.repository.PinnwandEintragRepository;
|
||||||
import de.oaa.xxx.social.repository.PinnwandLikeRepository;
|
import de.oaa.xxx.social.repository.PinnwandLikeRepository;
|
||||||
import de.oaa.xxx.social.repository.KommentarRepository;
|
import de.oaa.xxx.social.repository.ProfileImageLikeRepository;
|
||||||
import de.oaa.xxx.social.repository.KommentarLikeRepository;
|
import de.oaa.xxx.social.repository.ProfileImageRepository;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -54,7 +66,7 @@ public class UserController {
|
|||||||
private final ToyRepository toyRepository;
|
private final ToyRepository toyRepository;
|
||||||
private final FavoritRepository favoritRepository;
|
private final FavoritRepository favoritRepository;
|
||||||
private final GruppenAboRepository gruppenAboRepository;
|
private final GruppenAboRepository gruppenAboRepository;
|
||||||
private final SessionRepository sessionRepository;
|
private final BdsmGameRepository sessionRepository;
|
||||||
private final AktiveSperreRepository aktiveSperreRepository;
|
private final AktiveSperreRepository aktiveSperreRepository;
|
||||||
private final MitspielerRepository mitspielerRepository;
|
private final MitspielerRepository mitspielerRepository;
|
||||||
private final EmailChangeRepository emailChangeRepository;
|
private final EmailChangeRepository emailChangeRepository;
|
||||||
@@ -65,6 +77,8 @@ public class UserController {
|
|||||||
private final PinnwandLikeRepository pinnwandLikeRepository;
|
private final PinnwandLikeRepository pinnwandLikeRepository;
|
||||||
private final KommentarRepository kommentarRepository;
|
private final KommentarRepository kommentarRepository;
|
||||||
private final KommentarLikeRepository kommentarLikeRepository;
|
private final KommentarLikeRepository kommentarLikeRepository;
|
||||||
|
private final NotificationPreferenceRepository notificationPreferenceRepository;
|
||||||
|
private final BdsmDefaultsRepository bdsmDefaultsRepository;
|
||||||
|
|
||||||
public UserController(UserRepository userRepository,
|
public UserController(UserRepository userRepository,
|
||||||
RegistrationRepository registrationRepository,
|
RegistrationRepository registrationRepository,
|
||||||
@@ -75,7 +89,7 @@ public class UserController {
|
|||||||
ToyRepository toyRepository,
|
ToyRepository toyRepository,
|
||||||
FavoritRepository favoritRepository,
|
FavoritRepository favoritRepository,
|
||||||
GruppenAboRepository gruppenAboRepository,
|
GruppenAboRepository gruppenAboRepository,
|
||||||
SessionRepository sessionRepository,
|
BdsmGameRepository sessionRepository,
|
||||||
AktiveSperreRepository aktiveSperreRepository,
|
AktiveSperreRepository aktiveSperreRepository,
|
||||||
MitspielerRepository mitspielerRepository,
|
MitspielerRepository mitspielerRepository,
|
||||||
EmailChangeRepository emailChangeRepository,
|
EmailChangeRepository emailChangeRepository,
|
||||||
@@ -85,7 +99,9 @@ public class UserController {
|
|||||||
PinnwandEintragRepository pinnwandEintragRepository,
|
PinnwandEintragRepository pinnwandEintragRepository,
|
||||||
PinnwandLikeRepository pinnwandLikeRepository,
|
PinnwandLikeRepository pinnwandLikeRepository,
|
||||||
KommentarRepository kommentarRepository,
|
KommentarRepository kommentarRepository,
|
||||||
KommentarLikeRepository kommentarLikeRepository) {
|
KommentarLikeRepository kommentarLikeRepository,
|
||||||
|
NotificationPreferenceRepository notificationPreferenceRepository,
|
||||||
|
BdsmDefaultsRepository bdsmDefaultsRepository) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.registrationRepository = registrationRepository;
|
this.registrationRepository = registrationRepository;
|
||||||
this.aufgabenGruppeRepository = aufgabenGruppeRepository;
|
this.aufgabenGruppeRepository = aufgabenGruppeRepository;
|
||||||
@@ -106,12 +122,23 @@ public class UserController {
|
|||||||
this.pinnwandLikeRepository = pinnwandLikeRepository;
|
this.pinnwandLikeRepository = pinnwandLikeRepository;
|
||||||
this.kommentarRepository = kommentarRepository;
|
this.kommentarRepository = kommentarRepository;
|
||||||
this.kommentarLikeRepository = kommentarLikeRepository;
|
this.kommentarLikeRepository = kommentarLikeRepository;
|
||||||
|
this.notificationPreferenceRepository = notificationPreferenceRepository;
|
||||||
|
this.bdsmDefaultsRepository = bdsmDefaultsRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
record ProfilePictureRequest(String picture, String pictureHq) {}
|
record ProfilePictureRequest(String picture, String pictureHq) {}
|
||||||
record NameChangeRequest(String name) {}
|
record NameChangeRequest(String name) {}
|
||||||
record ProfileRequest(Integer alter, Integer groesse, Integer gewicht,
|
record GeburtsdatumChangeRequest(LocalDate geburtsdatum) {}
|
||||||
|
record ProfileRequest(Integer groesse, Integer gewicht,
|
||||||
Geschlecht geschlecht, Neigung neigung, Beziehungsstatus beziehungsstatus, String beschreibung) {}
|
Geschlecht geschlecht, Neigung neigung, Beziehungsstatus beziehungsstatus, String beschreibung) {}
|
||||||
|
record PrivacyRequest(
|
||||||
|
Sichtbarkeit sichtbarkeitGrunddaten,
|
||||||
|
Sichtbarkeit sichtbarkeitGalerie,
|
||||||
|
Sichtbarkeit sichtbarkeitFreunde,
|
||||||
|
Sichtbarkeit sichtbarkeitFeed,
|
||||||
|
Sichtbarkeit sichtbarkeitPinnwand,
|
||||||
|
Sichtbarkeit sichtbarkeitXp,
|
||||||
|
Sichtbarkeit sichtbarkeitLockhistorie) {}
|
||||||
|
|
||||||
@PutMapping("/me/picture")
|
@PutMapping("/me/picture")
|
||||||
public ResponseEntity<Void> updateProfilePicture(@RequestBody ProfilePictureRequest request, Principal principal) {
|
public ResponseEntity<Void> updateProfilePicture(@RequestBody ProfilePictureRequest request, Principal principal) {
|
||||||
@@ -132,7 +159,6 @@ public class UserController {
|
|||||||
if (request.beschreibung() != null && request.beschreibung().length() > 600) {
|
if (request.beschreibung() != null && request.beschreibung().length() > 600) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
user.setAlter(request.alter());
|
|
||||||
user.setGroesse(request.groesse());
|
user.setGroesse(request.groesse());
|
||||||
user.setGewicht(request.gewicht());
|
user.setGewicht(request.gewicht());
|
||||||
user.setGeschlecht(request.geschlecht());
|
user.setGeschlecht(request.geschlecht());
|
||||||
@@ -144,6 +170,126 @@ public class UserController {
|
|||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/me/privacy")
|
||||||
|
public ResponseEntity<Void> updatePrivacy(@RequestBody PrivacyRequest request, Principal principal) {
|
||||||
|
var userOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
var user = userOpt.get();
|
||||||
|
if (request.sichtbarkeitGrunddaten() != null) user.setSichtbarkeitGrunddaten(request.sichtbarkeitGrunddaten());
|
||||||
|
if (request.sichtbarkeitGalerie() != null) user.setSichtbarkeitGalerie(request.sichtbarkeitGalerie());
|
||||||
|
if (request.sichtbarkeitFreunde() != null) user.setSichtbarkeitFreunde(request.sichtbarkeitFreunde());
|
||||||
|
if (request.sichtbarkeitFeed() != null) user.setSichtbarkeitFeed(request.sichtbarkeitFeed());
|
||||||
|
if (request.sichtbarkeitPinnwand() != null) user.setSichtbarkeitPinnwand(request.sichtbarkeitPinnwand());
|
||||||
|
if (request.sichtbarkeitXp() != null) user.setSichtbarkeitXp(request.sichtbarkeitXp());
|
||||||
|
if (request.sichtbarkeitLockhistorie()!= null) user.setSichtbarkeitLockhistorie(request.sichtbarkeitLockhistorie());
|
||||||
|
userRepository.save(user);
|
||||||
|
LOGGER.info("User {} hat Datenschutz-Einstellungen aktualisiert", user.getUserId());
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
record NotificationPreferenceRequest(boolean inApp, boolean email) {}
|
||||||
|
|
||||||
|
@GetMapping("/me/notifications")
|
||||||
|
public ResponseEntity<Map<String, Object>> getNotifications(Principal principal) {
|
||||||
|
var userOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
UUID userId = userOpt.get().getUserId();
|
||||||
|
|
||||||
|
Map<String, NotificationPreferenceEntity> byKey = notificationPreferenceRepository.findByUserId(userId)
|
||||||
|
.stream().collect(Collectors.toMap(p -> p.getCause().name(), p -> p));
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
for (MessageCause cause : MessageCause.values()) {
|
||||||
|
NotificationPreferenceEntity pref = byKey.getOrDefault(
|
||||||
|
cause.name(), NotificationPreferenceEntity.defaultFor(userId, cause));
|
||||||
|
Map<String, Object> entry = new LinkedHashMap<>();
|
||||||
|
entry.put("inApp", pref.isInApp());
|
||||||
|
entry.put("email", pref.isEmail());
|
||||||
|
result.put(cause.name(), entry);
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/me/notifications")
|
||||||
|
public ResponseEntity<Void> updateNotifications(@RequestBody Map<String, NotificationPreferenceRequest> request, Principal principal) {
|
||||||
|
var userOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
UUID userId = userOpt.get().getUserId();
|
||||||
|
|
||||||
|
for (var entry : request.entrySet()) {
|
||||||
|
MessageCause cause;
|
||||||
|
try {
|
||||||
|
cause = MessageCause.valueOf(entry.getKey());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NotificationPreferenceEntity pref = notificationPreferenceRepository
|
||||||
|
.findByUserIdAndCause(userId, cause)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
NotificationPreferenceEntity n = new NotificationPreferenceEntity();
|
||||||
|
n.setUserId(userId);
|
||||||
|
n.setCause(cause);
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
pref.setInApp(entry.getValue().inApp());
|
||||||
|
pref.setEmail(entry.getValue().email());
|
||||||
|
notificationPreferenceRepository.save(pref);
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
record BdsmDefaultsRequest(List<String> spieltMit, List<String> rollen, List<String> werkzeuge) {}
|
||||||
|
|
||||||
|
@GetMapping("/me/bdsm-defaults")
|
||||||
|
public ResponseEntity<Map<String, Object>> getBdsmDefaults(Principal principal) {
|
||||||
|
var userOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
UUID userId = userOpt.get().getUserId();
|
||||||
|
|
||||||
|
BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
|
||||||
|
.orElse(new BdsmDefaultsEntity());
|
||||||
|
Map<String, Object> result = new java.util.LinkedHashMap<>();
|
||||||
|
result.put("spieltMit", splitOrEmpty(d.getSpieltMit()));
|
||||||
|
result.put("rollen", splitOrEmpty(d.getRollen()));
|
||||||
|
result.put("werkzeuge", splitOrEmpty(d.getWerkzeuge()));
|
||||||
|
return ResponseEntity.ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/me/bdsm-defaults")
|
||||||
|
public ResponseEntity<Void> updateBdsmDefaults(@RequestBody BdsmDefaultsRequest request, Principal principal) {
|
||||||
|
var userOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
UUID userId = userOpt.get().getUserId();
|
||||||
|
|
||||||
|
BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
|
||||||
|
.orElseGet(() -> { BdsmDefaultsEntity n = new BdsmDefaultsEntity(); n.setUserId(userId); return n; });
|
||||||
|
d.setSpieltMit(request.spieltMit() == null ? "" : String.join(",", request.spieltMit()));
|
||||||
|
d.setRollen(request.rollen() == null ? "" : String.join(",", request.rollen()));
|
||||||
|
d.setWerkzeuge(request.werkzeuge() == null ? "" : String.join(",", request.werkzeuge()));
|
||||||
|
bdsmDefaultsRepository.save(d);
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> splitOrEmpty(String s) {
|
||||||
|
if (s == null || s.isBlank()) return List.of();
|
||||||
|
return List.of(s.split(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/me/geburtsdatum")
|
||||||
|
public ResponseEntity<Void> updateGeburtsdatum(@RequestBody GeburtsdatumChangeRequest request, Principal principal) {
|
||||||
|
if (request.geburtsdatum() == null
|
||||||
|
|| Period.between(request.geburtsdatum(), LocalDate.now()).getYears() < 18) {
|
||||||
|
return ResponseEntity.status(422).build();
|
||||||
|
}
|
||||||
|
var userOpt = userRepository.findByEmail(principal.getName());
|
||||||
|
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||||
|
var user = userOpt.get();
|
||||||
|
user.setGeburtsdatum(request.geburtsdatum());
|
||||||
|
userRepository.save(user);
|
||||||
|
LOGGER.info("User {} hat Geburtsdatum aktualisiert", user.getUserId());
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/me/name")
|
@PutMapping("/me/name")
|
||||||
public ResponseEntity<Void> updateName(@RequestBody NameChangeRequest request, Principal principal) {
|
public ResponseEntity<Void> updateName(@RequestBody NameChangeRequest request, Principal principal) {
|
||||||
String newName = request.name();
|
String newName = request.name();
|
||||||
@@ -264,7 +410,14 @@ public class UserController {
|
|||||||
entity.setEmail(registration.getEmail());
|
entity.setEmail(registration.getEmail());
|
||||||
entity.setName(registration.getName());
|
entity.setName(registration.getName());
|
||||||
entity.setPassword(registration.getPasswordHash());
|
entity.setPassword(registration.getPasswordHash());
|
||||||
|
entity.setGeburtsdatum(registration.getGeburtsdatum());
|
||||||
userRepository.save(entity);
|
userRepository.save(entity);
|
||||||
|
|
||||||
|
for (MessageCause cause : MessageCause.values()) {
|
||||||
|
notificationPreferenceRepository.save(
|
||||||
|
NotificationPreferenceEntity.defaultFor(entity.getUserId(), cause));
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseEntity.status(201).build();
|
return ResponseEntity.status(201).build();
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
LOGGER.error(exception.getMessage(), exception);
|
LOGGER.error(exception.getMessage(), exception);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import jakarta.persistence.*;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.Period;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -28,8 +30,8 @@ public class UserEntity {
|
|||||||
@Column(columnDefinition = "MEDIUMTEXT")
|
@Column(columnDefinition = "MEDIUMTEXT")
|
||||||
private String profilePictureHq;
|
private String profilePictureHq;
|
||||||
|
|
||||||
@Column(name = "benutzer_alter")
|
@Column
|
||||||
private Integer alter;
|
private LocalDate geburtsdatum;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
private Integer groesse;
|
private Integer groesse;
|
||||||
@@ -58,6 +60,39 @@ public class UserEntity {
|
|||||||
@Column(nullable = false, columnDefinition = "INT DEFAULT 0")
|
@Column(nullable = false, columnDefinition = "INT DEFAULT 0")
|
||||||
private int keyholderXp;
|
private int keyholderXp;
|
||||||
|
|
||||||
|
// ── Datenschutz / Sichtbarkeit ──
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitGrunddaten = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitGalerie = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitFreunde = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitFeed = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitPinnwand = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitXp = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'")
|
||||||
|
private Sichtbarkeit sichtbarkeitLockhistorie = Sichtbarkeit.ALLE;
|
||||||
|
|
||||||
|
public Integer getAlter() {
|
||||||
|
return geburtsdatum != null ? Period.between(geburtsdatum, LocalDate.now()).getYears() : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "UserEntity[userId=" + userId + ", name=" + name + ", email=" + email + "]";
|
return "UserEntity[userId=" + userId + ", name=" + name + ", email=" + email + "]";
|
||||||
@@ -69,7 +104,7 @@ public class UserEntity {
|
|||||||
user.setName(name);
|
user.setName(name);
|
||||||
user.setUserId(userId);
|
user.setUserId(userId);
|
||||||
user.setProfilePicture(profilePicture);
|
user.setProfilePicture(profilePicture);
|
||||||
user.setAlter(alter);
|
user.setGeburtsdatum(geburtsdatum);
|
||||||
user.setGroesse(groesse);
|
user.setGroesse(groesse);
|
||||||
user.setGewicht(gewicht);
|
user.setGewicht(gewicht);
|
||||||
user.setGeschlecht(geschlecht);
|
user.setGeschlecht(geschlecht);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Aufgaben – XXX The Game</title>
|
<title>Aufgaben – XXX The Game</title>
|
||||||
@@ -608,44 +608,47 @@
|
|||||||
resetSelection();
|
resetSelection();
|
||||||
document.getElementById('userLoading').style.display = 'block';
|
document.getElementById('userLoading').style.display = 'block';
|
||||||
fetch(`/gruppe/list/user?page=${userPage}&size=${PAGE_SIZE}`)
|
fetch(`/gruppe/list/user?page=${userPage}&size=${PAGE_SIZE}`)
|
||||||
.then(r => r.json())
|
.then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
console.log('[aufgaben] user gruppen:', data);
|
||||||
userTotalPages = data.totalPages || 1;
|
userTotalPages = data.totalPages || 1;
|
||||||
renderGruppen('userList', data.content, 'user');
|
try { renderGruppen('userList', data.content, 'user'); } catch(e) { console.error('[aufgaben] renderGruppen user Fehler:', e); throw e; }
|
||||||
updatePaging('userPaging', 'userPrev', 'userNext', 'userPageInfo', userPage, userTotalPages);
|
updatePaging('userPaging', 'userPrev', 'userNext', 'userPageInfo', userPage, userTotalPages);
|
||||||
document.getElementById('userLoading').style.display = 'none';
|
document.getElementById('userLoading').style.display = 'none';
|
||||||
reapplyPendingExpand();
|
reapplyPendingExpand();
|
||||||
})
|
})
|
||||||
.catch(() => { document.getElementById('userLoading').textContent = 'Fehler beim Laden.'; });
|
.catch(err => { console.error('[aufgaben] Fehler user gruppen:', err); document.getElementById('userLoading').textContent = 'Fehler beim Laden: ' + err.message; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSystemGruppen() {
|
function loadSystemGruppen() {
|
||||||
resetSelection();
|
resetSelection();
|
||||||
document.getElementById('systemLoading').style.display = 'block';
|
document.getElementById('systemLoading').style.display = 'block';
|
||||||
fetch(`/gruppe/list/system?page=${systemPage}&size=${PAGE_SIZE}`)
|
fetch(`/gruppe/list/system?page=${systemPage}&size=${PAGE_SIZE}`)
|
||||||
.then(r => r.json())
|
.then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
console.log('[aufgaben] system gruppen:', data);
|
||||||
systemTotalPages = data.totalPages || 1;
|
systemTotalPages = data.totalPages || 1;
|
||||||
renderGruppen('systemList', data.content, 'system');
|
try { renderGruppen('systemList', data.content, 'system'); } catch(e) { console.error('[aufgaben] renderGruppen system Fehler:', e); throw e; }
|
||||||
updatePaging('systemPaging', 'systemPrev', 'systemNext', 'systemPageInfo', systemPage, systemTotalPages);
|
updatePaging('systemPaging', 'systemPrev', 'systemNext', 'systemPageInfo', systemPage, systemTotalPages);
|
||||||
document.getElementById('systemLoading').style.display = 'none';
|
document.getElementById('systemLoading').style.display = 'none';
|
||||||
reapplyPendingExpand();
|
reapplyPendingExpand();
|
||||||
})
|
})
|
||||||
.catch(() => { document.getElementById('systemLoading').textContent = 'Fehler beim Laden.'; });
|
.catch(err => { console.error('[aufgaben] Fehler system gruppen:', err); document.getElementById('systemLoading').textContent = 'Fehler beim Laden: ' + err.message; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAboGruppen() {
|
function loadAboGruppen() {
|
||||||
document.getElementById('aboLoading').style.display = 'block';
|
document.getElementById('aboLoading').style.display = 'block';
|
||||||
fetch(`/abo/list?page=${aboPage}&size=${PAGE_SIZE}`)
|
fetch(`/abo/list?page=${aboPage}&size=${PAGE_SIZE}`)
|
||||||
.then(r => r.json())
|
.then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
console.log('[aufgaben] abo gruppen:', data);
|
||||||
aboTotalPages = data.totalPages || 1;
|
aboTotalPages = data.totalPages || 1;
|
||||||
renderGruppen('aboList', data.content, 'abo');
|
renderGruppen('aboList', data.content, 'abo');
|
||||||
updatePaging('aboPaging', 'aboPrev', 'aboNext', 'aboPageInfo', aboPage, aboTotalPages);
|
updatePaging('aboPaging', 'aboPrev', 'aboNext', 'aboPageInfo', aboPage, aboTotalPages);
|
||||||
document.getElementById('aboLoading').style.display = 'none';
|
document.getElementById('aboLoading').style.display = 'none';
|
||||||
reapplyPendingExpand();
|
reapplyPendingExpand();
|
||||||
})
|
})
|
||||||
.catch(() => { document.getElementById('aboLoading').textContent = 'Fehler beim Laden.'; });
|
.catch(err => { console.error('[aufgaben] Fehler abo gruppen:', err); document.getElementById('aboLoading').textContent = 'Fehler beim Laden: ' + err.message; });
|
||||||
}
|
}
|
||||||
|
|
||||||
function reapplyPendingExpand() {
|
function reapplyPendingExpand() {
|
||||||
|
|||||||
128
xxxthegame/src/main/resources/static/bdsm-einladung.html
Normal file
128
xxxthegame/src/main/resources/static/bdsm-einladung.html
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>BDSM Game – Einladung – XXX The Game</title>
|
||||||
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
<style>
|
||||||
|
.invite-card {
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 420px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.invite-icon { font-size: 2.5rem; margin-bottom: 1rem; }
|
||||||
|
.invite-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 0.5rem; }
|
||||||
|
.invite-sub { font-size: 0.9rem; color: var(--color-muted); margin-bottom: 2rem; line-height: 1.6; }
|
||||||
|
.invite-actions { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||||
|
.invite-actions button { width: 100%; padding: 0.85rem; }
|
||||||
|
.decline-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
padding: 0.25rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="app">
|
||||||
|
<div class="main">
|
||||||
|
<div class="content">
|
||||||
|
<div id="loading" style="text-align:center;color:var(--color-muted);padding:3rem 0;">Einladung wird geladen…</div>
|
||||||
|
<div class="invite-card" id="card" style="display:none;">
|
||||||
|
<div class="invite-icon">⛓️</div>
|
||||||
|
<div class="invite-title" id="title"></div>
|
||||||
|
<div class="invite-sub" id="sub"></div>
|
||||||
|
<div class="message" id="message" style="display:none;margin-bottom:1rem;"></div>
|
||||||
|
<div class="invite-actions" id="actions"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/js/sidebar.js"></script>
|
||||||
|
<script>
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const einladungId = params.get('id');
|
||||||
|
if (!einladungId) window.location.replace('/userhome.html');
|
||||||
|
|
||||||
|
let einladung = null;
|
||||||
|
|
||||||
|
async function laden() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/einladung/${einladungId}`);
|
||||||
|
if (!res.ok) { zeigeFehler('Einladung nicht gefunden.'); return; }
|
||||||
|
einladung = await res.json();
|
||||||
|
document.getElementById('loading').style.display = 'none';
|
||||||
|
document.getElementById('card').style.display = '';
|
||||||
|
|
||||||
|
if (einladung.status === 'ACCEPTED_OWN' || einladung.status === 'ACCEPTED_HOST') {
|
||||||
|
zeigeBestaetigt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (einladung.status === 'DECLINED' || einladung.status === 'CANCELLED') {
|
||||||
|
zeigeFehler('Diese Einladung ist nicht mehr gültig.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('title').textContent = `${einladung.inviterName || 'Jemand'} lädt dich ein`;
|
||||||
|
document.getElementById('sub').textContent = 'Du wurdest zu einem BDSM Game eingeladen. Wie möchtest du mitspielen?';
|
||||||
|
const actions = document.getElementById('actions');
|
||||||
|
actions.innerHTML = `
|
||||||
|
<button onclick="antworten(true, 'OWN_DEVICE')">Am eigenen Gerät mitspielen</button>
|
||||||
|
<button class="secondary" onclick="antworten(true, 'HOST_DEVICE')">Am Gerät von ${einladung.inviterName || 'der einladenden Person'}</button>
|
||||||
|
<button class="decline-btn" onclick="antworten(false, null)">Einladung ablehnen</button>`;
|
||||||
|
} catch (e) {
|
||||||
|
zeigeFehler('Fehler beim Laden der Einladung.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function antworten(accepted, mode) {
|
||||||
|
document.getElementById('actions').innerHTML = '<div style="color:var(--color-muted);font-size:0.9rem;">Wird gespeichert…</div>';
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/einladung/${einladungId}/antwort`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ accepted, mode }),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error();
|
||||||
|
if (!accepted) {
|
||||||
|
document.getElementById('title').textContent = 'Einladung abgelehnt';
|
||||||
|
document.getElementById('sub').textContent = 'Du hast die Einladung abgelehnt.';
|
||||||
|
document.getElementById('actions').innerHTML = '<button onclick="window.location.href=\'/userhome.html\'">Zur Startseite</button>';
|
||||||
|
} else if (mode === 'OWN_DEVICE') {
|
||||||
|
window.location.replace(`/bdsmwarten.html?id=${einladungId}`);
|
||||||
|
} else {
|
||||||
|
zeigeBestaetigt();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
document.getElementById('actions').innerHTML = '';
|
||||||
|
zeigeFehler('Fehler beim Speichern der Antwort.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeigeBestaetigt() {
|
||||||
|
document.getElementById('title').textContent = 'Einladung angenommen';
|
||||||
|
document.getElementById('sub').textContent = 'Du spielst am Gerät der einladenden Person mit. Das Spiel wird dort von ihr gestartet.';
|
||||||
|
document.getElementById('actions').innerHTML = '<button onclick="window.location.href=\'/userhome.html\'">Zur Startseite</button>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeigeFehler(text) {
|
||||||
|
document.getElementById('loading').style.display = 'none';
|
||||||
|
document.getElementById('card').style.display = '';
|
||||||
|
document.getElementById('title').textContent = 'Hinweis';
|
||||||
|
document.getElementById('sub').textContent = text;
|
||||||
|
document.getElementById('actions').innerHTML = '<button onclick="window.location.href=\'/userhome.html\'">Zur Startseite</button>';
|
||||||
|
}
|
||||||
|
|
||||||
|
laden();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>BDSM Game – Neue Session – XXX The Game</title>
|
<title>BDSM Game – Neue Session – XXX The Game</title>
|
||||||
<link rel="stylesheet" href="/css/variables.css">
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<style>
|
<style>
|
||||||
.session-setup { max-width: 540px; }
|
.session-setup { }
|
||||||
|
|
||||||
.setup-section { margin-bottom: 2.5rem; }
|
.setup-section { margin-bottom: 2.5rem; }
|
||||||
.setup-section h2 {
|
.setup-section h2 {
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
aufgabenProLevel: parseInt(document.getElementById('sldAufgaben').value),
|
aufgabenProLevel: parseInt(document.getElementById('sldAufgaben').value),
|
||||||
zeitfaktorZeitstrafen: parseInt(document.getElementById('sldZeit').value) / 10,
|
zeitfaktorZeitstrafen: parseInt(document.getElementById('sldZeit').value) / 10,
|
||||||
}));
|
}));
|
||||||
window.location.href = '/sessionbdsmplayers.html';
|
window.location.href = '/bdsmplayers.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMessage(text, type) {
|
function showMessage(text, type) {
|
||||||
@@ -224,7 +224,7 @@
|
|||||||
function sessionFortfahren(sid) {
|
function sessionFortfahren(sid) {
|
||||||
BDSM_STORAGE_KEYS.forEach(k => sessionStorage.removeItem(k));
|
BDSM_STORAGE_KEYS.forEach(k => sessionStorage.removeItem(k));
|
||||||
sessionStorage.setItem('bdsm-session-id', sid);
|
sessionStorage.setItem('bdsm-session-id', sid);
|
||||||
window.location.href = '/sessionbdsmingame.html';
|
window.location.href = '/bdsmingame.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
function sessionBeendenFragen(sid) {
|
function sessionBeendenFragen(sid) {
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
async function sessionLoeschen(sid) {
|
async function sessionLoeschen(sid) {
|
||||||
versteckeModal();
|
versteckeModal();
|
||||||
try {
|
try {
|
||||||
await fetch('/session', {
|
await fetch('/bdsm', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ sessionId: sid }),
|
body: JSON.stringify({ sessionId: sid }),
|
||||||
@@ -256,7 +256,7 @@
|
|||||||
if (!meRes.ok) return;
|
if (!meRes.ok) return;
|
||||||
const user = await meRes.json();
|
const user = await meRes.json();
|
||||||
|
|
||||||
const sessionRes = await fetch(`/session?userId=${user.userId}`);
|
const sessionRes = await fetch(`/bdsm?userId=${user.userId}`);
|
||||||
if (sessionRes.status === 204) return;
|
if (sessionRes.status === 204) return;
|
||||||
if (!sessionRes.ok) return;
|
if (!sessionRes.ok) return;
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>BDSM Game – Im Spiel – XXX The Game</title>
|
<title>BDSM Game – Im Spiel – XXX The Game</title>
|
||||||
@@ -236,7 +236,12 @@
|
|||||||
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup') || 'null');
|
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup') || 'null');
|
||||||
const toys = JSON.parse(sessionStorage.getItem('bdsm-session-toys') || '[]');
|
const toys = JSON.parse(sessionStorage.getItem('bdsm-session-toys') || '[]');
|
||||||
const sessionId = sessionStorage.getItem('bdsm-session-id');
|
const sessionId = sessionStorage.getItem('bdsm-session-id');
|
||||||
if (!sessionId) window.location.replace('/sessionbdsm.html');
|
if (!sessionId) window.location.replace('/bdsm.html');
|
||||||
|
|
||||||
|
// Multi-Device: bin ich Gast?
|
||||||
|
const isGuest = sessionStorage.getItem('bdsm-is-guest') === 'true';
|
||||||
|
const myMitspielerId = sessionStorage.getItem('bdsm-guest-mitspieler-id') || null;
|
||||||
|
let guestPollInterval = null;
|
||||||
|
|
||||||
// ── Modal ──
|
// ── Modal ──
|
||||||
function zeigeModal(title, text, actions) {
|
function zeigeModal(title, text, actions) {
|
||||||
@@ -283,6 +288,7 @@
|
|||||||
|
|
||||||
function clearTimer() {
|
function clearTimer() {
|
||||||
if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }
|
if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }
|
||||||
|
stopHostPoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function zeigeTaskFehler(text) {
|
function zeigeTaskFehler(text) {
|
||||||
@@ -310,7 +316,7 @@
|
|||||||
card.innerHTML = 'Aufgabe wird geladen…';
|
card.innerHTML = 'Aufgabe wird geladen…';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/session/${sessionId}/aufgaben/next`);
|
const res = await fetch(`/bdsm/${sessionId}/aufgaben/next`);
|
||||||
if (res.status === 204) { zeigeFinaleDialog(); return; }
|
if (res.status === 204) { zeigeFinaleDialog(); return; }
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
currentTask = await res.json();
|
currentTask = await res.json();
|
||||||
@@ -326,9 +332,189 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Aktive Aufgabe persistieren ──
|
||||||
|
async function saveAktiveAufgabe(task, timerStartedAt) {
|
||||||
|
try {
|
||||||
|
await fetch(`/bdsm/${sessionId}/active-task`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ taskJson: JSON.stringify(task), timerStartedAt }),
|
||||||
|
});
|
||||||
|
} catch (_) { /* ignorieren */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearAktiveAufgabe() {
|
||||||
|
try {
|
||||||
|
await fetch(`/bdsm/${sessionId}/active-task`, { method: 'DELETE' });
|
||||||
|
} catch (_) { /* ignorieren */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkAktiveAufgabe() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/${sessionId}/active-task`);
|
||||||
|
if (res.status === 204 || !res.ok) { ladeAufgabe(); return; }
|
||||||
|
const data = await res.json();
|
||||||
|
currentTask = JSON.parse(data.taskJson);
|
||||||
|
if (currentTask.level) {
|
||||||
|
document.getElementById('levelImg').src = `/img/lvl${currentTask.level}.png`;
|
||||||
|
document.getElementById('levelDisplay').style.display = '';
|
||||||
|
}
|
||||||
|
if (data.elapsedSeconds !== null && data.elapsedSeconds !== undefined) {
|
||||||
|
const remaining = currentTask.timer - data.elapsedSeconds;
|
||||||
|
if (remaining <= 0) {
|
||||||
|
await clearAktiveAufgabe();
|
||||||
|
aufgabeAbgeschlossen();
|
||||||
|
} else {
|
||||||
|
restoreTimer(Math.floor(remaining));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
zeigeAufgabe();
|
||||||
|
}
|
||||||
|
} catch (_) { ladeAufgabe(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreTimer(remaining) {
|
||||||
|
const task = currentTask;
|
||||||
|
const card = document.getElementById('taskCard');
|
||||||
|
card.className = 'task-card';
|
||||||
|
card.innerHTML = `
|
||||||
|
${badgeHtml(task.nameAktiverMitspieler)}
|
||||||
|
<div class="task-text" title="${escapeAttr(task.aufgabeText)}">${task.aufgabeText}</div>
|
||||||
|
<div class="task-footer">
|
||||||
|
<div class="task-timer-row" id="taskActions">
|
||||||
|
<div class="timer-big" id="timerValue">${formatTime(remaining)}</div>
|
||||||
|
<button class="btn-sm-cancel" onclick="timerAbbrechen()">✕ Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
${SESSION_BEENDEN_BTN}
|
||||||
|
</div>`;
|
||||||
|
let rem = remaining;
|
||||||
|
timerInterval = setInterval(() => {
|
||||||
|
rem--;
|
||||||
|
const el = document.getElementById('timerValue');
|
||||||
|
if (rem <= 0) {
|
||||||
|
clearTimer();
|
||||||
|
if (el) { el.textContent = formatTime(0); el.classList.add('expired'); }
|
||||||
|
aufgabeAbgeschlossen();
|
||||||
|
} else {
|
||||||
|
if (el) el.textContent = formatTime(rem);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Gast-Polling: wartet auf aktive Aufgabe für mein Gerät ──
|
||||||
|
function startGastPoll() {
|
||||||
|
if (guestPollInterval) return;
|
||||||
|
const card = document.getElementById('taskCard');
|
||||||
|
card.className = 'task-card loading';
|
||||||
|
card.innerHTML = 'Warte auf Aufgabe…';
|
||||||
|
guestPollInterval = setInterval(pollGastAufgabe, 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollGastAufgabe() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/${sessionId}/active-task`);
|
||||||
|
if (res.status === 204) {
|
||||||
|
// Keine aktive Aufgabe – warten
|
||||||
|
const card = document.getElementById('taskCard');
|
||||||
|
if (!card.classList.contains('loading')) {
|
||||||
|
card.className = 'task-card loading';
|
||||||
|
card.innerHTML = 'Warte auf Aufgabe…';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!res.ok) return;
|
||||||
|
const data = await res.json();
|
||||||
|
const task = JSON.parse(data.taskJson);
|
||||||
|
|
||||||
|
if (task.mitspielerId && task.mitspielerId === myMitspielerId) {
|
||||||
|
// Meine Aufgabe!
|
||||||
|
clearInterval(guestPollInterval); guestPollInterval = null;
|
||||||
|
currentTask = task;
|
||||||
|
if (task.level) {
|
||||||
|
document.getElementById('levelImg').src = `/img/lvl${task.level}.png`;
|
||||||
|
document.getElementById('levelDisplay').style.display = '';
|
||||||
|
}
|
||||||
|
if (data.elapsedSeconds !== null && data.elapsedSeconds !== undefined && task.timer != null) {
|
||||||
|
const remaining = task.timer - data.elapsedSeconds;
|
||||||
|
if (remaining <= 0) { await clearAktiveAufgabe(); gastAufgabeAbgeschlossen(); }
|
||||||
|
else restoreTimer(Math.floor(remaining));
|
||||||
|
} else {
|
||||||
|
zeigeGastAufgabe(task);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Jemand anderes ist dran
|
||||||
|
const card = document.getElementById('taskCard');
|
||||||
|
const name = task.nameAktiverMitspieler || 'Jemand';
|
||||||
|
card.className = 'task-card loading';
|
||||||
|
card.innerHTML = `<div style="font-size:1rem;color:var(--color-muted);">${name} ist dran…</div>`;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeigeGastAufgabe(task) {
|
||||||
|
// Gast sieht seine Aufgabe mit eigenem "Erledigt"-Button
|
||||||
|
const cb = task.callback;
|
||||||
|
if (task.timer != null && !data?.elapsedSeconds) zeigeTimerAufgabe(task);
|
||||||
|
else if (cb && cb.sperreId != null) zeigeSperreAufgabe(task);
|
||||||
|
else if (cb && cb.faktor != null) zeigeVerlaengernAufgabe(task);
|
||||||
|
else zeigeEinfacheAufgabe(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function gastAufgabeAbgeschlossen() {
|
||||||
|
clearTimer();
|
||||||
|
await clearAktiveAufgabe();
|
||||||
|
const card = document.getElementById('taskCard');
|
||||||
|
card.className = 'task-card loading';
|
||||||
|
card.innerHTML = 'Aufgabe erledigt – warte auf nächste Aufgabe…';
|
||||||
|
startGastPoll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Host: wenn Aufgabe einem eigenem-Gerät-Spieler gehört ──
|
||||||
|
let hostPollInterval = null;
|
||||||
|
|
||||||
|
function startHostPoll() {
|
||||||
|
if (hostPollInterval) return;
|
||||||
|
hostPollInterval = setInterval(pollHostAktiv, 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopHostPoll() {
|
||||||
|
if (hostPollInterval) { clearInterval(hostPollInterval); hostPollInterval = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollHostAktiv() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/${sessionId}/active-task`);
|
||||||
|
if (res.status === 204) {
|
||||||
|
// Gast hat Aufgabe erledigt → Host lädt nächste
|
||||||
|
stopHostPoll();
|
||||||
|
ladeAufgabe();
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
function zeigeAufgabe() {
|
function zeigeAufgabe() {
|
||||||
const task = currentTask;
|
const task = currentTask;
|
||||||
const cb = task.callback;
|
const cb = task.callback;
|
||||||
|
saveAktiveAufgabe(task, null);
|
||||||
|
|
||||||
|
// Wenn Aufgabe für eigenes-Gerät-Spieler: Host zeigt nur Hinweis
|
||||||
|
if (!isGuest && task.eigenesGeraet && task.mitspielerId) {
|
||||||
|
const name = task.nameAktiverMitspieler || 'Jemand';
|
||||||
|
const card = document.getElementById('taskCard');
|
||||||
|
card.className = 'task-card';
|
||||||
|
card.innerHTML = `
|
||||||
|
<div style="flex:1;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:0.5rem;">
|
||||||
|
<div style="font-size:1.8rem;">📱</div>
|
||||||
|
<div style="font-size:1rem;color:var(--color-muted);text-align:center;">${name} spielt gerade auf dem eigenen Gerät.</div>
|
||||||
|
</div>
|
||||||
|
<div class="task-footer">
|
||||||
|
<div class="task-btns"></div>
|
||||||
|
${SESSION_BEENDEN_BTN}
|
||||||
|
</div>`;
|
||||||
|
startHostPoll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cb && cb.sperreId != null) zeigeSperreAufgabe(task);
|
if (cb && cb.sperreId != null) zeigeSperreAufgabe(task);
|
||||||
else if (cb && cb.faktor != null) zeigeVerlaengernAufgabe(task);
|
else if (cb && cb.faktor != null) zeigeVerlaengernAufgabe(task);
|
||||||
else if (task.timer != null) zeigeTimerAufgabe(task);
|
else if (task.timer != null) zeigeTimerAufgabe(task);
|
||||||
@@ -380,6 +566,7 @@
|
|||||||
actions.innerHTML = `
|
actions.innerHTML = `
|
||||||
<div class="timer-big" id="timerValue">${formatTime(remaining)}</div>
|
<div class="timer-big" id="timerValue">${formatTime(remaining)}</div>
|
||||||
<button class="btn-sm-cancel" onclick="timerAbbrechen()">✕ Abbrechen</button>`;
|
<button class="btn-sm-cancel" onclick="timerAbbrechen()">✕ Abbrechen</button>`;
|
||||||
|
saveAktiveAufgabe(task, new Date().toISOString());
|
||||||
|
|
||||||
timerInterval = setInterval(() => {
|
timerInterval = setInterval(() => {
|
||||||
remaining--;
|
remaining--;
|
||||||
@@ -405,8 +592,10 @@
|
|||||||
|
|
||||||
async function aufgabeAbgeschlossen() {
|
async function aufgabeAbgeschlossen() {
|
||||||
clearTimer();
|
clearTimer();
|
||||||
|
await clearAktiveAufgabe();
|
||||||
|
if (isGuest) { gastAufgabeAbgeschlossen(); return; }
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/session/sperre/abgelaufene?sessionId=${sessionId}`);
|
const res = await fetch(`/bdsm/sperre/abgelaufene?sessionId=${sessionId}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
const texte = text.split(';').map(t => t.trim()).filter(t => t.length > 0);
|
const texte = text.split(';').map(t => t.trim()).filter(t => t.length > 0);
|
||||||
@@ -429,7 +618,7 @@
|
|||||||
const cb = currentTask?.callback;
|
const cb = currentTask?.callback;
|
||||||
if (!cb) return;
|
if (!cb) return;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/session/sperre', {
|
const res = await fetch('/bdsm/sperre', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ ...cb, sessionId }),
|
body: JSON.stringify({ ...cb, sessionId }),
|
||||||
@@ -445,7 +634,7 @@
|
|||||||
const cb = currentTask?.callback;
|
const cb = currentTask?.callback;
|
||||||
if (!cb) return;
|
if (!cb) return;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/session/sperre/verlaengern', {
|
const res = await fetch('/bdsm/sperre/verlaengern', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ ...cb, sessionId }),
|
body: JSON.stringify({ ...cb, sessionId }),
|
||||||
@@ -478,14 +667,14 @@
|
|||||||
|
|
||||||
async function zurueckZuLevel5() {
|
async function zurueckZuLevel5() {
|
||||||
try {
|
try {
|
||||||
await fetch(`/session/${sessionId}/backToLevel5`, { method: 'POST' });
|
await fetch(`/bdsm/${sessionId}/backToLevel5`, { method: 'POST' });
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
ladeAufgabe();
|
ladeAufgabe();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function starteFinale() {
|
async function starteFinale() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/session/sperre/aktive?sessionId=${sessionId}`);
|
const res = await fetch(`/bdsm/sperre/aktive?sessionId=${sessionId}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const sperren = await res.json();
|
const sperren = await res.json();
|
||||||
const texte = (sperren || []).map(s => s.releaseText).filter(t => t);
|
const texte = (sperren || []).map(s => s.releaseText).filter(t => t);
|
||||||
@@ -506,7 +695,7 @@
|
|||||||
|
|
||||||
async function ladeFinisher() {
|
async function ladeFinisher() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/session/${sessionId}/finisher`);
|
const res = await fetch(`/bdsm/${sessionId}/finisher`);
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
const liste = await res.json();
|
const liste = await res.json();
|
||||||
naechsterFinisher(liste, 0);
|
naechsterFinisher(liste, 0);
|
||||||
@@ -572,7 +761,7 @@
|
|||||||
async function sessionLoeschen() {
|
async function sessionLoeschen() {
|
||||||
versteckeModal();
|
versteckeModal();
|
||||||
try {
|
try {
|
||||||
await fetch('/session', {
|
await fetch('/bdsm', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ sessionId }),
|
body: JSON.stringify({ sessionId }),
|
||||||
@@ -583,7 +772,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Start ──
|
// ── Start ──
|
||||||
ladeAufgabe();
|
if (isGuest) {
|
||||||
|
startGastPoll();
|
||||||
|
} else {
|
||||||
|
checkAktiveAufgabe();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
743
xxxthegame/src/main/resources/static/bdsmplayers.html
Normal file
743
xxxthegame/src/main/resources/static/bdsmplayers.html
Normal file
@@ -0,0 +1,743 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>BDSM Game – Mitspieler – XXX The Game</title>
|
||||||
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
<style>
|
||||||
|
.session-setup { }
|
||||||
|
|
||||||
|
.setup-section { margin-bottom: 2.5rem; }
|
||||||
|
.setup-section h2 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--color-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Player cards ── */
|
||||||
|
.player-card {
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.player-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
.player-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.player-badge {
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.1rem 0.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.player-badge-pending {
|
||||||
|
background: var(--color-secondary);
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.1rem 0.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.player-badge-accepted {
|
||||||
|
background: #1a5c2a;
|
||||||
|
color: #6fcf97;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.1rem 0.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.player-remove {
|
||||||
|
margin-left: auto;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
color: var(--color-muted);
|
||||||
|
padding: 0.25rem 0.6rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: normal;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.player-remove:hover {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-primary);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.btn-invite {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-primary);
|
||||||
|
color: var(--color-primary);
|
||||||
|
padding: 0.45rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.btn-invite:hover { background: var(--color-primary); color: #fff; }
|
||||||
|
.btn-cancel-invite {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
color: var(--color-muted);
|
||||||
|
padding: 0.2rem 0.6rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: normal;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.btn-cancel-invite:hover { border-color: var(--color-primary); color: var(--color-primary); background: transparent; }
|
||||||
|
|
||||||
|
.pending-info {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
}
|
||||||
|
.pending-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
.pending-mode {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-field { margin-bottom: 1rem; }
|
||||||
|
.card-field > label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #aaa;
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-group { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
||||||
|
.check-group--two-col { display: grid; grid-template-columns: 1fr 1fr; }
|
||||||
|
.check-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.45rem;
|
||||||
|
background: var(--color-secondary);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.4rem 0.7rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.check-item.is-checked { border-color: var(--color-primary); }
|
||||||
|
.check-item input { accent-color: var(--color-primary); width: auto; margin-top: 0.15rem; cursor: pointer; flex-shrink: 0; }
|
||||||
|
.check-item-label { font-size: 0.88rem; color: var(--color-text); line-height: 1.3; }
|
||||||
|
.check-item-desc { display: block; font-size: 0.72rem; color: var(--color-muted); margin-top: 0.1rem; }
|
||||||
|
|
||||||
|
.add-player-btn {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px dashed var(--color-secondary);
|
||||||
|
color: var(--color-muted);
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s, color 0.15s;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.add-player-btn:hover { border-color: var(--color-primary); color: var(--color-text); background: transparent; }
|
||||||
|
|
||||||
|
.field-error { font-size: 0.78rem; color: var(--color-primary); margin-top: 0.3rem; display: none; }
|
||||||
|
|
||||||
|
/* ── Freunde-Modal ── */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed; inset: 0;
|
||||||
|
background: rgba(0,0,0,0.75);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
.modal-card {
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 1.75rem;
|
||||||
|
max-width: 420px; width: 100%;
|
||||||
|
}
|
||||||
|
.modal-title { font-size: 1rem; font-weight: 700; margin-bottom: 1rem; }
|
||||||
|
.check-item.is-disabled { opacity: 0.5; pointer-events: none; cursor: default; }
|
||||||
|
.friend-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; background: var(--color-secondary); flex-shrink: 0; }
|
||||||
|
.friend-combobox { position: relative; }
|
||||||
|
.friend-dropdown {
|
||||||
|
display: none; position: absolute; top: 100%; left: 0; right: 0;
|
||||||
|
background: var(--color-card); border: 1px solid var(--color-secondary);
|
||||||
|
border-radius: 8px; max-height: 220px; overflow-y: auto; z-index: 10; margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
.friend-dropdown-item {
|
||||||
|
display: flex; align-items: center; gap: 0.75rem;
|
||||||
|
padding: 0.6rem 0.75rem; cursor: pointer; transition: background 0.1s;
|
||||||
|
font-size: 0.9rem; font-weight: 600;
|
||||||
|
}
|
||||||
|
.friend-dropdown-item:hover { background: var(--color-secondary); }
|
||||||
|
.selected-friend-box {
|
||||||
|
display: none; margin-top: 0.75rem; padding: 0.6rem 0.75rem;
|
||||||
|
background: var(--color-secondary); border-radius: 8px;
|
||||||
|
font-size: 0.9rem; font-weight: 600;
|
||||||
|
border: 1px solid var(--color-primary); color: var(--color-text);
|
||||||
|
}
|
||||||
|
.modal-cancel { margin-top: 0.6rem; width: 100%; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="app">
|
||||||
|
|
||||||
|
<div class="modal-overlay" id="friendModal" style="display:none;">
|
||||||
|
<div class="modal-card">
|
||||||
|
<div class="modal-title">Freund einladen</div>
|
||||||
|
<div class="friend-combobox">
|
||||||
|
<input type="text" id="friendSearch" placeholder="Name eingeben…" autocomplete="off" oninput="filterFreunde(this.value)">
|
||||||
|
<div class="friend-dropdown" id="friendDropdown"></div>
|
||||||
|
</div>
|
||||||
|
<div class="selected-friend-box" id="selectedFriendBox"></div>
|
||||||
|
<button id="btnEinladen" style="margin-top:1rem; width:100%;" disabled onclick="confirmedEinladen()">Einladen</button>
|
||||||
|
<button class="secondary modal-cancel" onclick="schliesseFriendModal()">Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main">
|
||||||
|
<div class="content session-setup">
|
||||||
|
|
||||||
|
<h1>BDSM Game</h1>
|
||||||
|
<p style="margin-bottom:2rem;">Schritt 2 von 4 – Mitspieler</p>
|
||||||
|
|
||||||
|
<div class="setup-section">
|
||||||
|
<h2>Mitspieler</h2>
|
||||||
|
<div id="playersContainer"></div>
|
||||||
|
<button class="add-player-btn" onclick="addPlayer()">+ Spieler hinzufügen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message" id="message"></div>
|
||||||
|
<div style="display:flex; gap:1rem;">
|
||||||
|
<button style="flex:1;" class="secondary" onclick="window.location.href='/bdsm.html'">← Zurück</button>
|
||||||
|
<button style="flex:2;" id="weiterBtn" onclick="weiter()">Weiter</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/sidebar.js"></script>
|
||||||
|
<script>
|
||||||
|
if (!sessionStorage.getItem('bdsm-session-settings')) {
|
||||||
|
window.location.replace('/bdsm.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupId erzeugen (persistent über sessionStorage)
|
||||||
|
if (!sessionStorage.getItem('bdsm-setup-id')) {
|
||||||
|
sessionStorage.setItem('bdsm-setup-id', crypto.randomUUID());
|
||||||
|
}
|
||||||
|
const setupId = sessionStorage.getItem('bdsm-setup-id');
|
||||||
|
|
||||||
|
const GESCHLECHTER = [
|
||||||
|
{ value: 'MAENNLICH', label: 'Männlich' },
|
||||||
|
{ value: 'WEIBLICH', label: 'Weiblich' },
|
||||||
|
{ value: 'DIVERS', label: 'Divers' },
|
||||||
|
];
|
||||||
|
const ROLLEN = [
|
||||||
|
{ value: 'AUFGABE_AKTIV', label: 'Aufgabe – Aktiv' },
|
||||||
|
{ value: 'AUFGABE_PASSIV', label: 'Aufgabe – Passiv' },
|
||||||
|
{ value: 'BESTRAFUNG_AKTIV', label: 'Bestrafung – Aktiv' },
|
||||||
|
{ value: 'BESTRAFUNG_PASSIV', label: 'Bestrafung – Passiv' },
|
||||||
|
];
|
||||||
|
const WERKZEUGE_DEFAULTS = {
|
||||||
|
MAENNLICH: ['MUND', 'PENIS', 'ANUS', 'UMSCHNALLDILDO'],
|
||||||
|
WEIBLICH: ['MUND', 'VAGINA', 'ANUS', 'UMSCHNALLDILDO'],
|
||||||
|
DIVERS: ['MUND', 'ANUS', 'UMSCHNALLDILDO'],
|
||||||
|
};
|
||||||
|
const WERKZEUGE = [
|
||||||
|
{ value: 'MUND', label: 'Mund', desc: 'Gewillt den Mund einzusetzen' },
|
||||||
|
{ value: 'VAGINA', label: 'Vagina', desc: 'Verfügt über eine Vagina und setzt sie ein' },
|
||||||
|
{ value: 'PENIS', label: 'Penis', desc: 'Verfügt über einen Penis und setzt ihn ein' },
|
||||||
|
{ value: 'ANUS', label: 'Anus', desc: 'Gewillt den Anus einzusetzen' },
|
||||||
|
{ value: 'UMSCHNALLDILDO', label: 'Umschnall-Dildo', desc: 'Verfügt über einen Umschnall-Dildo' },
|
||||||
|
];
|
||||||
|
const ROLE_LABELS = {
|
||||||
|
AUFGABE_AKTIV: 'Aufgabe – Aktiv', AUFGABE_PASSIV: 'Aufgabe – Passiv',
|
||||||
|
BESTRAFUNG_AKTIV: 'Bestrafung – Aktiv', BESTRAFUNG_PASSIV: 'Bestrafung – Passiv',
|
||||||
|
};
|
||||||
|
|
||||||
|
let playerSeq = 0;
|
||||||
|
let playerIds = [];
|
||||||
|
// { [playerId]: { einladungId, status, inviteeId, inviteeName, mode } | null }
|
||||||
|
let playerInvitations = {};
|
||||||
|
let pollIntervalId = null;
|
||||||
|
let myUserId = null;
|
||||||
|
let freundeListe = [];
|
||||||
|
|
||||||
|
function buildCheckItems(name, items, type, disabled = false) {
|
||||||
|
return items.map(({ value, label, desc }) => `
|
||||||
|
<label class="check-item${disabled ? ' is-disabled' : ''}">
|
||||||
|
<input type="${type}" name="${name}" value="${value}"${disabled ? ' disabled' : ''}>
|
||||||
|
<span>
|
||||||
|
<span class="check-item-label">${label}</span>
|
||||||
|
${desc ? `<span class="check-item-desc">${desc}</span>` : ''}
|
||||||
|
</span>
|
||||||
|
</label>`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCardHtml(id, prefillName, isSelf) {
|
||||||
|
const badge = isSelf ? '<span class="player-badge">Du</span>' : '';
|
||||||
|
const num = playerIds.indexOf(id) + 1;
|
||||||
|
const nameField = isSelf
|
||||||
|
? `<input type="text" id="p${id}-name" value="${prefillName}" readonly style="background:transparent;cursor:default;color:var(--color-muted);">`
|
||||||
|
: `<input type="text" id="p${id}-name" value="${prefillName}" placeholder="Name" autocomplete="off">`;
|
||||||
|
const inviteBtn = isSelf ? '' : `<button class="btn-invite" onclick="oeffneFreundeModal(${id})">👥 Einladen</button>`;
|
||||||
|
return `
|
||||||
|
<div class="player-card" id="player-${id}">
|
||||||
|
<div class="player-card-header">
|
||||||
|
<span class="player-title">Spieler ${num}</span>
|
||||||
|
${badge}
|
||||||
|
${inviteBtn}
|
||||||
|
<button class="player-remove" onclick="removePlayer(${id})">✕ Entfernen</button>
|
||||||
|
</div>
|
||||||
|
<div id="p${id}-body">
|
||||||
|
${buildPlayerBody(id, nameField, isSelf)}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPlayerBody(id, nameField, genderDisabled = false) {
|
||||||
|
return `
|
||||||
|
<div class="card-field">
|
||||||
|
<label>Name</label>
|
||||||
|
${nameField}
|
||||||
|
<div class="field-error" id="p${id}-name-err">Bitte Namen eingeben.</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-field">
|
||||||
|
<label>Geschlecht${genderDisabled ? ' <span style="font-size:0.75rem;color:var(--color-muted);">(unveränderlich)</span>' : ''}</label>
|
||||||
|
<div class="check-group">${buildCheckItems('p' + id + '-geschlecht', GESCHLECHTER, 'radio', genderDisabled)}</div>
|
||||||
|
<div class="field-error" id="p${id}-geschlecht-err">Bitte Geschlecht auswählen.</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-field">
|
||||||
|
<label>Spielt mit</label>
|
||||||
|
<div class="check-group">${buildCheckItems('p' + id + '-spieltmit', GESCHLECHTER, 'checkbox')}</div>
|
||||||
|
<div class="field-error" id="p${id}-spieltmit-err">Bitte mindestens eine Option wählen.</div>
|
||||||
|
<div class="field-error" id="p${id}-partner-err">Kein Mitspieler mit passendem Geschlecht vorhanden.</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-field">
|
||||||
|
<label>Rollen</label>
|
||||||
|
<div class="check-group">${buildCheckItems('p' + id + '-rollen', ROLLEN, 'checkbox')}</div>
|
||||||
|
<div class="field-error" id="p${id}-rollen-err">Bitte mindestens eine Rolle wählen.</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-field">
|
||||||
|
<label>Verfügbar</label>
|
||||||
|
<div class="check-group check-group--two-col">${buildCheckItems('p' + id + '-werkzeuge', WERKZEUGE, 'checkbox')}</div>
|
||||||
|
<div class="field-error" id="p${id}-werkzeuge-err">Bitte mindestens ein Werkzeug wählen.</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPlayer(prefillName = '', isSelf = false) {
|
||||||
|
playerSeq++;
|
||||||
|
const id = playerSeq;
|
||||||
|
playerIds.push(id);
|
||||||
|
playerInvitations[id] = null;
|
||||||
|
document.getElementById('playersContainer')
|
||||||
|
.insertAdjacentHTML('beforeend', createCardHtml(id, prefillName, isSelf));
|
||||||
|
refreshRemoveButtons();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePlayer(id) {
|
||||||
|
const inv = playerInvitations[id];
|
||||||
|
if (inv && (inv.status === 'PENDING' || inv.status === 'ACCEPTED_OWN' || inv.status === 'ACCEPTED_HOST')) {
|
||||||
|
cancelEinladung(id);
|
||||||
|
}
|
||||||
|
document.getElementById('player-' + id)?.remove();
|
||||||
|
playerIds = playerIds.filter(x => x !== id);
|
||||||
|
delete playerInvitations[id];
|
||||||
|
refreshPlayerTitles();
|
||||||
|
refreshRemoveButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshPlayerTitles() {
|
||||||
|
playerIds.forEach((id, idx) => {
|
||||||
|
const el = document.querySelector(`#player-${id} .player-title`);
|
||||||
|
if (el) el.textContent = 'Spieler ' + (idx + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshRemoveButtons() {
|
||||||
|
const canRemove = playerIds.length > 2;
|
||||||
|
playerIds.forEach(id => {
|
||||||
|
const btn = document.querySelector(`#player-${id} .player-remove`);
|
||||||
|
if (btn) btn.style.display = canRemove ? '' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('change', e => {
|
||||||
|
const input = e.target;
|
||||||
|
if (input.type !== 'checkbox' && input.type !== 'radio') return;
|
||||||
|
if (input.type === 'radio') {
|
||||||
|
document.querySelectorAll(`input[name="${input.name}"]`).forEach(r => {
|
||||||
|
r.closest('.check-item')?.classList.toggle('is-checked', r.checked);
|
||||||
|
});
|
||||||
|
if (input.checked && input.name.endsWith('-geschlecht')) {
|
||||||
|
const prefix = input.name.slice(0, -'-geschlecht'.length);
|
||||||
|
const defaults = WERKZEUGE_DEFAULTS[input.value] || [];
|
||||||
|
document.querySelectorAll(`input[name="${prefix}-werkzeuge"]`).forEach(cb => {
|
||||||
|
cb.checked = defaults.includes(cb.value);
|
||||||
|
cb.closest('.check-item')?.classList.toggle('is-checked', cb.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input.closest('.check-item')?.classList.toggle('is-checked', input.checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getChecked(name) {
|
||||||
|
return [...document.querySelectorAll(`input[name="${name}"]:checked`)].map(el => el.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFieldError(id, show) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.style.display = show ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Freunde-Modal ──
|
||||||
|
let currentInvitePlayerId = null;
|
||||||
|
let selectedFriend = null; // { userId, name }
|
||||||
|
|
||||||
|
async function oeffneFreundeModal(playerId) {
|
||||||
|
currentInvitePlayerId = playerId;
|
||||||
|
selectedFriend = null;
|
||||||
|
document.getElementById('friendSearch').value = '';
|
||||||
|
document.getElementById('friendDropdown').style.display = 'none';
|
||||||
|
document.getElementById('friendDropdown').innerHTML = '';
|
||||||
|
document.getElementById('selectedFriendBox').style.display = 'none';
|
||||||
|
document.getElementById('selectedFriendBox').textContent = '';
|
||||||
|
document.getElementById('btnEinladen').disabled = true;
|
||||||
|
document.getElementById('friendModal').style.display = 'flex';
|
||||||
|
if (freundeListe.length === 0) {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/social/friends');
|
||||||
|
freundeListe = res.ok ? await res.json() : [];
|
||||||
|
} catch (_) { freundeListe = []; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterFreunde(query) {
|
||||||
|
selectedFriend = null;
|
||||||
|
document.getElementById('selectedFriendBox').style.display = 'none';
|
||||||
|
document.getElementById('btnEinladen').disabled = true;
|
||||||
|
const dropdown = document.getElementById('friendDropdown');
|
||||||
|
dropdown.innerHTML = '';
|
||||||
|
const q = query.trim().toLowerCase();
|
||||||
|
if (!q) { dropdown.style.display = 'none'; return; }
|
||||||
|
const matches = freundeListe.filter(f => (f.user.name || '').toLowerCase().includes(q));
|
||||||
|
if (!matches.length) {
|
||||||
|
dropdown.innerHTML = '<div style="padding:0.6rem 0.75rem;color:var(--color-muted);font-size:0.9rem;">Keine Treffer.</div>';
|
||||||
|
dropdown.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
matches.forEach(f => {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'friend-dropdown-item';
|
||||||
|
item.addEventListener('click', () => selectFriend(f.user.userId, f.user.name || 'Unbekannt'));
|
||||||
|
if (f.user.profilePicture) {
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.className = 'friend-avatar';
|
||||||
|
img.src = 'data:image/png;base64,' + f.user.profilePicture;
|
||||||
|
img.alt = '';
|
||||||
|
item.appendChild(img);
|
||||||
|
} else {
|
||||||
|
const av = document.createElement('div');
|
||||||
|
av.className = 'friend-avatar';
|
||||||
|
item.appendChild(av);
|
||||||
|
}
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.textContent = f.user.name || 'Unbekannt';
|
||||||
|
item.appendChild(span);
|
||||||
|
dropdown.appendChild(item);
|
||||||
|
});
|
||||||
|
dropdown.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFriend(userId, name) {
|
||||||
|
selectedFriend = { userId, name };
|
||||||
|
document.getElementById('friendSearch').value = name;
|
||||||
|
document.getElementById('friendDropdown').style.display = 'none';
|
||||||
|
const box = document.getElementById('selectedFriendBox');
|
||||||
|
box.textContent = '✓ ' + name;
|
||||||
|
box.style.display = 'block';
|
||||||
|
document.getElementById('btnEinladen').disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmedEinladen() {
|
||||||
|
if (!selectedFriend) return;
|
||||||
|
await einladen(selectedFriend.userId, selectedFriend.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function schliesseFriendModal() {
|
||||||
|
document.getElementById('friendModal').style.display = 'none';
|
||||||
|
currentInvitePlayerId = null;
|
||||||
|
selectedFriend = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function einladen(inviteeId, inviteeName) {
|
||||||
|
const id = currentInvitePlayerId;
|
||||||
|
schliesseFriendModal();
|
||||||
|
if (!id) return;
|
||||||
|
try {
|
||||||
|
const res = await fetch('/bdsm/einladung', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ setupId, slotIndex: id, inviteeId }),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error();
|
||||||
|
const data = await res.json();
|
||||||
|
playerInvitations[id] = { einladungId: data.einladungId, status: 'PENDING', inviteeId, inviteeName };
|
||||||
|
renderPending(id);
|
||||||
|
startPoll();
|
||||||
|
} catch (_) {
|
||||||
|
showMessage('Einladung konnte nicht gesendet werden.', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPending(id) {
|
||||||
|
const inv = playerInvitations[id];
|
||||||
|
if (!inv) return;
|
||||||
|
const body = document.getElementById(`p${id}-body`);
|
||||||
|
if (!body) return;
|
||||||
|
const headerInvBtn = document.querySelector(`#player-${id} .btn-invite`);
|
||||||
|
if (headerInvBtn) headerInvBtn.style.display = 'none';
|
||||||
|
|
||||||
|
if (inv.status === 'PENDING') {
|
||||||
|
// Badge
|
||||||
|
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||||
|
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||||
|
header.insertAdjacentHTML('afterbegin', `<span class="player-badge-pending">Ausstehend</span>`);
|
||||||
|
body.innerHTML = `
|
||||||
|
<div class="pending-info">
|
||||||
|
<div class="pending-name">${inv.inviteeName}</div>
|
||||||
|
<div>Einladung wurde gesendet – warte auf Antwort…</div>
|
||||||
|
<button class="btn-cancel-invite" style="margin-top:1rem;" onclick="cancelEinladung(${id})">Einladung abbrechen</button>
|
||||||
|
</div>`;
|
||||||
|
} else if (inv.status === 'ACCEPTED_OWN' || inv.status === 'ACCEPTED_HOST') {
|
||||||
|
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||||
|
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||||
|
const modeLabel = inv.status === 'ACCEPTED_OWN' ? 'Eigenes Gerät' : 'Host-Gerät';
|
||||||
|
header.insertAdjacentHTML('afterbegin', `<span class="player-badge-accepted">✓ ${modeLabel}</span>`);
|
||||||
|
// Name readonly, Geschlecht gesperrt (kommt vom Profil der eingeladenen Person)
|
||||||
|
const nameField = `<input type="text" id="p${id}-name" value="${inv.inviteeName}" readonly style="background:transparent;cursor:default;color:var(--color-muted);">`;
|
||||||
|
body.innerHTML = buildPlayerBody(id, nameField, true);
|
||||||
|
// Defaults laden falls verfügbar
|
||||||
|
if (inv.defaults) {
|
||||||
|
restorePlayer(id, {
|
||||||
|
geschlecht: inv.defaults.geschlecht,
|
||||||
|
spieltMit: inv.defaults.spieltMit || [],
|
||||||
|
rollen: inv.defaults.rollen || [],
|
||||||
|
werkzeuge: inv.defaults.werkzeuge || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (inv.status === 'DECLINED' || inv.status === 'CANCELLED') {
|
||||||
|
// Slot wieder freigeben
|
||||||
|
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||||
|
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||||
|
if (headerInvBtn) headerInvBtn.style.display = '';
|
||||||
|
playerInvitations[id] = null;
|
||||||
|
body.innerHTML = buildPlayerBody(id, `<input type="text" id="p${id}-name" placeholder="Name" autocomplete="off">`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cancelEinladung(id) {
|
||||||
|
const inv = playerInvitations[id];
|
||||||
|
if (!inv) return;
|
||||||
|
await fetch(`/bdsm/einladung/${inv.einladungId}`, { method: 'DELETE' }).catch(() => {});
|
||||||
|
playerInvitations[id] = null;
|
||||||
|
// UI zurücksetzen
|
||||||
|
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||||
|
if (header) {
|
||||||
|
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||||
|
const invBtn = header.querySelector('.btn-invite');
|
||||||
|
if (invBtn) invBtn.style.display = '';
|
||||||
|
}
|
||||||
|
const body = document.getElementById(`p${id}-body`);
|
||||||
|
if (body) body.innerHTML = buildPlayerBody(id, `<input type="text" id="p${id}-name" placeholder="Name" autocomplete="off">`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Polling ──
|
||||||
|
function startPoll() {
|
||||||
|
if (pollIntervalId) return;
|
||||||
|
pollIntervalId = setInterval(pollEinladungen, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPoll() {
|
||||||
|
if (pollIntervalId) { clearInterval(pollIntervalId); pollIntervalId = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollEinladungen() {
|
||||||
|
const hasPending = Object.values(playerInvitations).some(inv => inv && inv.status === 'PENDING');
|
||||||
|
if (!hasPending) { stopPoll(); return; }
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/einladung?setupId=${setupId}`);
|
||||||
|
if (!res.ok) return;
|
||||||
|
const liste = await res.json();
|
||||||
|
for (const e of liste) {
|
||||||
|
const id = playerIds.find(pid => {
|
||||||
|
const inv = playerInvitations[pid];
|
||||||
|
return inv && inv.einladungId === e.einladungId;
|
||||||
|
});
|
||||||
|
if (!id) continue;
|
||||||
|
const inv = playerInvitations[id];
|
||||||
|
if (!inv || inv.status === e.status) continue;
|
||||||
|
inv.status = e.status;
|
||||||
|
if (e.status === 'ACCEPTED_OWN' || e.status === 'ACCEPTED_HOST') {
|
||||||
|
// Defaults der eingeladenen Person laden
|
||||||
|
try {
|
||||||
|
const dRes = await fetch(`/user/${inv.inviteeId}/bdsm-defaults`);
|
||||||
|
if (dRes.ok) inv.defaults = await dRes.json();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
renderPending(id);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
updateWeiterBtn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWeiterBtn() {
|
||||||
|
const hasPending = Object.values(playerInvitations).some(inv => inv && inv.status === 'PENDING');
|
||||||
|
document.getElementById('weiterBtn').disabled = hasPending;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Validation & Weiter ──
|
||||||
|
function weiter() {
|
||||||
|
hideMessage();
|
||||||
|
|
||||||
|
const hasPending = Object.values(playerInvitations).some(inv => inv && inv.status === 'PENDING');
|
||||||
|
if (hasPending) {
|
||||||
|
showMessage('Bitte warte, bis alle Einladungen beantwortet wurden.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let valid = true;
|
||||||
|
playerIds.forEach(id => setFieldError(`p${id}-partner-err`, false));
|
||||||
|
|
||||||
|
const mitspieler = playerIds.map(id => {
|
||||||
|
const inv = playerInvitations[id];
|
||||||
|
const name = document.getElementById(`p${id}-name`)?.value.trim() || '';
|
||||||
|
const geschlecht = getChecked(`p${id}-geschlecht`);
|
||||||
|
const spieltMit = getChecked(`p${id}-spieltmit`);
|
||||||
|
const rollen = getChecked(`p${id}-rollen`);
|
||||||
|
const werkzeuge = getChecked(`p${id}-werkzeuge`);
|
||||||
|
|
||||||
|
setFieldError(`p${id}-name-err`, !name);
|
||||||
|
setFieldError(`p${id}-geschlecht-err`, geschlecht.length === 0);
|
||||||
|
setFieldError(`p${id}-spieltmit-err`, spieltMit.length === 0);
|
||||||
|
setFieldError(`p${id}-rollen-err`, rollen.length === 0);
|
||||||
|
setFieldError(`p${id}-werkzeuge-err`, werkzeuge.length === 0);
|
||||||
|
|
||||||
|
if (!name || geschlecht.length === 0 || spieltMit.length === 0 || rollen.length === 0 || werkzeuge.length === 0) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
geschlecht: geschlecht[0] || null,
|
||||||
|
spieltMit, rollen, werkzeuge,
|
||||||
|
userId: inv ? inv.inviteeId : null,
|
||||||
|
eigenesGeraet: inv ? inv.status === 'ACCEPTED_OWN' : false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!valid) { showMessage('Bitte alle Felder für jeden Spieler ausfüllen.', 'error'); return; }
|
||||||
|
|
||||||
|
const allRoles = new Set(mitspieler.flatMap(p => p.rollen));
|
||||||
|
const missingRoles = Object.keys(ROLE_LABELS).filter(r => !allRoles.has(r));
|
||||||
|
if (missingRoles.length > 0) {
|
||||||
|
showMessage('Folgende Rollen müssen mindestens einmal vergeben sein: ' +
|
||||||
|
missingRoles.map(r => ROLE_LABELS[r]).join(', '), 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let partnerFehler = false;
|
||||||
|
mitspieler.forEach((player, i) => {
|
||||||
|
const andereGeschlechter = mitspieler.filter((_, j) => j !== i).map(p => p.geschlecht);
|
||||||
|
const hatPartner = player.spieltMit.some(g => andereGeschlechter.includes(g));
|
||||||
|
if (!hatPartner) { setFieldError(`p${playerIds[i]}-partner-err`, true); partnerFehler = true; }
|
||||||
|
});
|
||||||
|
if (partnerFehler) { showMessage('Mindestens ein Spieler hat keinen kompatiblen Mitspieler.', 'error'); return; }
|
||||||
|
|
||||||
|
const settings = JSON.parse(sessionStorage.getItem('bdsm-session-settings'));
|
||||||
|
sessionStorage.setItem('bdsm-session-setup', JSON.stringify({ settings, mitspieler }));
|
||||||
|
window.location.href = '/bdsmtasks.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(text, type) {
|
||||||
|
const el = document.getElementById('message');
|
||||||
|
el.textContent = text; el.className = `message ${type}`; el.style.display = 'block';
|
||||||
|
}
|
||||||
|
function hideMessage() { document.getElementById('message').style.display = 'none'; }
|
||||||
|
|
||||||
|
function restorePlayer(id, data) {
|
||||||
|
if (data.geschlecht) {
|
||||||
|
const radio = document.querySelector(`input[name="p${id}-geschlecht"][value="${data.geschlecht}"]`);
|
||||||
|
if (radio) { radio.checked = true; radio.closest('.check-item')?.classList.add('is-checked'); }
|
||||||
|
}
|
||||||
|
(data.spieltMit || []).forEach(val => {
|
||||||
|
const cb = document.querySelector(`input[name="p${id}-spieltmit"][value="${val}"]`);
|
||||||
|
if (cb) { cb.checked = true; cb.closest('.check-item')?.classList.add('is-checked'); }
|
||||||
|
});
|
||||||
|
(data.rollen || []).forEach(val => {
|
||||||
|
const cb = document.querySelector(`input[name="p${id}-rollen"][value="${val}"]`);
|
||||||
|
if (cb) { cb.checked = true; cb.closest('.check-item')?.classList.add('is-checked'); }
|
||||||
|
});
|
||||||
|
(data.werkzeuge || []).forEach(val => {
|
||||||
|
const cb = document.querySelector(`input[name="p${id}-werkzeuge"][value="${val}"]`);
|
||||||
|
if (cb) { cb.checked = true; cb.closest('.check-item')?.classList.add('is-checked'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Init ──
|
||||||
|
const savedSetup = sessionStorage.getItem('bdsm-session-setup');
|
||||||
|
if (savedSetup) {
|
||||||
|
const { mitspieler } = JSON.parse(savedSetup);
|
||||||
|
mitspieler.forEach((p, i) => {
|
||||||
|
const id = addPlayer(p.name, i === 0);
|
||||||
|
restorePlayer(id, p);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Promise.all([
|
||||||
|
fetch('/login/me').then(r => r.ok ? r.json() : null).catch(() => null),
|
||||||
|
fetch('/user/me/bdsm-defaults').then(r => r.ok ? r.json() : {}).catch(() => ({}))
|
||||||
|
]).then(([user, defaults]) => {
|
||||||
|
myUserId = user?.userId || null;
|
||||||
|
addPlayer(user ? user.name : '', true);
|
||||||
|
addPlayer();
|
||||||
|
const selfId = playerIds[0];
|
||||||
|
restorePlayer(selfId, {
|
||||||
|
geschlecht: user?.geschlecht || null,
|
||||||
|
spieltMit: defaults.spieltMit || [],
|
||||||
|
rollen: defaults.rollen || [],
|
||||||
|
werkzeuge: defaults.werkzeuge || [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>BDSM Game – Aufgaben-Gruppen – XXX The Game</title>
|
<title>BDSM Game – Aufgaben-Gruppen – XXX The Game</title>
|
||||||
<link rel="stylesheet" href="/css/variables.css">
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<style>
|
<style>
|
||||||
.session-setup { max-width: 700px; }
|
.session-setup { }
|
||||||
|
|
||||||
.setup-section { margin-bottom: 2.5rem; }
|
.setup-section { margin-bottom: 2.5rem; }
|
||||||
.setup-section h2 {
|
.setup-section h2 {
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
<div style="position:relative; margin-top:2rem;">
|
<div style="position:relative; margin-top:2rem;">
|
||||||
<div class="message" id="message" style="position:absolute; bottom:calc(100% + 0.5rem); left:0; right:0; margin:0;"></div>
|
<div class="message" id="message" style="position:absolute; bottom:calc(100% + 0.5rem); left:0; right:0; margin:0;"></div>
|
||||||
<div style="display:flex; gap:1rem;">
|
<div style="display:flex; gap:1rem;">
|
||||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/sessionbdsmplayers.html'">← Zurück</button>
|
<button style="flex:1;" class="secondary" onclick="window.location.href='/bdsmplayers.html'">← Zurück</button>
|
||||||
<button style="flex:2;" onclick="weiter()">Weiter</button>
|
<button style="flex:2;" onclick="weiter()">Weiter</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
<script src="/js/sidebar.js"></script>
|
<script src="/js/sidebar.js"></script>
|
||||||
<script>
|
<script>
|
||||||
if (!sessionStorage.getItem('bdsm-session-setup')) {
|
if (!sessionStorage.getItem('bdsm-session-setup')) {
|
||||||
window.location.replace('/sessionbdsm.html');
|
window.location.replace('/bdsm.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedGruppen = new Set(JSON.parse(sessionStorage.getItem('bdsm-session-gruppen') || '[]'));
|
const savedGruppen = new Set(JSON.parse(sessionStorage.getItem('bdsm-session-gruppen') || '[]'));
|
||||||
@@ -308,18 +308,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
sessionStorage.setItem('bdsm-session-gruppen', JSON.stringify(selected));
|
sessionStorage.setItem('bdsm-session-gruppen', JSON.stringify(selected));
|
||||||
window.location.href = '/sessionbdsmtoys.html';
|
window.location.href = '/bdsmtoys.html';
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
fetch('/gruppe/list/user?page=0&size=500').then(r => r.ok ? r.json() : { content: [] }),
|
fetch('/gruppe/list/user?page=0&size=500').then(r => r.ok ? r.json() : (console.warn('[bdsmtasks] user HTTP', r.status), { content: [] })),
|
||||||
fetch('/abo/list?page=0&size=500').then(r => r.ok ? r.json() : { content: [] }),
|
fetch('/abo/list?page=0&size=500').then(r => r.ok ? r.json() : (console.warn('[bdsmtasks] abo HTTP', r.status), { content: [] })),
|
||||||
fetch('/gruppe/list/system?page=0&size=500').then(r => r.ok ? r.json() : { content: [] }),
|
fetch('/gruppe/list/system?page=0&size=500').then(r => r.ok ? r.json() : (console.warn('[bdsmtasks] system HTTP', r.status), { content: [] })),
|
||||||
]).then(([own, abo, system]) => {
|
]).then(([own, abo, system]) => {
|
||||||
|
console.log('[bdsmtasks] own:', own, 'abo:', abo, 'system:', system);
|
||||||
renderList('listOwn', own.content || []);
|
renderList('listOwn', own.content || []);
|
||||||
renderList('listSubscribed', abo.content || []);
|
renderList('listSubscribed', abo.content || []);
|
||||||
renderList('listSystem', system.content || []);
|
renderList('listSystem', system.content || []);
|
||||||
});
|
}).catch(err => console.error('[bdsmtasks] Fehler beim Laden:', err));
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>BDSM Game – Toys – XXX The Game</title>
|
<title>BDSM Game – Toys – XXX The Game</title>
|
||||||
<link rel="stylesheet" href="/css/variables.css">
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<style>
|
<style>
|
||||||
.session-setup { max-width: 700px; }
|
.session-setup { }
|
||||||
|
|
||||||
.setup-section { margin-bottom: 2.5rem; }
|
.setup-section { margin-bottom: 2.5rem; }
|
||||||
.setup-section h2 {
|
.setup-section h2 {
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
<div style="position:relative; margin-top:2rem;">
|
<div style="position:relative; margin-top:2rem;">
|
||||||
<div class="message" id="message" style="position:absolute; bottom:calc(100% + 0.5rem); left:0; right:0; margin:0;"></div>
|
<div class="message" id="message" style="position:absolute; bottom:calc(100% + 0.5rem); left:0; right:0; margin:0;"></div>
|
||||||
<div style="display:flex; gap:1rem;">
|
<div style="display:flex; gap:1rem;">
|
||||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/sessionbdsmtasks.html'">← Zurück</button>
|
<button style="flex:1;" class="secondary" onclick="window.location.href='/bdsmtasks.html'">← Zurück</button>
|
||||||
<button style="flex:2;" onclick="spielStarten()">Spiel starten</button>
|
<button style="flex:2;" onclick="spielStarten()">Spiel starten</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<script src="/js/sidebar.js"></script>
|
<script src="/js/sidebar.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const savedGruppen = JSON.parse(sessionStorage.getItem('bdsm-session-gruppen') || 'null');
|
const savedGruppen = JSON.parse(sessionStorage.getItem('bdsm-session-gruppen') || 'null');
|
||||||
if (!savedGruppen) window.location.replace('/sessionbdsm.html');
|
if (!savedGruppen) window.location.replace('/bdsm.html');
|
||||||
|
|
||||||
// Previously saved toy selection (when navigating back from game page)
|
// Previously saved toy selection (when navigating back from game page)
|
||||||
const savedToysRaw = sessionStorage.getItem('bdsm-session-toys');
|
const savedToysRaw = sessionStorage.getItem('bdsm-session-toys');
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Session anlegen
|
// 1. Session anlegen
|
||||||
const sessionRes = await fetch('/session', {
|
const sessionRes = await fetch('/bdsm', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -249,6 +249,7 @@
|
|||||||
wahrscheinlichkeitSperre: settings.wahrscheinlichkeitSperre,
|
wahrscheinlichkeitSperre: settings.wahrscheinlichkeitSperre,
|
||||||
aufgabenProLevel: settings.aufgabenProLevel,
|
aufgabenProLevel: settings.aufgabenProLevel,
|
||||||
zeitfaktorZeitstrafen: settings.zeitfaktorZeitstrafen,
|
zeitfaktorZeitstrafen: settings.zeitfaktorZeitstrafen,
|
||||||
|
setupId: sessionStorage.getItem('bdsm-setup-id'),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (!sessionRes.ok) throw new Error('Session konnte nicht angelegt werden.');
|
if (!sessionRes.ok) throw new Error('Session konnte nicht angelegt werden.');
|
||||||
@@ -258,7 +259,7 @@
|
|||||||
// 2. Mitspieler hinzufügen
|
// 2. Mitspieler hinzufügen
|
||||||
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup'));
|
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup'));
|
||||||
for (const p of setup.mitspieler) {
|
for (const p of setup.mitspieler) {
|
||||||
const res = await fetch(`/session/${sessionId}/mitspieler`, {
|
const res = await fetch(`/bdsm/${sessionId}/mitspieler`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -267,13 +268,15 @@
|
|||||||
spieltMit: p.spieltMit,
|
spieltMit: p.spieltMit,
|
||||||
rollen: p.rollen,
|
rollen: p.rollen,
|
||||||
verfuegbareWerkzeuge: p.werkzeuge,
|
verfuegbareWerkzeuge: p.werkzeuge,
|
||||||
|
userId: p.userId || null,
|
||||||
|
eigenesGeraet: p.eigenesGeraet || false,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error(`Mitspieler "${p.name}" konnte nicht hinzugefügt werden.`);
|
if (!res.ok) throw new Error(`Mitspieler "${p.name}" konnte nicht hinzugefügt werden.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Aufgaben setzen
|
// 3. Aufgaben setzen
|
||||||
const aufgabenRes = await fetch(`/session/${sessionId}/aufgaben`, {
|
const aufgabenRes = await fetch(`/bdsm/${sessionId}/aufgaben`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(gameContent),
|
body: JSON.stringify(gameContent),
|
||||||
@@ -281,7 +284,7 @@
|
|||||||
if (!aufgabenRes.ok) throw new Error('Aufgaben konnten nicht gespeichert werden.');
|
if (!aufgabenRes.ok) throw new Error('Aufgaben konnten nicht gespeichert werden.');
|
||||||
|
|
||||||
sessionStorage.setItem('bdsm-session-id', sessionId);
|
sessionStorage.setItem('bdsm-session-id', sessionId);
|
||||||
window.location.href = '/sessionbdsmingame.html';
|
window.location.href = '/bdsmingame.html';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showMessage(e.message, 'error');
|
showMessage(e.message, 'error');
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
92
xxxthegame/src/main/resources/static/bdsmwarten.html
Normal file
92
xxxthegame/src/main/resources/static/bdsmwarten.html
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>BDSM Game – Warten – XXX The Game</title>
|
||||||
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
<style>
|
||||||
|
.wait-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
}
|
||||||
|
.wait-icon { font-size: 3rem; margin-bottom: 1.5rem; animation: pulse 2s ease-in-out infinite; }
|
||||||
|
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.4; } }
|
||||||
|
.wait-title { font-size: 1.3rem; font-weight: 700; margin-bottom: 0.75rem; }
|
||||||
|
.wait-sub { font-size: 0.9rem; color: var(--color-muted); line-height: 1.6; margin-bottom: 2rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="app">
|
||||||
|
<div class="main">
|
||||||
|
<div class="content wait-card">
|
||||||
|
<div class="wait-icon">⏳</div>
|
||||||
|
<div class="wait-title">Warte auf Spielstart…</div>
|
||||||
|
<div class="wait-sub" id="sub">Der Host startet das Spiel in Kürze. Diese Seite aktualisiert sich automatisch.</div>
|
||||||
|
<div class="message" id="message" style="display:none;"></div>
|
||||||
|
<button class="secondary" onclick="abbrechen()">Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/js/sidebar.js"></script>
|
||||||
|
<script>
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const einladungId = params.get('id');
|
||||||
|
if (!einladungId) window.location.replace('/userhome.html');
|
||||||
|
|
||||||
|
let pollInterval = null;
|
||||||
|
|
||||||
|
async function pruefen() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/bdsm/einladung/${einladungId}`);
|
||||||
|
if (!res.ok) return;
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.status === 'CANCELLED') {
|
||||||
|
stopPoll();
|
||||||
|
zeigeFehler('Die Einladung wurde abgebrochen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.sessionId) {
|
||||||
|
stopPoll();
|
||||||
|
// Meinen Mitspieler laden
|
||||||
|
try {
|
||||||
|
const mRes = await fetch(`/bdsm/${data.sessionId}/mitspieler/me`);
|
||||||
|
if (mRes.ok) {
|
||||||
|
const mData = await mRes.json();
|
||||||
|
sessionStorage.setItem('bdsm-guest-mitspieler-id', mData.mitspielerId);
|
||||||
|
sessionStorage.setItem('bdsm-guest-name', mData.name);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
sessionStorage.setItem('bdsm-session-id', data.sessionId);
|
||||||
|
sessionStorage.setItem('bdsm-is-guest', 'true');
|
||||||
|
window.location.replace('/bdsmingame.html');
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPoll() {
|
||||||
|
if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeigeFehler(text) {
|
||||||
|
document.getElementById('sub').style.display = 'none';
|
||||||
|
const el = document.getElementById('message');
|
||||||
|
el.textContent = text;
|
||||||
|
el.className = 'message error';
|
||||||
|
el.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function abbrechen() {
|
||||||
|
stopPoll();
|
||||||
|
await fetch(`/bdsm/einladung/${einladungId}`, { method: 'DELETE' }).catch(() => {});
|
||||||
|
window.location.href = '/userhome.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sofort prüfen, dann alle 3 Sekunden
|
||||||
|
pruefen();
|
||||||
|
pollInterval = setInterval(pruefen, 3000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -406,11 +406,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tabs: Feed | Pinnwand | Lock-Historie -->
|
<!-- Tabs: Feed | Pinnwand | Spielhistorie -->
|
||||||
<div class="profil-tabs" style="margin-top:1.25rem;">
|
<div class="profil-tabs" style="margin-top:1.25rem;">
|
||||||
<button class="profil-tab-btn active" id="tabBtnPosts" onclick="switchProfilTab('posts', this)">Feed</button>
|
<button class="profil-tab-btn active" id="tabBtnPosts" onclick="switchProfilTab('posts', this)">Feed</button>
|
||||||
<button class="profil-tab-btn" id="tabBtnPinnwand" onclick="switchProfilTab('pinnwand', this)">Pinnwand</button>
|
<button class="profil-tab-btn" id="tabBtnPinnwand" onclick="switchProfilTab('pinnwand', this)">Pinnwand</button>
|
||||||
<button class="profil-tab-btn" id="tabBtnLockHistory" onclick="switchProfilTab('lockhistory', this); loadLockHistory()">Lock-Historie</button>
|
<button class="profil-tab-btn" id="tabBtnGameHistory" onclick="switchProfilTab('gamehistory', this); loadGameHistory()">Spielhistorie</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Feed Tab (vorausgewählt) -->
|
<!-- Feed Tab (vorausgewählt) -->
|
||||||
@@ -429,10 +429,10 @@
|
|||||||
<div id="pinnwandList"></div>
|
<div id="pinnwandList"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Lock-Historie Tab -->
|
<!-- Spielhistorie Tab -->
|
||||||
<div class="profil-tab-panel" id="tab-lockhistory">
|
<div class="profil-tab-panel" id="tab-gamehistory">
|
||||||
<div id="lockHistoryList" style="margin-top:0.75rem;"></div>
|
<div id="gameHistoryList" style="margin-top:0.75rem;"></div>
|
||||||
<p id="lockHistoryEmpty" style="color:var(--color-muted);font-size:0.9rem;display:none;">Keine abgeschlossenen Locks vorhanden.</p>
|
<p id="gameHistoryEmpty" style="color:var(--color-muted);font-size:0.9rem;display:none;">Keine abgeschlossenen Locks vorhanden.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -467,6 +467,7 @@
|
|||||||
// ── State ──
|
// ── State ──
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
let targetUserId = params.get('userId');
|
let targetUserId = params.get('userId');
|
||||||
|
const previewMode = params.get('preview'); // 'FREUND' | 'UNBEKANNT' | null
|
||||||
let myUserId = null;
|
let myUserId = null;
|
||||||
let isOwnProfile = false;
|
let isOwnProfile = false;
|
||||||
let profileData = null;
|
let profileData = null;
|
||||||
@@ -524,25 +525,52 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
myUserId = me ? me.userId : null;
|
myUserId = me ? me.userId : null;
|
||||||
isOwnProfile = me && me.userId === profile.userId;
|
isOwnProfile = !previewMode && me && me.userId === profile.userId;
|
||||||
profileData = profile;
|
profileData = profile;
|
||||||
allImages = images;
|
allImages = images;
|
||||||
|
|
||||||
|
// ── Preview-Modus: friendStatus simulieren ──
|
||||||
|
if (previewMode) {
|
||||||
|
profile.friendStatus = (previewMode === 'FREUND') ? 'FRIEND' : 'NONE';
|
||||||
|
showPreviewBanner(previewMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFriend = profile.friendStatus === 'FRIEND';
|
||||||
|
|
||||||
document.title = profile.name + ' – XXX The Game';
|
document.title = profile.name + ' – XXX The Game';
|
||||||
renderHeader(profile);
|
renderHeader(profile);
|
||||||
renderGallery();
|
|
||||||
loadFriends();
|
// ── Galerie ──
|
||||||
if (profile.beschreibung) {
|
if (canSee(profile.sichtbarkeitGalerie, isFriend, isOwnProfile)) {
|
||||||
|
renderGallery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Freunde ──
|
||||||
|
if (canSee(profile.sichtbarkeitFreunde, isFriend, isOwnProfile)) {
|
||||||
|
loadFriends();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.beschreibung && canSee(profile.sichtbarkeitGrunddaten, isFriend, isOwnProfile)) {
|
||||||
document.getElementById('beschreibungLabel').style.display = '';
|
document.getElementById('beschreibungLabel').style.display = '';
|
||||||
const el = document.getElementById('profilBeschreibung');
|
const el = document.getElementById('profilBeschreibung');
|
||||||
el.style.display = '';
|
el.style.display = '';
|
||||||
el.textContent = profile.beschreibung;
|
el.textContent = profile.beschreibung;
|
||||||
}
|
}
|
||||||
await loadPinnwand();
|
|
||||||
|
// ── Tabs: Feed, Pinnwand, Spielhistorie ──
|
||||||
|
applyTabPrivacy(profile, isFriend);
|
||||||
|
|
||||||
|
if (canSee(profile.sichtbarkeitPinnwand, isFriend, isOwnProfile)) {
|
||||||
|
await loadPinnwand();
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('profileView').style.display = '';
|
document.getElementById('profileView').style.display = '';
|
||||||
// Feed-Tab ist vorausgewählt → sofort laden
|
|
||||||
loadProfilPosts();
|
// Feed-Tab ist vorausgewählt → sofort laden (nur wenn sichtbar)
|
||||||
profilPostsObserver.observe(document.getElementById('profilPostsSentinel'));
|
if (canSee(profile.sichtbarkeitFeed, isFriend, isOwnProfile)) {
|
||||||
|
loadProfilPosts();
|
||||||
|
profilPostsObserver.observe(document.getElementById('profilPostsSentinel'));
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
document.getElementById('loadingHint').textContent = 'Fehler beim Laden.';
|
document.getElementById('loadingHint').textContent = 'Fehler beim Laden.';
|
||||||
document.getElementById('loadingHint').style.display = '';
|
document.getElementById('loadingHint').style.display = '';
|
||||||
@@ -568,14 +596,16 @@
|
|||||||
d.innerHTML = `<span class="label">${label}</span><span class="value">${esc(value)}</span>`;
|
d.innerHTML = `<span class="label">${label}</span><span class="value">${esc(value)}</span>`;
|
||||||
tags.appendChild(d);
|
tags.appendChild(d);
|
||||||
};
|
};
|
||||||
if (profile.alter) addTag('Alter', profile.alter + ' J.');
|
const grunddatenVisible = canSee(profile.sichtbarkeitGrunddaten, profile.friendStatus === 'FRIEND', isOwnProfile);
|
||||||
if (profile.groesse) addTag('Größe', profile.groesse + ' cm');
|
if (grunddatenVisible && profile.alter) addTag('Alter', profile.alter + ' J.');
|
||||||
if (profile.gewicht) addTag('Gewicht', profile.gewicht + ' kg');
|
if (grunddatenVisible && profile.groesse) addTag('Größe', profile.groesse + ' cm');
|
||||||
if (profile.geschlecht) addTag('Geschlecht', GESCHLECHT_LABEL[profile.geschlecht] || profile.geschlecht);
|
if (grunddatenVisible && profile.gewicht) addTag('Gewicht', profile.gewicht + ' kg');
|
||||||
if (profile.neigung) addTag('Neigung', NEIGUNG_LABEL[profile.neigung] || profile.neigung);
|
if (grunddatenVisible && profile.geschlecht) addTag('Geschlecht', GESCHLECHT_LABEL[profile.geschlecht] || profile.geschlecht);
|
||||||
if (profile.beziehungsstatus) addTag('Beziehung', BEZIEHUNG_LABEL[profile.beziehungsstatus] || profile.beziehungsstatus);
|
if (grunddatenVisible && profile.neigung) addTag('Neigung', NEIGUNG_LABEL[profile.neigung] || profile.neigung);
|
||||||
if (profile.lockeeXp > 0) addTag('🔒 Lockee XP', profile.lockeeXp + ' XP');
|
if (grunddatenVisible && profile.beziehungsstatus) addTag('Beziehung', BEZIEHUNG_LABEL[profile.beziehungsstatus] || profile.beziehungsstatus);
|
||||||
if (profile.keyholderXp > 0) addTag('🔑 Keyholder XP', profile.keyholderXp + ' XP');
|
const xpVisible = canSee(profile.sichtbarkeitXp, profile.friendStatus === 'FRIEND', isOwnProfile);
|
||||||
|
if (xpVisible && profile.lockeeXp > 0) addTag('🔒 Lockee XP', profile.lockeeXp + ' XP');
|
||||||
|
if (xpVisible && profile.keyholderXp > 0) addTag('🔑 Keyholder XP', profile.keyholderXp + ' XP');
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons
|
||||||
const actions = document.getElementById('profileActions');
|
const actions = document.getElementById('profileActions');
|
||||||
@@ -596,6 +626,50 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Privacy helpers ──
|
||||||
|
function canSee(sichtbarkeit, isFriend, isOwn) {
|
||||||
|
if (isOwn || !sichtbarkeit) return true;
|
||||||
|
if (sichtbarkeit === 'ALLE') return true;
|
||||||
|
if (sichtbarkeit === 'NUR_FREUNDE') return isFriend;
|
||||||
|
// NUR_ICH
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTabPrivacy(profile, isFriend) {
|
||||||
|
const showFeed = canSee(profile.sichtbarkeitFeed, isFriend, isOwnProfile);
|
||||||
|
const showPinnwand = canSee(profile.sichtbarkeitPinnwand, isFriend, isOwnProfile);
|
||||||
|
const showHistory = canSee(profile.sichtbarkeitLockhistorie, isFriend, isOwnProfile);
|
||||||
|
|
||||||
|
const btnFeed = document.getElementById('tabBtnPosts');
|
||||||
|
const btnPinnwand = document.getElementById('tabBtnPinnwand');
|
||||||
|
const btnHistory = document.getElementById('tabBtnGameHistory');
|
||||||
|
|
||||||
|
if (!showFeed) { btnFeed.style.display = 'none'; document.getElementById('tab-posts').classList.remove('active'); }
|
||||||
|
if (!showPinnwand) { btnPinnwand.style.display = 'none'; }
|
||||||
|
if (!showHistory) { btnHistory.style.display = 'none'; }
|
||||||
|
|
||||||
|
// Ersten sichtbaren Tab aktivieren
|
||||||
|
if (!showFeed) {
|
||||||
|
btnFeed.classList.remove('active');
|
||||||
|
document.getElementById('tab-posts').classList.remove('active');
|
||||||
|
if (showPinnwand) {
|
||||||
|
btnPinnwand.classList.add('active');
|
||||||
|
document.getElementById('tab-pinnwand').classList.add('active');
|
||||||
|
} else if (showHistory) {
|
||||||
|
btnHistory.classList.add('active');
|
||||||
|
document.getElementById('tab-gamehistory').classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPreviewBanner(mode) {
|
||||||
|
const banner = document.createElement('div');
|
||||||
|
banner.style.cssText = 'background:var(--color-secondary);border:1px solid var(--color-primary);border-radius:8px;padding:0.65rem 1rem;margin-bottom:1rem;font-size:0.88rem;display:flex;align-items:center;justify-content:space-between;gap:0.75rem;';
|
||||||
|
const label = mode === 'FREUND' ? '👥 Vorschau aus Freundessicht' : '👤 Vorschau aus Sicht einer fremden Person';
|
||||||
|
banner.innerHTML = `<span>${label}</span><a href="/einstellungen.html" style="font-size:0.82rem;color:var(--color-primary);">← Einstellungen</a>`;
|
||||||
|
document.getElementById('profileView').prepend(banner);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Tab switching ──
|
// ── Tab switching ──
|
||||||
function switchProfilTab(name, btn) {
|
function switchProfilTab(name, btn) {
|
||||||
document.querySelectorAll('.profil-tab-btn').forEach(b => b.classList.remove('active'));
|
document.querySelectorAll('.profil-tab-btn').forEach(b => b.classList.remove('active'));
|
||||||
@@ -777,47 +851,60 @@
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Lock-Historie ──
|
// ── Spielhistorie ──
|
||||||
let lockHistoryLoaded = false;
|
const GAME_TYPE_ICON = {
|
||||||
async function loadLockHistory() {
|
CARDLOCK: '<span style="position:relative;display:inline-block;line-height:1;"><img src="/img/card.png" style="width:2.7rem;height:2.7rem;object-fit:contain;display:block;"><span style="position:absolute;bottom:-2px;right:-4px;font-size:1.3rem;line-height:1;">🔒</span></span>',
|
||||||
if (lockHistoryLoaded) return;
|
TIMELOCK: '<span style="position:relative;display:inline-block;line-height:1;"><span style="font-size:2.7rem;">⏰</span><span style="position:absolute;bottom:-2px;right:-4px;font-size:1.3rem;line-height:1;">🔒</span></span>',
|
||||||
lockHistoryLoaded = true;
|
BDSM: '⛓️',
|
||||||
const list = document.getElementById('lockHistoryList');
|
VANILLA: '❤️'
|
||||||
const empty = document.getElementById('lockHistoryEmpty');
|
};
|
||||||
|
const ROLE_BADGE = { KEYHOLDER: '🔑', LOCKEE: '🔒', PLAYER: '' };
|
||||||
|
|
||||||
|
let gameHistoryLoaded = false;
|
||||||
|
async function loadGameHistory() {
|
||||||
|
if (gameHistoryLoaded) return;
|
||||||
|
gameHistoryLoaded = true;
|
||||||
|
const list = document.getElementById('gameHistoryList');
|
||||||
|
const empty = document.getElementById('gameHistoryEmpty');
|
||||||
list.innerHTML = '<p style="color:var(--color-muted);font-size:0.85rem;">Lädt…</p>';
|
list.innerHTML = '<p style="color:var(--color-muted);font-size:0.85rem;">Lädt…</p>';
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/lockhistory?userId=' + targetUserId);
|
const res = await fetch('/gamehistory?userId=' + targetUserId);
|
||||||
if (!res.ok) { list.innerHTML = ''; return; }
|
if (!res.ok) { list.innerHTML = ''; return; }
|
||||||
const entries = await res.json();
|
const entries = await res.json();
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
if (entries.length === 0) { empty.style.display = ''; return; }
|
if (entries.length === 0) { empty.style.display = ''; return; }
|
||||||
|
|
||||||
list.innerHTML = entries.map(e => {
|
list.innerHTML = entries.map(e => {
|
||||||
const icon = e.role === 'KEYHOLDER' ? '🔑' : '🔒';
|
const gameIconRaw = GAME_TYPE_ICON[e.gameType] || '🎮';
|
||||||
const partner = e.role === 'KEYHOLDER'
|
const gameIcon = (e.gameType === 'CARDLOCK' || e.gameType === 'TIMELOCK')
|
||||||
? (e.lockeeName ? `<span style="color:var(--color-muted);font-size:0.78rem;">Lockee: ${esc(e.lockeeName)}</span>` : '')
|
? gameIconRaw
|
||||||
: (e.keyholderName ? `<span style="color:var(--color-muted);font-size:0.78rem;">Keyholder: ${esc(e.keyholderName)}</span>` : '<span style="color:var(--color-muted);font-size:0.78rem;">Self-Lock</span>');
|
: `<span style="font-size:2.7rem;line-height:1;">${gameIconRaw}</span>`;
|
||||||
const days = Math.floor(e.durationMinutes / 1440);
|
|
||||||
const hours = Math.floor((e.durationMinutes % 1440) / 60);
|
const days = Math.floor(e.durationMinutes / 1440);
|
||||||
const mins = e.durationMinutes % 60;
|
const hours = Math.floor((e.durationMinutes % 1440) / 60);
|
||||||
const dur = days > 0
|
const mins = e.durationMinutes % 60;
|
||||||
|
const dur = days > 0
|
||||||
? `${days}d ${hours}h ${mins}min`
|
? `${days}d ${hours}h ${mins}min`
|
||||||
: hours > 0 ? `${hours}h ${mins}min` : `${mins}min`;
|
: hours > 0 ? `${hours}h ${mins}min` : `${mins}min`;
|
||||||
const avatar = e.partnerPic
|
|
||||||
? `<img src="data:image/jpeg;base64,${e.partnerPic}" style="width:40px;height:40px;border-radius:50%;object-fit:cover;display:block;">`
|
const participants = (e.participants || []).map(p => {
|
||||||
: `<div style="width:40px;height:40px;border-radius:50%;background:var(--color-secondary);display:flex;align-items:center;justify-content:center;font-size:1.1rem;">👤</div>`;
|
const badge = ROLE_BADGE[p.role] || '';
|
||||||
return `<div style="display:flex;align-items:center;gap:0.75rem;padding:0.65rem 0;border-bottom:1px solid var(--color-secondary);">
|
const img = p.picture
|
||||||
<div style="position:relative;width:40px;height:40px;flex-shrink:0;">
|
? `<img src="data:image/png;base64,${p.picture}" style="width:40px;height:40px;border-radius:50%;object-fit:cover;display:block;">`
|
||||||
${avatar}
|
: `<div style="width:40px;height:40px;border-radius:50%;background:var(--color-secondary);display:flex;align-items:center;justify-content:center;font-size:1.1rem;flex-shrink:0;">👤</div>`;
|
||||||
<span style="position:absolute;top:-3px;left:-3px;font-size:1.4rem;line-height:1;">${icon}</span>
|
return `<a href="/benutzer.html?userId=${esc(p.userId)}" style="position:relative;flex-shrink:0;text-decoration:none;" title="${esc(p.name || '')} (${p.role})">
|
||||||
</div>
|
${img}
|
||||||
|
${badge ? `<span style="position:absolute;top:-4px;right:-4px;font-size:0.9rem;line-height:1;">${badge}</span>` : ''}
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
return `<div style="display:flex;align-items:flex-start;gap:0.85rem;padding:0.75rem 0;border-bottom:1px solid var(--color-secondary);">
|
||||||
|
<div style="flex-shrink:0;width:3rem;text-align:center;">${gameIcon}</div>
|
||||||
<div style="flex:1;min-width:0;">
|
<div style="flex:1;min-width:0;">
|
||||||
<div style="font-weight:600;font-size:0.92rem;">${esc(e.lockName) || 'Unbenanntes Lock'}</div>
|
<div style="font-weight:600;font-size:0.92rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${esc(e.gameName) || 'Unbenannt'}</div>
|
||||||
<div style="display:flex;gap:1rem;flex-wrap:wrap;margin-top:0.15rem;">
|
<div style="font-size:0.78rem;color:var(--color-muted);margin-top:0.15rem;">⏱ ${dur} · ${new Date(e.endTime).toLocaleDateString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric'})}</div>
|
||||||
${partner}
|
|
||||||
<span style="color:var(--color-muted);font-size:0.78rem;">⏱ ${dur}</span>
|
|
||||||
<span style="color:var(--color-muted);font-size:0.78rem;">${fmtDate(e.unlockTime)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display:flex;gap:0.35rem;align-items:center;flex-shrink:0;">${participants}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
} catch(e) { list.innerHTML = ''; }
|
} catch(e) { list.innerHTML = ''; }
|
||||||
@@ -954,7 +1041,7 @@
|
|||||||
: '◉';
|
: '◉';
|
||||||
const bildRaw = bilderCarousel(p.bilder);
|
const bildRaw = bilderCarousel(p.bilder);
|
||||||
const bildHtml = bildRaw
|
const bildHtml = bildRaw
|
||||||
? `<div class="post-bild-wrap" data-post-id="${p.postId}">${bildRaw}<div class="post-bild-hover-icon">💬</div></div>`
|
? `<div class="post-bild-wrap" data-post-id="${p.postId}">${bildRaw}</div>`
|
||||||
: '';
|
: '';
|
||||||
const privacyLabel = p.isPublic ? '' : '<span style="font-size:0.72rem;color:var(--color-muted);margin-left:0.4rem;">🔒 privat</span>';
|
const privacyLabel = p.isPublic ? '' : '<span style="font-size:0.72rem;color:var(--color-muted);margin-left:0.4rem;">🔒 privat</span>';
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,33 @@
|
|||||||
}
|
}
|
||||||
.blind-hint-icon { font-size: 1.4rem; flex-shrink: 0; }
|
.blind-hint-icon { font-size: 1.4rem; flex-shrink: 0; }
|
||||||
|
|
||||||
|
/* Bestätigungs-Modal */
|
||||||
|
.confirm-modal-bg {
|
||||||
|
display: none; position: fixed; inset: 0; z-index: 600;
|
||||||
|
align-items: center; justify-content: center;
|
||||||
|
}
|
||||||
|
.confirm-modal-bg.open { display: flex; }
|
||||||
|
.confirm-modal-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.6); }
|
||||||
|
.confirm-modal-box {
|
||||||
|
position: relative; background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary); border-radius: 12px;
|
||||||
|
padding: 1.75rem 1.5rem 1.5rem; max-width: 380px; width: 92%; z-index: 1;
|
||||||
|
display: flex; flex-direction: column; gap: 1rem;
|
||||||
|
}
|
||||||
|
.confirm-modal-title {
|
||||||
|
font-weight: 700; font-size: 1rem; padding-right: 1.5rem;
|
||||||
|
}
|
||||||
|
.confirm-modal-text {
|
||||||
|
font-size: 0.9rem; color: var(--color-muted); line-height: 1.5;
|
||||||
|
}
|
||||||
|
.confirm-modal-actions {
|
||||||
|
display: flex; gap: 0.6rem; justify-content: flex-end; margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
.confirm-modal-actions button { width: auto; padding: 0.6rem 1.3rem; font-size: 0.9rem; }
|
||||||
|
.confirm-modal-cancel { background: var(--color-secondary) !important; color: var(--color-text) !important; }
|
||||||
|
.confirm-modal-ok { background: #c0392b !important; }
|
||||||
|
.confirm-modal-ok:hover { background: #a93226 !important; }
|
||||||
|
|
||||||
/* Entsperrcode-Modal */
|
/* Entsperrcode-Modal */
|
||||||
.unlock-modal-bg {
|
.unlock-modal-bg {
|
||||||
display: none; position: fixed; inset: 0; z-index: 500;
|
display: none; position: fixed; inset: 0; z-index: 500;
|
||||||
@@ -214,6 +241,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Bestätigungs-Modal -->
|
||||||
|
<div class="confirm-modal-bg" id="confirmModal">
|
||||||
|
<div class="confirm-modal-overlay" onclick="confirmCancel()"></div>
|
||||||
|
<div class="confirm-modal-box">
|
||||||
|
<button onclick="confirmCancel()" style="position:absolute;top:0.75rem;right:0.75rem;background:none;border:none;color:var(--color-muted);font-size:1.3rem;cursor:pointer;padding:0.2rem 0.5rem;line-height:1;" title="Schließen">✕</button>
|
||||||
|
<div class="confirm-modal-title" id="confirmTitle"></div>
|
||||||
|
<div class="confirm-modal-text" id="confirmText"></div>
|
||||||
|
<div class="confirm-modal-actions">
|
||||||
|
<button class="confirm-modal-cancel" onclick="confirmCancel()">Abbrechen</button>
|
||||||
|
<button class="confirm-modal-ok" id="confirmOkBtn">Bestätigen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Lockee-Einladungs-Dialog -->
|
<!-- Lockee-Einladungs-Dialog -->
|
||||||
<div class="lockee-dialog-bg" id="lockeeInviteDialog">
|
<div class="lockee-dialog-bg" id="lockeeInviteDialog">
|
||||||
<div class="lockee-dialog-overlay" onclick="closeLockeeInviteDialog()"></div>
|
<div class="lockee-dialog-overlay" onclick="closeLockeeInviteDialog()"></div>
|
||||||
@@ -475,9 +516,29 @@
|
|||||||
renderSentPage();
|
renderSentPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Bestätigungs-Modal ──
|
||||||
|
let _confirmResolve = null;
|
||||||
|
|
||||||
|
function showConfirm(title, text) {
|
||||||
|
document.getElementById('confirmTitle').textContent = title;
|
||||||
|
document.getElementById('confirmText').textContent = text;
|
||||||
|
document.getElementById('confirmModal').classList.add('open');
|
||||||
|
return new Promise(resolve => {
|
||||||
|
_confirmResolve = resolve;
|
||||||
|
document.getElementById('confirmOkBtn').onclick = () => { confirmClose(true); };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmCancel() { confirmClose(false); }
|
||||||
|
|
||||||
|
function confirmClose(result) {
|
||||||
|
document.getElementById('confirmModal').classList.remove('open');
|
||||||
|
if (_confirmResolve) { _confirmResolve(result); _confirmResolve = null; }
|
||||||
|
}
|
||||||
|
|
||||||
// ── Aktionen: Empfangen ──
|
// ── Aktionen: Empfangen ──
|
||||||
async function declineLockeeInvitation(token, btn) {
|
async function declineLockeeInvitation(token, btn) {
|
||||||
if (!confirm('Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return;
|
if (!await showConfirm('Einladung ablehnen', 'Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return;
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/lockee/invitation/' + encodeURIComponent(token), { method: 'DELETE' });
|
const res = await fetch('/lockee/invitation/' + encodeURIComponent(token), { method: 'DELETE' });
|
||||||
@@ -487,7 +548,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function declineKhInvitation(token, btn) {
|
async function declineKhInvitation(token, btn) {
|
||||||
if (!confirm('Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return;
|
if (!await showConfirm('Einladung ablehnen', 'Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return;
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/keyholder/invitations/mine/' + encodeURIComponent(token), { method: 'DELETE' });
|
const res = await fetch('/keyholder/invitations/mine/' + encodeURIComponent(token), { method: 'DELETE' });
|
||||||
@@ -498,10 +559,11 @@
|
|||||||
|
|
||||||
// ── Aktionen: Gesendet ──
|
// ── Aktionen: Gesendet ──
|
||||||
async function cancelSentInvitation(token, type, btn) {
|
async function cancelSentInvitation(token, type, btn) {
|
||||||
const msg = type === 'lockee'
|
const title = 'Einladung zurückziehen';
|
||||||
? 'Einladung zurückziehen? Das Lock wird gelöscht und der Lockee wird benachrichtigt.'
|
const text = type === 'lockee'
|
||||||
: 'Keyholder-Einladung zurückziehen? Der Keyholder wird benachrichtigt.';
|
? 'Das Lock wird gelöscht und der Lockee wird benachrichtigt.'
|
||||||
if (!confirm(msg)) return;
|
: 'Der Keyholder wird benachrichtigt.';
|
||||||
|
if (!await showConfirm(title, text)) return;
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
const url = type === 'lockee'
|
const url = type === 'lockee'
|
||||||
? '/lockee/invitations/sent/' + encodeURIComponent(token)
|
? '/lockee/invitations/sent/' + encodeURIComponent(token)
|
||||||
@@ -634,7 +696,7 @@
|
|||||||
|
|
||||||
async function declineLockeeInviteDialog() {
|
async function declineLockeeInviteDialog() {
|
||||||
if (!activeDialogToken) return;
|
if (!activeDialogToken) return;
|
||||||
if (!confirm('Bist du sicher, dass du diese Einladung ablehnen möchtest? Das Lock wird gelöscht.')) return;
|
if (!await showConfirm('Einladung ablehnen', 'Bist du sicher, dass du diese Einladung ablehnen möchtest? Das Lock wird gelöscht.')) return;
|
||||||
const declineBtn = document.querySelector('.btn-decline');
|
const declineBtn = document.querySelector('.btn-decline');
|
||||||
declineBtn.disabled = true;
|
declineBtn.disabled = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
968
xxxthegame/src/main/resources/static/einstellungen.html
Normal file
968
xxxthegame/src/main/resources/static/einstellungen.html
Normal file
@@ -0,0 +1,968 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Einstellungen – XXX The Game</title>
|
||||||
|
<link rel="stylesheet" href="/css/variables.css">
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
<style>
|
||||||
|
.settings-page h1 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Accordion ── */
|
||||||
|
.settings-section {
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-header:hover {
|
||||||
|
background: rgba(255,255,255,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-arrow {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section.open .settings-section-arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section-body {
|
||||||
|
display: none;
|
||||||
|
padding: 0 1.25rem 1.25rem;
|
||||||
|
border-top: 1px solid var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section.open .settings-section-body {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Einstellungs-Zeilen ── */
|
||||||
|
.settings-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.9rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-row-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-row-label {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-row-desc {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-row select {
|
||||||
|
width: auto;
|
||||||
|
min-width: 150px;
|
||||||
|
padding: 0.45rem 0.75rem;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Benachrichtigungen Grid ── */
|
||||||
|
.notif-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 5rem 5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notif-grid-row {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notif-grid-row > * {
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notif-grid-row.notif-header > * {
|
||||||
|
padding-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notif-col-check {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notif-col-check input[type="checkbox"] {
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
accent-color: var(--color-primary, #c0392b);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Speichern-Feedback (Toast) ── */
|
||||||
|
.save-toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1.5rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(2rem);
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
border-left: 3px solid var(--color-success, #4caf50);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.6rem 1.25rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
color: var(--color-success, #4caf50);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.2s, transform 0.2s;
|
||||||
|
z-index: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-toast.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Spiel-Einstellungen Check-Items ── */
|
||||||
|
.spiel-check-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.4rem;
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
}
|
||||||
|
.spiel-check-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
background: var(--color-secondary);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.35rem 0.65rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
user-select: none;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
.spiel-check-item.is-checked { border-color: var(--color-primary); }
|
||||||
|
.spiel-check-item input {
|
||||||
|
accent-color: var(--color-primary);
|
||||||
|
width: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.spiel-subsection {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
.spiel-subsection-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text);
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding-bottom: 0.4rem;
|
||||||
|
border-bottom: 1px solid var(--color-secondary);
|
||||||
|
}
|
||||||
|
.spiel-field { margin-bottom: 1rem; }
|
||||||
|
.spiel-field > .settings-row-label { margin-bottom: 0.2rem; }
|
||||||
|
|
||||||
|
/* ── Modal ── */
|
||||||
|
.modal-backdrop {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0,0,0,0.6);
|
||||||
|
z-index: 200;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-backdrop.visible { display: flex; }
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: var(--color-card);
|
||||||
|
border: 1px solid var(--color-secondary);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 2rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 360px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal h2 {
|
||||||
|
color: var(--color-primary);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions button { flex: 1; margin-top: 0; }
|
||||||
|
|
||||||
|
/* ── Separator ── */
|
||||||
|
.settings-separator {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--color-secondary);
|
||||||
|
margin: 1.25rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Vorschau-Buttons ── */
|
||||||
|
.preview-row {
|
||||||
|
padding-top: 1.25rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-row span {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
flex-basis: 100%;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-row button {
|
||||||
|
width: auto;
|
||||||
|
padding: 0.5rem 1.1rem;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="app">
|
||||||
|
<div class="main">
|
||||||
|
<div class="content">
|
||||||
|
<h1>⚙️ Einstellungen</h1>
|
||||||
|
|
||||||
|
<!-- Grunddaten -->
|
||||||
|
<div class="settings-section" id="sec-grunddaten">
|
||||||
|
<div class="settings-section-header" onclick="toggleSection('sec-grunddaten')">
|
||||||
|
<span class="settings-section-title">👤 Grunddaten</span>
|
||||||
|
<span class="settings-section-arrow">▸</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Nickname</div>
|
||||||
|
<div class="settings-row-desc" id="gd-name">…</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="openNameDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;">Ändern</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">E-Mail</div>
|
||||||
|
<div class="settings-row-desc" id="gd-email">…</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="openEmailDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;">Ändern</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Geburtsdatum</div>
|
||||||
|
<div class="settings-row-desc" id="gd-geburtsdatum">…</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="openGeburtsdatumDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;">Ändern</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label" style="color:var(--color-primary);">Konto löschen</div>
|
||||||
|
<div class="settings-row-desc">Alle Daten werden unwiderruflich gelöscht</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="openDeleteDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;background:transparent;border:1px solid var(--color-secondary);color:var(--color-muted);">Löschen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Spiel Einstellungen -->
|
||||||
|
<div class="settings-section" id="sec-spiel">
|
||||||
|
<div class="settings-section-header" onclick="toggleSection('sec-spiel')">
|
||||||
|
<span class="settings-section-title">🕹️ Spiel Einstellungen</span>
|
||||||
|
<span class="settings-section-arrow">▸</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
|
||||||
|
<!-- BDSM Game -->
|
||||||
|
<div class="spiel-subsection">
|
||||||
|
<div class="spiel-subsection-title">BDSM Game</div>
|
||||||
|
|
||||||
|
<div class="spiel-field">
|
||||||
|
<div class="settings-row-label">Spiele mit Geschlecht</div>
|
||||||
|
<div class="spiel-check-group" id="bdsm-spieltmit">
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="WEIBLICH"> Weiblich</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="DIVERS"> Divers</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="MAENNLICH"> Männlich</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spiel-field">
|
||||||
|
<div class="settings-row-label">Meine Rollen</div>
|
||||||
|
<div class="spiel-check-group" id="bdsm-rollen">
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="AUFGABE_AKTIV"> Aufgaben aktiv</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="AUFGABE_PASSIV"> Aufgaben passiv</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="BESTRAFUNG_AKTIV"> Bestrafungen aktiv</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="BESTRAFUNG_PASSIV"> Bestrafungen passiv</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="spiel-field">
|
||||||
|
<div class="settings-row-label">Was ich einsetze</div>
|
||||||
|
<div class="spiel-check-group" id="bdsm-werkzeuge">
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="MUND"> Mund</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="VAGINA"> Vagina</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="PENIS"> Penis</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="ANUS"> Anus</label>
|
||||||
|
<label class="spiel-check-item"><input type="checkbox" value="UMSCHNALLDILDO"> Umschnall-Dildo</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Benachrichtigungen -->
|
||||||
|
<div class="settings-section" id="sec-benachrichtigungen">
|
||||||
|
<div class="settings-section-header" onclick="toggleSection('sec-benachrichtigungen')">
|
||||||
|
<span class="settings-section-title">🔔 Benachrichtigungen</span>
|
||||||
|
<span class="settings-section-arrow">▸</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
<div class="notif-grid">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="notif-grid-row notif-header">
|
||||||
|
<div></div>
|
||||||
|
<div class="notif-col-check settings-row-label">In-App</div>
|
||||||
|
<div class="notif-col-check settings-row-label">E-Mail</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Einladungen -->
|
||||||
|
<div class="notif-grid-row">
|
||||||
|
<div>
|
||||||
|
<div class="settings-row-label">Einladungen</div>
|
||||||
|
<div class="settings-row-desc">Einladungen zu Locks und Spielen, Annahmen und Ablehnungen</div>
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-INVITATION-inApp" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-INVITATION-email" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Spielstatus -->
|
||||||
|
<div class="notif-grid-row">
|
||||||
|
<div>
|
||||||
|
<div class="settings-row-label">Spielstatus</div>
|
||||||
|
<div class="settings-row-desc">Karten, Aufgaben, Verifikationen, Einfrierungen und andere Spielereignisse</div>
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-GAME_STATE-inApp" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-GAME_STATE-email" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notfall -->
|
||||||
|
<div class="notif-grid-row">
|
||||||
|
<div>
|
||||||
|
<div class="settings-row-label">Notfall</div>
|
||||||
|
<div class="settings-row-desc">Notfall-Entsperrungen und dringende Meldungen</div>
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-EMERGENCY-inApp" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-EMERGENCY-email" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Freundschaftsanfragen -->
|
||||||
|
<div class="notif-grid-row">
|
||||||
|
<div>
|
||||||
|
<div class="settings-row-label">Freundschaftsanfragen</div>
|
||||||
|
<div class="settings-row-desc">Neue Freundschaftsanfragen von anderen Nutzern</div>
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-FRIENDREQUEST-inApp" checked disabled
|
||||||
|
title="In-App-Benachrichtigungen für Freundschaftsanfragen sind immer aktiv">
|
||||||
|
</div>
|
||||||
|
<div class="notif-col-check">
|
||||||
|
<input type="checkbox" id="notif-FRIENDREQUEST-email" onchange="saveNotifications()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Datenschutz -->
|
||||||
|
<div class="settings-section" id="sec-datenschutz">
|
||||||
|
<div class="settings-section-header" onclick="toggleSection('sec-datenschutz')">
|
||||||
|
<span class="settings-section-title">🛡️ Datenschutz</span>
|
||||||
|
<span class="settings-section-arrow">▸</span>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
|
||||||
|
<!-- Grunddaten -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Grunddaten</div>
|
||||||
|
<div class="settings-row-desc">Alter, Größe, Gewicht, Geschlecht, Neigung, Beziehungsstatus, Beschreibung</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-grunddaten" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Galerie -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Galerie</div>
|
||||||
|
<div class="settings-row-desc">Fotos auf dem Profil</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-galerie" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Freunde -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Freundesliste</div>
|
||||||
|
<div class="settings-row-desc">Wer kann sehen, wer deine Freunde sind</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-freunde" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Feed -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Feed / Posts</div>
|
||||||
|
<div class="settings-row-desc">Posts auf dem Profil-Tab</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-feed" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pinnwand -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Pinnwand</div>
|
||||||
|
<div class="settings-row-desc">Einträge auf der Pinnwand</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-pinnwand" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- XP -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">XP-Punkte</div>
|
||||||
|
<div class="settings-row-desc">Lockee-XP und Keyholder-XP</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-xp" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
<option value="NUR_ICH">Nur ich</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lock-Historie -->
|
||||||
|
<div class="settings-row">
|
||||||
|
<div class="settings-row-info">
|
||||||
|
<div class="settings-row-label">Lock-Historie</div>
|
||||||
|
<div class="settings-row-desc">Abgeschlossene Locks und Keyholder-Aktivitäten</div>
|
||||||
|
</div>
|
||||||
|
<select id="sv-lockhistorie" onchange="doSave()">
|
||||||
|
<option value="ALLE">Alle</option>
|
||||||
|
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||||
|
<option value="NUR_ICH">Nur ich</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="settings-separator">
|
||||||
|
|
||||||
|
<!-- Vorschau -->
|
||||||
|
<div class="preview-row">
|
||||||
|
<span>Profil-Vorschau – wie sieht mein Profil für andere aus?</span>
|
||||||
|
<button onclick="openPreview('FREUND')">👥 Vorschau als Freund</button>
|
||||||
|
<button onclick="openPreview('UNBEKANNT')" style="background:var(--color-secondary);color:var(--color-text);">👤 Vorschau als Fremder</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nickname Modal -->
|
||||||
|
<div class="modal-backdrop" id="nameModal">
|
||||||
|
<div class="modal">
|
||||||
|
<h2>Nickname ändern</h2>
|
||||||
|
<label for="newName">Neuer Nickname</label>
|
||||||
|
<input type="text" id="newName" placeholder="Neuer Name" autocomplete="off">
|
||||||
|
<div class="message" id="nameMessage"></div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="secondary" onclick="closeNameDialog()">Abbrechen</button>
|
||||||
|
<button id="nameConfirmBtn" onclick="saveName()">Speichern</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Geburtsdatum Modal -->
|
||||||
|
<div class="modal-backdrop" id="geburtsdatumModal">
|
||||||
|
<div class="modal">
|
||||||
|
<h2>Geburtsdatum ändern</h2>
|
||||||
|
<label for="newGeburtsdatum">Neues Geburtsdatum</label>
|
||||||
|
<input type="date" id="newGeburtsdatum" autocomplete="bday">
|
||||||
|
<div class="message" id="geburtsdatumMessage"></div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="secondary" onclick="closeGeburtsdatumDialog()">Abbrechen</button>
|
||||||
|
<button id="geburtsdatumConfirmBtn" onclick="saveGeburtsdatum()">Speichern</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- E-Mail Modal -->
|
||||||
|
<div class="modal-backdrop" id="emailModal">
|
||||||
|
<div class="modal">
|
||||||
|
<h2>E-Mail-Adresse ändern</h2>
|
||||||
|
<label for="newEmail">Neue E-Mail-Adresse</label>
|
||||||
|
<input type="email" id="newEmail" placeholder="neue@email.de" autocomplete="off">
|
||||||
|
<div class="message" id="emailMessage"></div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="secondary" onclick="closeEmailDialog()">Abbrechen</button>
|
||||||
|
<button id="emailConfirmBtn" onclick="requestEmailChange()">Bestätigungsmail senden</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Konto löschen Modal -->
|
||||||
|
<div class="modal-backdrop" id="deleteModal">
|
||||||
|
<div class="modal">
|
||||||
|
<h2>Konto löschen</h2>
|
||||||
|
<p style="color:var(--color-muted);font-size:0.9rem;margin-bottom:0.75rem;">
|
||||||
|
Dein Konto sowie alle gespeicherten Aufgaben und Toys werden unwiderruflich gelöscht.
|
||||||
|
</p>
|
||||||
|
<p style="color:var(--color-primary);font-size:0.85rem;">Dieser Vorgang kann nicht rückgängig gemacht werden.</p>
|
||||||
|
<div class="message" id="deleteMessage"></div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="secondary" onclick="closeDeleteDialog()">Abbrechen</button>
|
||||||
|
<button id="deleteConfirmBtn" onclick="deleteAccount()" style="background:#c0392b;">Konto löschen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="save-toast" id="saveToast">✓ Gespeichert</div>
|
||||||
|
|
||||||
|
<script src="/js/sidebar.js"></script>
|
||||||
|
<script src="/js/social-sidebar.js"></script>
|
||||||
|
<script>
|
||||||
|
let myUserId = null;
|
||||||
|
let toastTimer = null;
|
||||||
|
|
||||||
|
function toggleSection(id) {
|
||||||
|
const target = document.getElementById(id);
|
||||||
|
const isOpen = target.classList.contains('open');
|
||||||
|
document.querySelectorAll('.settings-section').forEach(s => s.classList.remove('open'));
|
||||||
|
if (!isOpen) target.classList.add('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Grunddaten laden ──
|
||||||
|
async function loadGrunddaten() {
|
||||||
|
const res = await fetch('/login/me');
|
||||||
|
if (res.status === 401) { window.location.href = '/login.html'; return; }
|
||||||
|
if (!res.ok) return;
|
||||||
|
const user = await res.json();
|
||||||
|
myUserId = user.userId;
|
||||||
|
document.getElementById('gd-name').textContent = user.name || '—';
|
||||||
|
document.getElementById('gd-email').textContent = user.email || '—';
|
||||||
|
document.getElementById('gd-geburtsdatum').textContent = formatGeburtsdatum(user.geburtsdatum);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatGeburtsdatum(iso) {
|
||||||
|
if (!iso) return '—';
|
||||||
|
const [y, m, d] = iso.split('-');
|
||||||
|
const today = new Date();
|
||||||
|
const birth = new Date(iso);
|
||||||
|
const age = today.getFullYear() - birth.getFullYear()
|
||||||
|
- (today < new Date(today.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0);
|
||||||
|
return `${d}.${m}.${y} (${age} Jahre)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Geburtsdatum Dialog ──
|
||||||
|
function openGeburtsdatumDialog() {
|
||||||
|
document.getElementById('newGeburtsdatum').value = '';
|
||||||
|
hideModalMessage('geburtsdatumMessage');
|
||||||
|
document.getElementById('geburtsdatumModal').classList.add('visible');
|
||||||
|
document.getElementById('newGeburtsdatum').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeGeburtsdatumDialog() {
|
||||||
|
document.getElementById('geburtsdatumModal').classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveGeburtsdatum() {
|
||||||
|
const val = document.getElementById('newGeburtsdatum').value;
|
||||||
|
if (!val) { showModalMessage('geburtsdatumMessage', 'Bitte ein Datum eingeben.', 'error'); return; }
|
||||||
|
const today = new Date();
|
||||||
|
const birth = new Date(val);
|
||||||
|
const age = today.getFullYear() - birth.getFullYear()
|
||||||
|
- (today < new Date(today.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0);
|
||||||
|
if (age < 18) {
|
||||||
|
showModalMessage('geburtsdatumMessage', 'Du musst mindestens 18 Jahre alt sein.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const btn = document.getElementById('geburtsdatumConfirmBtn');
|
||||||
|
btn.disabled = true; btn.textContent = 'Wird gespeichert…';
|
||||||
|
hideModalMessage('geburtsdatumMessage');
|
||||||
|
try {
|
||||||
|
const res = await fetch('/user/me/geburtsdatum', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ geburtsdatum: val })
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
document.getElementById('gd-geburtsdatum').textContent = formatGeburtsdatum(val);
|
||||||
|
closeGeburtsdatumDialog();
|
||||||
|
showToast();
|
||||||
|
} else if (res.status === 422) {
|
||||||
|
showModalMessage('geburtsdatumMessage', 'Du musst mindestens 18 Jahre alt sein.', 'error');
|
||||||
|
} else {
|
||||||
|
showModalMessage('geburtsdatumMessage', `Fehler: HTTP ${res.status}`, 'error');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showModalMessage('geburtsdatumMessage', 'Server nicht erreichbar.', 'error');
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false; btn.textContent = 'Speichern';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Nickname Dialog ──
|
||||||
|
function openNameDialog() {
|
||||||
|
document.getElementById('newName').value = '';
|
||||||
|
hideModalMessage('nameMessage');
|
||||||
|
document.getElementById('nameModal').classList.add('visible');
|
||||||
|
document.getElementById('newName').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeNameDialog() {
|
||||||
|
document.getElementById('nameModal').classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveName() {
|
||||||
|
const newName = document.getElementById('newName').value.trim();
|
||||||
|
if (!newName) { showModalMessage('nameMessage', 'Bitte einen Namen eingeben.', 'error'); return; }
|
||||||
|
const btn = document.getElementById('nameConfirmBtn');
|
||||||
|
btn.disabled = true; btn.textContent = 'Wird gespeichert…';
|
||||||
|
hideModalMessage('nameMessage');
|
||||||
|
try {
|
||||||
|
const res = await fetch('/user/me/name', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ name: newName })
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
document.getElementById('gd-name').textContent = newName;
|
||||||
|
closeNameDialog();
|
||||||
|
showToast();
|
||||||
|
} else if (res.status === 409) {
|
||||||
|
showModalMessage('nameMessage', 'Dieser Nickname ist bereits vergeben.', 'error');
|
||||||
|
} else {
|
||||||
|
showModalMessage('nameMessage', `Fehler: HTTP ${res.status}`, 'error');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showModalMessage('nameMessage', 'Server nicht erreichbar.', 'error');
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false; btn.textContent = 'Speichern';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── E-Mail Dialog ──
|
||||||
|
function openEmailDialog() {
|
||||||
|
document.getElementById('newEmail').value = '';
|
||||||
|
hideModalMessage('emailMessage');
|
||||||
|
document.getElementById('emailModal').classList.add('visible');
|
||||||
|
document.getElementById('newEmail').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEmailDialog() {
|
||||||
|
document.getElementById('emailModal').classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestEmailChange() {
|
||||||
|
const newEmail = document.getElementById('newEmail').value.trim();
|
||||||
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail)) {
|
||||||
|
showModalMessage('emailMessage', 'Bitte eine gültige E-Mail-Adresse eingeben.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const btn = document.getElementById('emailConfirmBtn');
|
||||||
|
btn.disabled = true; btn.textContent = 'Wird gesendet…';
|
||||||
|
hideModalMessage('emailMessage');
|
||||||
|
try {
|
||||||
|
const res = await fetch('/email-change', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ newEmail })
|
||||||
|
});
|
||||||
|
if (res.status === 202) {
|
||||||
|
showModalMessage('emailMessage',
|
||||||
|
'Bestätigungsmail wurde an die neue Adresse gesendet. Bitte bestätige die Änderung über den Link in der E-Mail.',
|
||||||
|
'success');
|
||||||
|
btn.disabled = true; btn.textContent = 'Gesendet';
|
||||||
|
} else if (res.status === 409) {
|
||||||
|
showModalMessage('emailMessage', 'Diese E-Mail-Adresse ist bereits vergeben.', 'error');
|
||||||
|
btn.disabled = false; btn.textContent = 'Bestätigungsmail senden';
|
||||||
|
} else {
|
||||||
|
showModalMessage('emailMessage', `Fehler: HTTP ${res.status}`, 'error');
|
||||||
|
btn.disabled = false; btn.textContent = 'Bestätigungsmail senden';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showModalMessage('emailMessage', 'Server nicht erreichbar.', 'error');
|
||||||
|
btn.disabled = false; btn.textContent = 'Bestätigungsmail senden';
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Konto löschen Dialog ──
|
||||||
|
function openDeleteDialog() {
|
||||||
|
hideModalMessage('deleteMessage');
|
||||||
|
document.getElementById('deleteConfirmBtn').disabled = false;
|
||||||
|
document.getElementById('deleteConfirmBtn').textContent = 'Konto löschen';
|
||||||
|
document.getElementById('deleteModal').classList.add('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDeleteDialog() {
|
||||||
|
document.getElementById('deleteModal').classList.remove('visible');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteAccount() {
|
||||||
|
const btn = document.getElementById('deleteConfirmBtn');
|
||||||
|
btn.disabled = true; btn.textContent = 'Wird gelöscht…';
|
||||||
|
hideModalMessage('deleteMessage');
|
||||||
|
try {
|
||||||
|
const res = await fetch('/user/me', { method: 'DELETE' });
|
||||||
|
if (res.ok) {
|
||||||
|
window.location.href = '/login.html?accountDeleted=1';
|
||||||
|
} else {
|
||||||
|
showModalMessage('deleteMessage', `Fehler: HTTP ${res.status}`, 'error');
|
||||||
|
btn.disabled = false; btn.textContent = 'Konto löschen';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showModalMessage('deleteMessage', 'Server nicht erreichbar.', 'error');
|
||||||
|
btn.disabled = false; btn.textContent = 'Konto löschen';
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModalMessage(id, text, type) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
el.textContent = text;
|
||||||
|
el.className = `message ${type}`;
|
||||||
|
el.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideModalMessage(id) {
|
||||||
|
document.getElementById(id).style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal-Backdrop-Klick schließt Modals
|
||||||
|
['nameModal','geburtsdatumModal','emailModal','deleteModal'].forEach(id => {
|
||||||
|
document.getElementById(id).addEventListener('click', e => {
|
||||||
|
if (e.target === document.getElementById(id)) document.getElementById(id).classList.remove('visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
document.getElementById('newName').addEventListener('keydown', e => { if (e.key === 'Enter') saveName(); });
|
||||||
|
document.getElementById('newEmail').addEventListener('keydown', e => { if (e.key === 'Enter') requestEmailChange(); });
|
||||||
|
|
||||||
|
// ── Datenschutz laden ──
|
||||||
|
async function loadPrivacy() {
|
||||||
|
const meRes = await fetch('/login/me');
|
||||||
|
if (!meRes.ok) return;
|
||||||
|
const me = await meRes.json();
|
||||||
|
myUserId = me.userId;
|
||||||
|
|
||||||
|
const profRes = await fetch('/social/users/' + myUserId);
|
||||||
|
if (!profRes.ok) return;
|
||||||
|
const profile = await profRes.json();
|
||||||
|
|
||||||
|
setValue('sv-grunddaten', profile.sichtbarkeitGrunddaten || 'ALLE');
|
||||||
|
setValue('sv-galerie', profile.sichtbarkeitGalerie || 'ALLE');
|
||||||
|
setValue('sv-freunde', profile.sichtbarkeitFreunde || 'ALLE');
|
||||||
|
setValue('sv-feed', profile.sichtbarkeitFeed || 'ALLE');
|
||||||
|
setValue('sv-pinnwand', profile.sichtbarkeitPinnwand || 'ALLE');
|
||||||
|
setValue('sv-xp', profile.sichtbarkeitXp || 'ALLE');
|
||||||
|
setValue('sv-lockhistorie', profile.sichtbarkeitLockhistorie || 'ALLE');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setValue(id, value) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doSave() {
|
||||||
|
const body = {
|
||||||
|
sichtbarkeitGrunddaten: document.getElementById('sv-grunddaten').value,
|
||||||
|
sichtbarkeitGalerie: document.getElementById('sv-galerie').value,
|
||||||
|
sichtbarkeitFreunde: document.getElementById('sv-freunde').value,
|
||||||
|
sichtbarkeitFeed: document.getElementById('sv-feed').value,
|
||||||
|
sichtbarkeitPinnwand: document.getElementById('sv-pinnwand').value,
|
||||||
|
sichtbarkeitXp: document.getElementById('sv-xp').value,
|
||||||
|
sichtbarkeitLockhistorie: document.getElementById('sv-lockhistorie').value,
|
||||||
|
};
|
||||||
|
const res = await fetch('/user/me/privacy', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
});
|
||||||
|
if (res.ok) showToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showToast() {
|
||||||
|
const t = document.getElementById('saveToast');
|
||||||
|
t.classList.add('visible');
|
||||||
|
clearTimeout(toastTimer);
|
||||||
|
toastTimer = setTimeout(() => t.classList.remove('visible'), 2200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPreview(mode) {
|
||||||
|
if (!myUserId) return;
|
||||||
|
window.open('/benutzer.html?userId=' + myUserId + '&preview=' + mode, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Benachrichtigungen laden ──
|
||||||
|
async function loadNotifications() {
|
||||||
|
const res = await fetch('/user/me/notifications');
|
||||||
|
if (!res.ok) return;
|
||||||
|
const data = await res.json();
|
||||||
|
for (const cause of ['INVITATION', 'GAME_STATE', 'EMERGENCY']) {
|
||||||
|
const pref = data[cause] || { inApp: true, email: false };
|
||||||
|
const inApp = document.getElementById('notif-' + cause + '-inApp');
|
||||||
|
const email = document.getElementById('notif-' + cause + '-email');
|
||||||
|
if (inApp) inApp.checked = pref.inApp;
|
||||||
|
if (email) email.checked = pref.email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveNotifications() {
|
||||||
|
const body = {};
|
||||||
|
for (const cause of ['INVITATION', 'GAME_STATE', 'EMERGENCY']) {
|
||||||
|
const inApp = document.getElementById('notif-' + cause + '-inApp');
|
||||||
|
const email = document.getElementById('notif-' + cause + '-email');
|
||||||
|
body[cause] = {
|
||||||
|
inApp: inApp ? inApp.checked : true,
|
||||||
|
email: email ? email.checked : false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const res = await fetch('/user/me/notifications', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
});
|
||||||
|
if (res.ok) showToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash-Navigation: Sektion anhand URL-Fragment öffnen
|
||||||
|
function openSectionFromHash() {
|
||||||
|
const hash = window.location.hash.replace('#', '');
|
||||||
|
if (hash) {
|
||||||
|
const el = document.getElementById(hash);
|
||||||
|
if (el) el.classList.add('open');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Spiel Einstellungen – BDSM ──
|
||||||
|
function applyCheckItems(groupId, values) {
|
||||||
|
document.querySelectorAll(`#${groupId} .spiel-check-item input`).forEach(cb => {
|
||||||
|
const checked = values.includes(cb.value);
|
||||||
|
cb.checked = checked;
|
||||||
|
cb.closest('.spiel-check-item').classList.toggle('is-checked', checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.spiel-check-item input').forEach(cb => {
|
||||||
|
cb.addEventListener('change', () => {
|
||||||
|
cb.closest('.spiel-check-item').classList.toggle('is-checked', cb.checked);
|
||||||
|
saveBdsmDefaults();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadBdsmDefaults() {
|
||||||
|
const res = await fetch('/user/me/bdsm-defaults');
|
||||||
|
if (!res.ok) return;
|
||||||
|
const d = await res.json();
|
||||||
|
applyCheckItems('bdsm-spieltmit', d.spieltMit || []);
|
||||||
|
applyCheckItems('bdsm-rollen', d.rollen || []);
|
||||||
|
applyCheckItems('bdsm-werkzeuge', d.werkzeuge || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveBdsmDefaults() {
|
||||||
|
const get = groupId => [...document.querySelectorAll(`#${groupId} input:checked`)].map(cb => cb.value);
|
||||||
|
await fetch('/user/me/bdsm-defaults', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
spieltMit: get('bdsm-spieltmit'),
|
||||||
|
rollen: get('bdsm-rollen'),
|
||||||
|
werkzeuge: get('bdsm-werkzeuge'),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
showToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadGrunddaten();
|
||||||
|
loadPrivacy();
|
||||||
|
loadNotifications();
|
||||||
|
loadBdsmDefaults();
|
||||||
|
openSectionFromHash();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user