Skip to content

Commit 4c8ac18

Browse files
authored
Merge pull request #6373 from WoltLab/6.2-interactions-on-detail-page
Make interactions available on the detail page
2 parents 21bb6f6 + 4663266 commit 4c8ac18

86 files changed

Lines changed: 1007 additions & 342 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

com.woltlab.wcf/templates/articleAdd.tpl

Lines changed: 3 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
{capture assign='__contentHeader'}
22
<header class="contentHeader">
33
<div class="contentHeaderTitle">
4-
<h1 class="contentTitle">
5-
{if $articleIsFrontend|empty}
6-
{if $action == 'add'}{lang}wcf.acp.article.add{/lang}{else}{lang}wcf.acp.article.edit{/lang}{/if}
7-
{else}
8-
{$__wcf->getActivePage()->getTitle()}
9-
{/if}
10-
</h1>
4+
<h1 class="contentTitle">{$__wcf->getActivePage()->getTitle()}</h1>
115
</div>
126

137
{hascontent}
@@ -25,48 +19,7 @@
2519
</header>
2620
{/capture}
2721

28-
{capture assign='contentInteractionButtons'}
29-
{if $action == 'edit'}
30-
{if $article->canDelete()}
31-
<button
32-
type="button"
33-
class="contentInteractionButton button small jsButtonRestore"
34-
{if !$article->isDeleted} style="display: none"{/if}
35-
>
36-
{icon name='rotate-left'}
37-
<span>{lang}wcf.global.button.restore{/lang}</span>
38-
</button>
39-
<button
40-
type="button"
41-
class="contentInteractionButton button small jsButtonDelete"
42-
{if !$article->isDeleted} style="display: none"{/if}
43-
>
44-
{icon name='xmark'}
45-
<span>{lang}wcf.global.button.delete{/lang}</span>
46-
</button>
47-
<button
48-
type="button"
49-
class="contentInteractionButton button small jsButtonTrash"
50-
{if $article->isDeleted} style="display: none"{/if}
51-
>
52-
{icon name='trash-can'}
53-
<span>{lang}wcf.global.button.trash{/lang}</span>
54-
</button>
55-
{/if}
56-
{if $languages|count > 1 || $article->isMultilingual}
57-
<button type="button" class="contentInteractionButton button small jsButtonToggleI18n">
58-
{icon name='language'}
59-
<span>{lang}wcf.acp.article.button.toggleI18n{/lang}</span>
60-
</button>
61-
{/if}
62-
{/if}
63-
{/capture}
64-
65-
{if $articleIsFrontend|empty}
66-
{include file='header' pageTitle='wcf.acp.article.'|concat:$action}
67-
{else}
68-
{include file='header' contentHeader=$__contentHeader}
69-
{/if}
22+
{include file='header' contentHeader=$__contentHeader}
7023

7124
{if $__wcf->session->getPermission('admin.content.article.canManageArticle')}
7225
<script data-relocate="true">
@@ -94,27 +47,8 @@
9447
{/if}
9548

