Skip to content
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
1b81a81
Added "Report" option in all user context menus
sandrade-dcl Mar 5, 2026
6a8edfd
Added confirmation popup when report option is clicked
sandrade-dcl Mar 5, 2026
1510302
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 5, 2026
70d3e4b
Refactor: Create ReportUserConfirmationDialog for unifying the logic …
sandrade-dcl Mar 6, 2026
d84bbca
Update RequestsSectionController.cs
sandrade-dcl Mar 6, 2026
43a113b
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 6, 2026
d680b22
Feature flag implemented
sandrade-dcl Mar 6, 2026
0e3ea75
Update FeaturesRegistry.cs
sandrade-dcl Mar 6, 2026
36a6034
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 10, 2026
d070996
IWebBrowser and IDecentralandUrlsSource dependencies injected
sandrade-dcl Mar 10, 2026
78e04bc
Added ReportUser into DecentralandUrl
sandrade-dcl Mar 10, 2026
8533385
Update ApplicationBlocklistGuard.cs
sandrade-dcl Mar 10, 2026
4a85264
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 10, 2026
3eed7a4
New GetBanStatus endpoint implemented
sandrade-dcl Mar 10, 2026
191d07f
Added BanStatusData into UserBlockedException
sandrade-dcl Mar 10, 2026
ad2111b
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 10, 2026
a1d3240
Update ApplicationBlocklistGuard.cs
sandrade-dcl Mar 10, 2026
cf83b21
Added params in the report url
sandrade-dcl Mar 10, 2026
5b343ad
Color for Block and Report options updated
sandrade-dcl Mar 11, 2026
6719641
Implemented ban_warning notification
sandrade-dcl Mar 11, 2026
94a4f5a
Made ban_warning notification doesn't hides automatically
sandrade-dcl Mar 11, 2026
029d828
Made Blocked Screen prefab's info dynamic
sandrade-dcl Mar 11, 2026
078ff0b
Delete DebugNotificationTrigger.cs.meta
sandrade-dcl Mar 11, 2026
11e41ae
Update BlockedScreenController.cs
sandrade-dcl Mar 11, 2026
061ba8d
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 11, 2026
0f5a028
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 11, 2026
d6a54ad
Update Explorer/Assets/DCL/Communities/CommunitiesBrowser/Communities…
sandrade-dcl Mar 11, 2026
eea29fa
Update CommunitiesBrowserController.cs
sandrade-dcl Mar 11, 2026
983cf1c
Implemented banned notification
sandrade-dcl Mar 11, 2026
70881aa
Implemented click on banned notification
sandrade-dcl Mar 11, 2026
a5632af
Implemented click on ban_warning notification
sandrade-dcl Mar 12, 2026
2c2ffd3
Added report-user app arg flag
sandrade-dcl Mar 12, 2026
fde62ed
Update BlockedScreenController.cs
sandrade-dcl Mar 12, 2026
8652499
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 13, 2026
4ae7d0d
Small updates in texts
sandrade-dcl Mar 13, 2026
ebc8e3e
Update Explorer/Assets/DCL/ApplicationsGuards/ApplicationBlocklistGua…
sandrade-dcl Mar 13, 2026
e8e54b6
Update BannedNotificationHandler.cs
sandrade-dcl Mar 13, 2026
bff455e
Update BannedNotificationHandler.cs
sandrade-dcl Mar 13, 2026
d43548b
Feedback applied
sandrade-dcl Mar 13, 2026
c8a1192
Feedback applied
sandrade-dcl Mar 13, 2026
92d4345
Merge branch 'dev' into feat/add-report-user-option-in-user-context-m…
sandrade-dcl Mar 23, 2026
b08b819
Updates from figma
sandrade-dcl Mar 23, 2026
7483419
fix: Replace bans url (#7617)
MauroCicerchia Mar 23, 2026
02c7f44
Updates from figma (II)
sandrade-dcl Mar 23, 2026
67c8ccd
Updates from figma (III)
sandrade-dcl Mar 23, 2026
d268659
Added new ban_lifted notification
sandrade-dcl Mar 23, 2026
3d78a5c
Apply feedback
sandrade-dcl Mar 23, 2026
a5eeb6e
Apply feedback (ReportUserHelper)
sandrade-dcl Mar 23, 2026
0f77192
Apply feedback
sandrade-dcl Mar 23, 2026
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
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
using Cysharp.Threading.Tasks;
using DCL.Diagnostics;
using DCL.FeatureFlags;
using DCL.Multiplayer.Connections.DecentralandUrls;
using DCL.Utilities.Extensions;
using DCL.WebRequests;
using SceneRuntime.Apis.Modules.SignedFetch.Messages;
using System;
using System.Linq;
using System.Threading;
using UnityEngine;

namespace DCL.ApplicationBlocklistGuard
{
public static class ApplicationBlocklistGuard
{
public static async UniTask<bool> IsUserBlocklistedAsync(IWebRequestController webRequestController, IDecentralandUrlsSource urlsSource, string userID, CancellationToken ct)
public static async UniTask<GetBanStatusData> IsUserBlocklistedAsync(IWebRequestController webRequestController, IDecentralandUrlsSource urlsSource, string userID, ModerationDataProvider moderationDataProvider, CancellationToken ct)
{
if (FeaturesRegistry.Instance.IsEnabled(FeatureId.REPORT_USER))
{
var result = await moderationDataProvider.GetBanStatusAsync(userID, ct)
.SuppressToResultAsync(ReportCategory.STARTUP);

if (!result.Success)
{
ReportHub.LogError(ReportCategory.STARTUP, $"Failed to get ban status: {result.ErrorMessage}. Skipping blocklist check.");
return new GetBanStatusData { isBanned = false };
}

return result.Value.data;
}

try
{
FlatFetchResponse response = await webRequestController.GetAsync<FlatFetchResponse<GenericGetRequest>, FlatFetchResponse>(
Expand All @@ -27,15 +42,16 @@ public static async UniTask<bool> IsUserBlocklistedAsync(IWebRequestController w

foreach (var t in bd.users)
{
if (string.Equals(t.wallet, userID, StringComparison.OrdinalIgnoreCase)) return true;
if (string.Equals(t.wallet, userID, StringComparison.OrdinalIgnoreCase))
return new GetBanStatusData { isBanned = true };
}

return false;
return new GetBanStatusData { isBanned = false };
}
catch (Exception ex)
{
ReportHub.LogError(ReportCategory.STARTUP, $"Failed to parse blocklist JSON: {ex.Message}. Skipping blocklist check.");
return false;
return new GetBanStatusData { isBanned = false };
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Cysharp.Threading.Tasks;
using DCL.Browser;
using DCL.Diagnostics;
using DCL.Multiplayer.Connections.DecentralandUrls;
using DCL.NotificationsBus;
using DCL.NotificationsBus.NotificationTypes;
using DCL.Web3.Identities;
using DCL.WebRequests;
using MVC;
using System;
using System.Threading;
using Utility;

namespace DCL.ApplicationBlocklistGuard
{
public class BannedNotificationHandler : IDisposable
{
private readonly IWebRequestController webRequestController;
private readonly IDecentralandUrlsSource urlsSource;
private readonly IWeb3IdentityCache identityCache;
private readonly ModerationDataProvider moderationDataProvider;
private readonly IMVCManager mvcManager;
private readonly IWebBrowser webBrowser;

private CancellationTokenSource cts = new ();

public BannedNotificationHandler(
IWebRequestController webRequestController,
IDecentralandUrlsSource urlsSource,
IWeb3IdentityCache identityCache,
ModerationDataProvider moderationDataProvider,
IMVCManager mvcManager,
IWebBrowser webBrowser)
{
this.webRequestController = webRequestController;
this.urlsSource = urlsSource;
this.identityCache = identityCache;
this.moderationDataProvider = moderationDataProvider;
this.mvcManager = mvcManager;
this.webBrowser = webBrowser;

NotificationsBusController.Instance.SubscribeToNotificationTypeClick(NotificationType.BAN_WARNING, OnBanWarningNotificationClicked);
NotificationsBusController.Instance.SubscribeToNotificationTypeClick(NotificationType.BANNED, OnBannedNotificationClicked);
}

public void Dispose() =>
cts.SafeCancelAndDispose();

private void OnBanWarningNotificationClicked(object[] parameters) =>
webBrowser.OpenUrl(urlsSource.Url(DecentralandUrl.SupportLink));

private void OnBannedNotificationClicked(object[] parameters)
{
cts = cts.SafeRestart();
FetchBanStatusAndShowBlockedScreenAsync(cts.Token).Forget();
return;

async UniTaskVoid FetchBanStatusAndShowBlockedScreenAsync(CancellationToken ct)
{
try
{
string selfUserId = identityCache.EnsuredIdentity().Address;

GetBanStatusData banStatusData = await ApplicationBlocklistGuard.IsUserBlocklistedAsync(
webRequestController, urlsSource, selfUserId, moderationDataProvider, ct);

if (!banStatusData.isBanned)
return;

await mvcManager.ShowAsync(BlockedScreenController.IssueCommand(new BlockedScreenParameters(banStatusData.ban)), ct);
}
catch (OperationCanceledException) { }
catch (Exception e) { ReportHub.LogException(e, ReportCategory.STARTUP); }
}
{
string selfUserId = identityCache.EnsuredIdentity().Address;

GetBanStatusData banStatusData = await ApplicationBlocklistGuard.IsUserBlocklistedAsync(
webRequestController, urlsSource, selfUserId, moderationDataProvider, ct);

if (!banStatusData.isBanned)
return;

await mvcManager.ShowAsync(BlockedScreenController.IssueCommand(new BlockedScreenParameters(banStatusData.ban)), ct);
}
}
}
}

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

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -40}
m_AnchoredPosition: {x: 0, y: -130}
m_SizeDelta: {x: 394, y: 24}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &7914235620027229274
Expand Down Expand Up @@ -103,6 +103,7 @@ MonoBehaviour:
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
Expand Down Expand Up @@ -372,11 +373,11 @@ RectTransform:
- {fileID: 358903991130365097}
m_Father: {fileID: 5733635956930358370}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 90, y: -177}
m_SizeDelta: {x: 180, y: 46}
m_Pivot: {x: 0.5, y: 0.5}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 102, y: 69.99997}
m_SizeDelta: {x: 190, y: 46}
m_Pivot: {x: 0, y: 0}
--- !u!222 &2674844391225083648
CanvasRenderer:
m_ObjectHideFlags: 0
Expand Down Expand Up @@ -608,11 +609,11 @@ RectTransform:
- {fileID: 7535037324065951718}
m_Father: {fileID: 5733635956930358370}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -90, y: -177}
m_SizeDelta: {x: 180, y: 46}
m_Pivot: {x: 0.5, y: 0.5}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -102, y: 70}
m_SizeDelta: {x: 190, y: 46}
m_Pivot: {x: 1, y: 0}
--- !u!222 &650203673732180744
CanvasRenderer:
m_ObjectHideFlags: 0
Expand Down Expand Up @@ -868,7 +869,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 542, y: 349.3}
m_SizeDelta: {x: 600, y: 407}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &3225889317316081314
GameObject:
Expand Down Expand Up @@ -974,6 +975,7 @@ MonoBehaviour:
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
Expand Down Expand Up @@ -1086,7 +1088,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -160, y: -160}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &3686250347175148123
GameObject:
Expand Down Expand Up @@ -1455,6 +1457,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
<canvas>k__BackingField: {fileID: 2814871423585080172}
<raycaster>k__BackingField: {fileID: 8454101078806804241}
<InfoText>k__BackingField: {fileID: 8007467556033942281}
<CloseButton>k__BackingField: {fileID: 4720656920351021949}
<SupportButton>k__BackingField: {fileID: 6676896487512417843}
--- !u!1 &5648600980457702302
Expand Down Expand Up @@ -1490,11 +1493,11 @@ RectTransform:
m_Children: []
m_Father: {fileID: 5733635956930358370}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -88.7}
m_SizeDelta: {x: 396, y: 23.3795}
m_Pivot: {x: 0.5, y: 1}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -10.5}
m_SizeDelta: {x: 396, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &454820469704838997
CanvasRenderer:
m_ObjectHideFlags: 0
Expand Down Expand Up @@ -1550,25 +1553,26 @@ MonoBehaviour:
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 16
m_fontSize: 18
m_fontSizeBase: 16
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_enableAutoSizing: 1
m_fontSizeMin: 5
m_fontSizeMax: 18
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 256
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_overflowMode: 1
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
Expand Down Expand Up @@ -1738,6 +1742,7 @@ MonoBehaviour:
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_characterHorizontalScale: 1
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
Expand Down Expand Up @@ -2263,8 +2268,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: 30}
m_SizeDelta: {x: 56, y: 56}
m_AnchoredPosition: {x: 0, y: -56}
m_SizeDelta: {x: 62, y: 62}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &2617928126541901870
CanvasRenderer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
using DCL.Multiplayer.Connections.DecentralandUrls;
using DCL.Utility;
using MVC;
using System;
using System.Globalization;
using System.Text;
using System.Threading;

