Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 0 additions & 26 deletions files/js/WoltLabSuite/Core/Conversation/Component/MarkAsRead.js

This file was deleted.

2 changes: 2 additions & 0 deletions files/lib/bootstrap/com.woltlab.wcf.conversation.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) {
$event->register(new \wcf\system\endpoint\controller\core\conversations\OpenConversation());
$event->register(new \wcf\system\endpoint\controller\core\conversations\CloseConversation());
$event->register(new \wcf\system\endpoint\controller\core\conversations\GetConversationHeaderTitle());
$event->register(new \wcf\system\endpoint\controller\core\conversations\MarkAllConversationsAsRead());
$event->register(new \wcf\system\endpoint\controller\core\conversations\MarkConversationAsRead());
$event->register(new \wcf\system\endpoint\controller\core\conversations\RemoveConversationParticipant());
$event->register(new \wcf\system\endpoint\controller\core\conversations\GetConversationParticipantList());
$event->register(new \wcf\system\endpoint\controller\core\conversations\labels\DeleteConversationLabel());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace wcf\command\conversation;

use wcf\data\user\User;
use wcf\system\user\notification\UserNotificationHandler;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\WCF;

/**
* Marks all conversations for a given user as read.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
final class MarkAllConversationsAsRead
{
public function __construct(
public readonly User $user
) {}

public function __invoke(): void
{
$sql = "UPDATE wcf1_conversation_to_user
SET lastVisitTime = ?
WHERE participantID = ?";
$statement = WCF::getDB()->prepare($sql);
$statement->execute([
\TIME_NOW,
$this->user->userID,
]);

UserStorageHandler::getInstance()->reset([$this->user->userID], 'unreadConversationCount');

UserNotificationHandler::getInstance()->markAsConfirmed(
'conversation',
'com.woltlab.wcf.conversation.notification',
[$this->user->userID]
);

UserNotificationHandler::getInstance()->markAsConfirmed(
'conversationMessage',
'com.woltlab.wcf.conversation.message.notification',
[$this->user->userID]
);
}
}
84 changes: 84 additions & 0 deletions files/lib/command/conversation/MarkConversationAsRead.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace wcf\command\conversation;

use wcf\data\conversation\Conversation;
use wcf\data\user\User;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\user\notification\UserNotificationHandler;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\WCF;

/**
* Marks a conversation for a given user as read.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
final class MarkConversationAsRead
{
public function __construct(
public readonly Conversation $conversation,
public readonly User $user
) {}

public function __invoke(): void
{
$sql = "UPDATE wcf1_conversation_to_user
SET lastVisitTime = ?
WHERE participantID = ?
AND conversationID = ?";
$statement = WCF::getDB()->prepare($sql);
$statement->execute([
\TIME_NOW,
$this->user->userID,
$this->conversation->conversationID,
]);

UserStorageHandler::getInstance()->reset([$this->user->userID], 'unreadConversationCount');

$this->markNotificationsAsConfirmed($this->conversation->conversationID, $this->user->userID);
}

private function markNotificationsAsConfirmed(int $conversationID, int $userID): void
{
// 1) Mark notifications about new conversations as read.
UserNotificationHandler::getInstance()->markAsConfirmed(
'conversation',
'com.woltlab.wcf.conversation.notification',
[$userID],
[$conversationID]
);

// 2) Mark notifications about new replies as read.
$eventID = UserNotificationHandler::getInstance()
->getEvent('com.woltlab.wcf.conversation.message.notification', 'conversationMessage')
->eventID;

$condition = new PreparedStatementConditionBuilder();
$condition->add('notification.userID = ?', [$userID]);
$condition->add('notification.confirmTime = ?', [0]);
$condition->add('notification.eventID = ?', [$eventID]);
$condition->add("notification.objectID IN (
SELECT messageID
FROM wcf1_conversation_message
WHERE conversationID = ?
AND time <= ?
)", [
$conversationID,
\TIME_NOW,
]);

$sql = "SELECT notificationID
FROM wcf1_user_notification notification
{$condition}";
$statement = WCF::getDB()->prepare($sql);
$statement->execute($condition->getParameters());

UserNotificationHandler::getInstance()->markAsConfirmedByIDs(
$statement->fetchAll(\PDO::FETCH_COLUMN)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace wcf\system\endpoint\controller\core\conversations;

use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use wcf\system\endpoint\IController;
use wcf\system\endpoint\PostRequest;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\WCF;

/**
* Marks all conversations for the current user as read.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
#[PostRequest('/core/conversations/mark-all-as-read')]
final class MarkAllConversationsAsRead implements IController
{
#[\Override]
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
if (!WCF::getUser()->userID) {
throw new PermissionDeniedException();
}

(new \wcf\command\conversation\MarkAllConversationsAsRead(WCF::getUser()))();

return new JsonResponse([]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace wcf\system\endpoint\controller\core\conversations;

use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use wcf\data\conversation\Conversation;
use wcf\http\Helper;
use wcf\system\endpoint\IController;
use wcf\system\endpoint\PostRequest;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\WCF;

/**
* Marks the conversation with the given ID as read for the current user.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/
#[PostRequest('/core/conversations/{id:\d+}/mark-as-read')]
final class MarkConversationAsRead implements IController
{
#[\Override]
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
$conversation = Helper::fetchObjectFromRequestParameter($variables['id'], Conversation::class);
$this->assertConversationIsAccessible($conversation);

(new \wcf\command\conversation\MarkConversationAsRead($conversation, WCF::getUser()))();

return new JsonResponse([]);
}

private function assertConversationIsAccessible(Conversation $conversation): void
{
if (!Conversation::isParticipant([$conversation->conversationID])) {
throw new PermissionDeniedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function __construct(string $filter = '')
$this->setSortOrder(\CONVERSATION_LIST_DEFAULT_SORT_ORDER);
$this->setCssClassName("discussionList conversationList");
$this->setContainerCssClassName('discussionList__container conversationList__container');
$this->setMarkAsReadEndpoints('core/conversations/%s/mark-as-read');
}

#[\Override]
Expand Down
1 change: 0 additions & 1 deletion language/de.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@
<item name="wcf.conversation.noParticipantsWarning"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du bist{else}Sie sind{/if} dabei, auf eine Konversation ohne weitere Teilnehmer zu antworten. Alle anderen Teilnehmer haben diese Konversation verlassen. Niemand wird {if LANGUAGE_USE_INFORMAL_VARIANT}deine{else}Ihre{/if} Nachricht lesen!]]></item>
<item name="wcf.conversation.invisibleParticipantWarning"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Du nimmst{else}Sie nehmen{/if} gegenwärtig an dieser Konversation als unsichtbarer Teilnehmer teil. Sobald {if LANGUAGE_USE_INFORMAL_VARIANT}du eine Antwort verfasst{else}Sie eine Antwort verfassen{/if}, wird diese für alle sichtbar sein und {if LANGUAGE_USE_INFORMAL_VARIANT}du bist{else}Sie sind{/if} nicht länger für andere Teilnehmer unsichtbar.]]></item>
<item name="wcf.conversation.message.permalink"><![CDATA[Permalink zur {#$startIndex}. Nachricht]]></item>
<item name="wcf.conversation.markAsRead"><![CDATA[Konversation als gelesen markieren]]></item>
<item name="wcf.conversation.visibility"><![CDATA[Leseberechtigung]]></item>
<item name="wcf.conversation.visibility.all"><![CDATA[Alle Nachrichten]]></item>
<item name="wcf.conversation.visibility.all.description"><![CDATA[Es werden alle Nachrichten angezeigt, auch jene die vor dem Beitritt zu dieser Konversation verfasst wurden.]]></item>
Expand Down
1 change: 0 additions & 1 deletion language/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@
<item name="wcf.conversation.noParticipantsWarning"><![CDATA[You are about to reply to a conversation without other participants, nobody is going to read your message!]]></item>
<item name="wcf.conversation.invisibleParticipantWarning"><![CDATA[You are currently an invisible participant of this conversation. Any message you write in this conversation will be visible to every participant and you will no longer be invisible to others.]]></item>
<item name="wcf.conversation.message.permalink"><![CDATA[Permalink to {#$startIndex}. message]]></item>
<item name="wcf.conversation.markAsRead"><![CDATA[Mark This Conversation as Read]]></item>
<item name="wcf.conversation.visibility"><![CDATA[Read Access]]></item>
<item name="wcf.conversation.visibility.all"><![CDATA[All messages]]></item>
<item name="wcf.conversation.visibility.all.description"><![CDATA[All messages will be shown, including those written before they were added to this conversation.]]></item>
Expand Down
8 changes: 2 additions & 6 deletions templates/conversationList.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,8 @@
</div>

<script data-relocate="true">
require([
'WoltLabSuite/Core/Conversation/Component/MarkAllAsRead',
'WoltLabSuite/Core/Conversation/Component/MarkAsRead',
], (MarkAllAsRead, MarkAsRead) => {
MarkAllAsRead.setup();
MarkAsRead.setup();
require(['WoltLabSuite/Core/Conversation/Component/MarkAllAsRead'], ({ setup }) => {
setup(document.getElementById('{unsafe:$listView->getID()|encodeJS}_items'));
});
</script>

Expand Down
9 changes: 1 addition & 8 deletions templates/conversationListItems.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,7 @@

<div class="discussionList__item__content">
{if $conversation->isNew()}
<button
type="button"
class="discussionList__item__markAsRead jsTooltip"
title="{lang}wcf.conversation.markAsRead{/lang}"
data-object-id="{$conversation->conversationID}"
>
<span class="discussionList__item__unread__indicator" aria-hidden="true"></span>
</button>
{unsafe:$view->renderMarkAsReadButton($conversation)}
{/if}

<h2 class="discussionList__item__title">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Marks all conversations as read.
*
* @author Marcel Werk
* @copyright 2001-2025 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.2
*/

import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "WoltLabSuite/Core/Api/Result";

export async function markAllConversationsAsRead(): Promise<ApiResult<[]>> {
try {
await prepareRequest(`${window.WSC_RPC_API_URL}core/conversations/mark-all-as-read`).post().fetchAsJson();
} catch (e) {
return apiResultFromError(e);
}

return apiResultFromValue([]);
}
Loading