9649
<script data-relocate="true">
97-
require(['Language', 'WoltLabSuite/Core/Ui/User/Search/Input', 'WoltLabSuite/Core/Acp/Ui/Article/InlineEditor'], function(Language, UiUserSearchInput, AcpUiArticleInlineEditor) {
98-
Language.addObject({
99-
'wcf.article.convertFromI18n.question': '{jslang}wcf.article.convertFromI18n.question{/jslang}',
100-
'wcf.article.convertFromI18n.description': '{jslang}wcf.article.convertFromI18n.description{/jslang}',
101-
'wcf.article.convertToI18n.question': '{jslang}wcf.article.convertToI18n.question{/jslang}',
102-
'wcf.article.convertToI18n.description': '{jslang}wcf.article.convertToI18n.description{/jslang}',
103-
'wcf.acp.article.i18n.source': '{jslang}wcf.acp.article.i18n.source{/jslang}',
104-
'wcf.message.status.deleted': '{jslang}wcf.message.status.deleted{/jslang}',
105-
});
106-
50+
require(['WoltLabSuite/Core/Ui/User/Search/Input'], (UiUserSearchInput) => {
10751
new UiUserSearchInput(document.querySelector('input[name="username"]'));
108-
{if $action == 'edit'}
109-
new AcpUiArticleInlineEditor({$article->articleID}, {
110-
i18n: {
111-
defaultLanguageId: {$defaultLanguageID},
112-
isI18n: {if $article->isMultilingual}true{else}false{/if},
113-
languages: { {implode from=$languages item=language glue=', '}{$language->languageID}: '{$language|encodeJS}'{/implode} }
114-
},
115-
redirectUrl: '{link controller='ArticleList'}{/link}'
116-
});
117-
{/if}
11852
});
11953
</script>
12054

@@ -131,10 +65,6 @@
13165
</script>
13266
{/if}
13367

134-
{if $articleIsFrontend|empty}
135-
{unsafe:$__contentHeader}
136-
{/if}
137-
13868
{include file='shared_formNotice'}
13969

14070
{if $action == 'edit'}

ts/WoltLabSuite/Core/Acp/Ui/Article/InlineEditor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @author Alexander Ebert
55
* @copyright 2001-2019 WoltLab GmbH
66
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7+
* @deprecated 6.2 No longer in use.
78
*/
89

910
import { showDefaultSuccessSnackbar } from "WoltLabSuite/Core/Component/Snackbar";

ts/WoltLabSuite/Core/Component/Interaction/FormBuilderDialog.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,65 @@
99

1010
import { dialogFactory } from "WoltLabSuite/Core/Component/Dialog";
1111
import { showDefaultSuccessSnackbar } from "WoltLabSuite/Core/Component/Snackbar";
12+
import { InteractionEffect } from "./InteractionEffect";
1213

13-
async function handleFormBuilderDialogAction(element: HTMLElement, endpoint: string): Promise<void> {
14+
type Payload = Record<string, string>;
15+
16+
async function handleFormBuilderDialogAction(
17+
container: HTMLElement,
18+
element: HTMLElement,
19+
endpoint: string,
20+
interactionEffect: InteractionEffect = InteractionEffect.ReloadItem,
21+
detail: Payload,
22+
): Promise<void> {
1423
const { ok } = await dialogFactory().usingFormBuilder().fromEndpoint(endpoint);
1524

1625
if (!ok) {
1726
return;
1827
}
1928

20-
element.dispatchEvent(
21-
new CustomEvent("interaction:invalidate", {
22-
bubbles: true,
23-
}),
24-
);
29+
if (interactionEffect === InteractionEffect.ReloadItem || interactionEffect === InteractionEffect.ReloadPage) {
30+
element.dispatchEvent(
31+
new CustomEvent<Payload>("interaction:invalidate", {
32+
bubbles: true,
33+
detail: {
34+
...detail,
35+
_reloadPage: String(interactionEffect === InteractionEffect.ReloadPage),
36+
},
37+
}),
38+
);
39+
} else if (interactionEffect === InteractionEffect.ReloadList) {
40+
container.dispatchEvent(
41+
new CustomEvent<Payload>("interaction:invalidate-all", {
42+
detail: {
43+
...detail,
44+
},
45+
}),
46+
);
47+
} else {
48+
element.dispatchEvent(
49+
new CustomEvent<Payload>("interaction:remove", {
50+
bubbles: true,
51+
detail: {
52+
...detail,
53+
},
54+
}),
55+
);
56+
}
2557

2658
showDefaultSuccessSnackbar();
2759
}
2860

2961
export function setup(identifier: string, container: HTMLElement): void {
30-
container.addEventListener("interaction:execute", (event: CustomEvent) => {
62+
container.addEventListener("interaction:execute", (event: CustomEvent<Payload>) => {
3163
if (event.detail.interaction === identifier) {
32-
void handleFormBuilderDialogAction(event.target as HTMLElement, event.detail.endpoint);
64+
void handleFormBuilderDialogAction(
65+
container,
66+
event.target as HTMLElement,
67+
event.detail.endpoint,
68+
event.detail.interactionEffect as InteractionEffect,
69+
event.detail,
70+
);
3371
}
3472
});
3573
}

ts/WoltLabSuite/Core/Component/Interaction/InteractionEffect.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
export enum InteractionEffect {
1111
ReloadItem = "ReloadItem",
1212
ReloadList = "ReloadList",
13+
ReloadPage = "ReloadPage",
1314
RemoveItem = "RemoveItem",
1415
}

ts/WoltLabSuite/Core/Component/Interaction/LegacyDboAction.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { showDefaultSuccessSnackbar, showSuccessSnackbar } from "WoltLabSuite/Co
1414
import { getPhrase } from "WoltLabSuite/Core/Language";
1515
import { InteractionEffect } from "./InteractionEffect";
1616

17+
type Payload = Record<string, string>;
18+
1719
async function handleDboAction(
1820
container: HTMLElement,
1921
element: HTMLElement,
@@ -23,6 +25,7 @@ async function handleDboAction(
2325
confirmationType: ConfirmationType,
2426
customConfirmationMessage: string = "",
2527
interactionEffect: InteractionEffect = InteractionEffect.ReloadItem,
28+
detail: Payload,
2629
): Promise<void> {
2730
const confirmationResult = await handleConfirmation(objectName, confirmationType, customConfirmationMessage);
2831
if (!confirmationResult.result) {
@@ -34,18 +37,27 @@ async function handleDboAction(
3437
.payload(confirmationResult.reason ? { reason: confirmationResult.reason } : {})
3538
.dispatch();
3639

37-
if (interactionEffect === InteractionEffect.ReloadItem) {
40+
if (interactionEffect === InteractionEffect.ReloadItem || interactionEffect === InteractionEffect.ReloadPage) {
3841
element.dispatchEvent(
39-
new CustomEvent("interaction:invalidate", {
42+
new CustomEvent<Payload>("interaction:invalidate", {
4043
bubbles: true,
44+
detail: {
45+
...detail,
46+
_reloadPage: String(interactionEffect === InteractionEffect.ReloadPage),
47+
},
4148
}),
4249
);
4350
} else if (interactionEffect === InteractionEffect.ReloadList) {
44-
container.dispatchEvent(new CustomEvent("interaction:invalidate-all"));
51+
container.dispatchEvent(
52+
new CustomEvent<Payload>("interaction:invalidate-all", {
53+
detail,
54+
}),
55+
);
4556
} else {
4657
element.dispatchEvent(
47-
new CustomEvent("interaction:remove", {
58+
new CustomEvent<Payload>("interaction:remove", {
4859
bubbles: true,
60+
detail,
4961
}),
5062
);
5163
}
@@ -58,17 +70,18 @@ async function handleDboAction(
5870
}
5971

6072
export function setup(identifier: string, container: HTMLElement): void {
61-
container.addEventListener("interaction:execute", (event: CustomEvent) => {
73+
container.addEventListener("interaction:execute", (event: CustomEvent<Payload>) => {
6274
if (event.detail.interaction === identifier) {
6375
void handleDboAction(
6476
container,
6577
event.target as HTMLElement,
6678
event.detail.objectName,
6779
event.detail.className,
6880
event.detail.actionName,
69-
event.detail.confirmationType,
81+
event.detail.confirmationType as ConfirmationType,
7082
event.detail.confirmationMessage,
71-
event.detail.interactionEffect,
83+
event.detail.interactionEffect as InteractionEffect,
84+
event.detail,
7285
);
7386
}
7487
});

ts/WoltLabSuite/Core/Component/Interaction/Rpc.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { showDefaultSuccessSnackbar, showSuccessSnackbar } from "WoltLabSuite/Co
1414
import { getPhrase } from "WoltLabSuite/Core/Language";
1515
import { InteractionEffect } from "./InteractionEffect";
1616

17+
type Payload = Record<string, string>;
18+
1719
async function handleRpcInteraction(
1820
container: HTMLElement,
1921
element: HTMLElement,
@@ -22,6 +24,7 @@ async function handleRpcInteraction(
2224
confirmationType: ConfirmationType,
2325
customConfirmationMessage: string = "",
2426
interactionEffect: InteractionEffect = InteractionEffect.ReloadItem,
27+
detail: Payload,
2528
): Promise<void> {
2629
const confirmationResult = await handleConfirmation(objectName, confirmationType, customConfirmationMessage);
2730
if (!confirmationResult.result) {
@@ -43,18 +46,23 @@ async function handleRpcInteraction(
4346
}
4447
}
4548

46-
if (interactionEffect === InteractionEffect.ReloadItem) {
49+
if (interactionEffect === InteractionEffect.ReloadItem || interactionEffect === InteractionEffect.ReloadPage) {
4750
element.dispatchEvent(
48-
new CustomEvent("interaction:invalidate", {
51+
new CustomEvent<Payload>("interaction:invalidate", {
4952
bubbles: true,
53+
detail: {
54+
...detail,
55+
_reloadPage: String(interactionEffect === InteractionEffect.ReloadPage),
56+
},
5057
}),
5158
);
5259
} else if (interactionEffect === InteractionEffect.ReloadList) {
53-
container.dispatchEvent(new CustomEvent("interaction:invalidate-all"));
60+
container.dispatchEvent(new CustomEvent<Payload>("interaction:invalidate-all", { detail }));
5461
} else {
5562
element.dispatchEvent(
56-
new CustomEvent("interaction:remove", {
63+
new CustomEvent<Payload>("interaction:remove", {
5764
bubbles: true,
65+
detail,
5866
}),
5967
);
6068
}
@@ -67,16 +75,17 @@ async function handleRpcInteraction(
6775
}
6876

6977
export function setup(identifier: string, container: HTMLElement): void {
70-
container.addEventListener("interaction:execute", (event: CustomEvent) => {
78+
container.addEventListener("interaction:execute", (event: CustomEvent<Payload>) => {
7179
if (event.detail.interaction === identifier) {
7280
void handleRpcInteraction(
7381
container,
7482
event.target as HTMLElement,
7583
event.detail.objectName,
7684
event.detail.endpoint,
77-
event.detail.confirmationType,
85+
event.detail.confirmationType as ConfirmationType,
7886
event.detail.confirmationMessage,
79-
event.detail.interactionEffect,
87+
event.detail.interactionEffect as InteractionEffect,
88+
event.detail,
8089
);
8190
}
8291
});

ts/WoltLabSuite/Core/Component/Interaction/StandaloneButton.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ interface HeaderContent {
1515
template: string;
1616
}
1717

18+
type Payload = Record<string, string>;
19+
1820
export class StandaloneButton {
1921
#container: HTMLElement;
2022
#providerClassName: string;
@@ -100,9 +102,15 @@ export class StandaloneButton {
100102
}
101103

102104
#initEventListeners(): void {
103-
this.#container.addEventListener("interaction:invalidate", () => {
104-
void this.#refreshContextMenu();
105-
void this.#refreshHeader();
105+
this.#container.addEventListener("interaction:invalidate", (event: CustomEvent<Payload>) => {
106+
if (event.detail._reloadPage === "true") {
107+
setTimeout(() => {
108+
window.location.reload();
109+
}, 2000);
110+
} else {
111+
void this.#refreshContextMenu();
112+
void this.#refreshHeader();
113+
}
106114
});
107115

108116
this.#container.addEventListener("interaction:invalidate-all", () => {
@@ -111,7 +119,9 @@ export class StandaloneButton {
111119
});
112120

113121
this.#container.addEventListener("interaction:remove", () => {
114-
window.location.href = this.#redirectUrl;
122+
setTimeout(() => {
123+
window.location.href = this.#redirectUrl;
124+
}, 2000);
115125
});
116126
}
117127
}

wcfsetup/install/files/acp/templates/adAdd.tpl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
<nav class="contentHeaderNavigation">
1717
<ul>
18-
<li><a href="{link controller='AdList'}{/link}" class="button">{icon name='list'} <span>{lang}wcf.acp.menu.link.ad.list{/lang}</span></a></li>
19-
18+
{if $action == 'edit'}
19+
<li>
20+
{unsafe:$interactionContextMenu->render()}
21+
</li>
22+
{/if}
2023
{event name='contentHeaderNavigation'}
2124
</ul>
2225
</nav>

0 commit comments

Comments
 (0)