namespace DCL.ApplicationBlocklistGuard
{
public class BlockedScreenController : ControllerBase<BlockedScreenView>
public class BlockedScreenController : ControllerBase<BlockedScreenView, BlockedScreenParameters>
{
private const string DEFAULT_INFO_TEXT = "Please contact support team for more information.";

private readonly IWebBrowser webBrowser;
private readonly StringBuilder infoTextBuilder = new ();

public override CanvasOrdering.SortingLayer Layer => CanvasOrdering.SortingLayer.Overlay;

public BlockedScreenController(ViewFactoryMethod viewFactory, IWebBrowser webBrowser) : base(viewFactory)
Expand All @@ -26,8 +33,28 @@ protected override void OnViewInstantiated()
}
}

protected override void OnBeforeViewShow()
{
infoTextBuilder.Clear();

if (inputData.BannedUserData != null && !string.IsNullOrEmpty(inputData.BannedUserData.expiresAt))
{
infoTextBuilder.Append("Ban period: ");
infoTextBuilder.Append(FormatRemainingBanTime(inputData.BannedUserData.expiresAt));
infoTextBuilder.AppendLine();
infoTextBuilder.Append("Reason: ");
infoTextBuilder.Append(inputData.BannedUserData.reason);
infoTextBuilder.AppendLine();
}
infoTextBuilder.Append(DEFAULT_INFO_TEXT);

viewInstance!.InfoText.text = infoTextBuilder.ToString();
}

public override void Dispose()
{
infoTextBuilder.Clear();

if (viewInstance == null)
return;

Expand All @@ -40,6 +67,27 @@ private void OnSupportClicked()
webBrowser.OpenUrl(DecentralandUrl.Help);
}

private static string FormatRemainingBanTime(string expiresAt)
{
if (!DateTime.TryParse(expiresAt, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTime expirationUtc))
return expiresAt;

TimeSpan remaining = expirationUtc - DateTime.UtcNow;

if (remaining.TotalSeconds <= 0)
return "expired";

var hours = (int)Math.Ceiling(remaining.TotalHours);

if (hours >= 24)
{
var days = (int)Math.Ceiling(remaining.TotalDays);
return days == 1 ? "1 day" : $"{days} days";
}

return hours <= 1 ? "1h" : $"{hours}h";
}

protected override UniTask WaitForCloseIntentAsync(CancellationToken ct) =>
UniTask.Never(ct);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace DCL.ApplicationBlocklistGuard
{
public struct BlockedScreenParameters
{
public readonly BannedUserData? BannedUserData;

public BlockedScreenParameters(BannedUserData? bannedUserData)
{
BannedUserData = bannedUserData;
}
}
}
Loading
Loading