add permission level changed meta
This commit is contained in:
parent
18f3c59b9b
commit
39a1066c74
@ -417,6 +417,7 @@ namespace IW4MAdmin.Application
|
|||||||
UpdatedAliasResourceQueryHelper>()
|
UpdatedAliasResourceQueryHelper>()
|
||||||
.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>()
|
.AddSingleton<IResourceQueryHelper<ChatSearchQuery, MessageResponse>, ChatResourceQueryHelper>()
|
||||||
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>, ConnectionsResourceQueryHelper>()
|
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>, ConnectionsResourceQueryHelper>()
|
||||||
|
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, PermissionLevelChangedResponse>, PermissionLevelChangedResourceQueryHelper>()
|
||||||
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
||||||
.AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>()
|
.AddSingleton<IRemoteAssemblyHandler, RemoteAssemblyHandler>()
|
||||||
.AddSingleton<IMasterCommunication, MasterCommunication>()
|
.AddSingleton<IMasterCommunication, MasterCommunication>()
|
||||||
|
@ -22,12 +22,16 @@ namespace IW4MAdmin.Application.Meta
|
|||||||
private readonly IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> _updatedAliasHelper;
|
private readonly IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> _updatedAliasHelper;
|
||||||
private readonly IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>
|
private readonly IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse>
|
||||||
_connectionHistoryHelper;
|
_connectionHistoryHelper;
|
||||||
|
private readonly IResourceQueryHelper<ClientPaginationRequest, PermissionLevelChangedResponse>
|
||||||
|
_permissionLevelHelper;
|
||||||
|
|
||||||
public MetaRegistration(ILogger<MetaRegistration> logger, IMetaService metaService, ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService,
|
public MetaRegistration(ILogger<MetaRegistration> logger, IMetaService metaService,
|
||||||
|
ITranslationLookup transLookup, IEntityService<EFClient> clientEntityService,
|
||||||
IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper,
|
IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse> receivedPenaltyHelper,
|
||||||
IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper,
|
IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse> administeredPenaltyHelper,
|
||||||
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper,
|
IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse> updatedAliasHelper,
|
||||||
IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse> connectionHistoryHelper)
|
IResourceQueryHelper<ClientPaginationRequest, ConnectionHistoryResponse> connectionHistoryHelper,
|
||||||
|
IResourceQueryHelper<ClientPaginationRequest, PermissionLevelChangedResponse> permissionLevelHelper)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_transLookup = transLookup;
|
_transLookup = transLookup;
|
||||||
@ -37,6 +41,7 @@ namespace IW4MAdmin.Application.Meta
|
|||||||
_administeredPenaltyHelper = administeredPenaltyHelper;
|
_administeredPenaltyHelper = administeredPenaltyHelper;
|
||||||
_updatedAliasHelper = updatedAliasHelper;
|
_updatedAliasHelper = updatedAliasHelper;
|
||||||
_connectionHistoryHelper = connectionHistoryHelper;
|
_connectionHistoryHelper = connectionHistoryHelper;
|
||||||
|
_permissionLevelHelper = permissionLevelHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Register()
|
public void Register()
|
||||||
@ -46,6 +51,8 @@ namespace IW4MAdmin.Application.Meta
|
|||||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, AdministeredPenaltyResponse>(MetaType.Penalized, GetAdministeredPenaltiesMeta);
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, AdministeredPenaltyResponse>(MetaType.Penalized, GetAdministeredPenaltiesMeta);
|
||||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, UpdatedAliasResponse>(MetaType.AliasUpdate, GetUpdatedAliasMeta);
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, UpdatedAliasResponse>(MetaType.AliasUpdate, GetUpdatedAliasMeta);
|
||||||
_metaService.AddRuntimeMeta<ClientPaginationRequest, ConnectionHistoryResponse>(MetaType.ConnectionHistory, GetConnectionHistoryMeta);
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, ConnectionHistoryResponse>(MetaType.ConnectionHistory, GetConnectionHistoryMeta);
|
||||||
|
_metaService.AddRuntimeMeta<ClientPaginationRequest, PermissionLevelChangedResponse>(
|
||||||
|
MetaType.PermissionLevel, GetPermissionLevelMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<InformationResponse>> GetProfileMeta(ClientPaginationRequest request)
|
private async Task<IEnumerable<InformationResponse>> GetProfileMeta(ClientPaginationRequest request)
|
||||||
@ -174,5 +181,12 @@ namespace IW4MAdmin.Application.Meta
|
|||||||
var connections = await _connectionHistoryHelper.QueryResource(request);
|
var connections = await _connectionHistoryHelper.QueryResource(request);
|
||||||
return connections.Results;
|
return connections.Results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<PermissionLevelChangedResponse>> GetPermissionLevelMeta(
|
||||||
|
ClientPaginationRequest request)
|
||||||
|
{
|
||||||
|
var permissionChanges = await _permissionLevelHelper.QueryResource(request);
|
||||||
|
return permissionChanges.Results;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Data.Abstractions;
|
||||||
|
using Data.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||||
|
using SharedLibraryCore.Helpers;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using SharedLibraryCore.QueryHelper;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.Meta;
|
||||||
|
|
||||||
|
public class
|
||||||
|
PermissionLevelChangedResourceQueryHelper : IResourceQueryHelper<ClientPaginationRequest,
|
||||||
|
PermissionLevelChangedResponse>
|
||||||
|
{
|
||||||
|
private readonly IDatabaseContextFactory _contextFactory;
|
||||||
|
|
||||||
|
public PermissionLevelChangedResourceQueryHelper(IDatabaseContextFactory contextFactory)
|
||||||
|
{
|
||||||
|
_contextFactory = contextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ResourceQueryHelperResult<PermissionLevelChangedResponse>> QueryResource(
|
||||||
|
ClientPaginationRequest query)
|
||||||
|
{
|
||||||
|
await using var context = _contextFactory.CreateContext();
|
||||||
|
|
||||||
|
var auditEntries = context.EFChangeHistory.Where(change => change.TargetEntityId == query.ClientId)
|
||||||
|
.Where(change => change.TypeOfChange == EFChangeHistory.ChangeType.Permission);
|
||||||
|
|
||||||
|
var audits = from change in auditEntries
|
||||||
|
join client in context.Clients
|
||||||
|
on change.OriginEntityId equals client.ClientId
|
||||||
|
select new PermissionLevelChangedResponse
|
||||||
|
{
|
||||||
|
ChangedById = change.OriginEntityId,
|
||||||
|
ChangedByName = client.CurrentAlias.Name,
|
||||||
|
PreviousPermissionLevelValue = change.PreviousValue,
|
||||||
|
CurrentPermissionLevelValue = change.CurrentValue,
|
||||||
|
When = change.TimeChanged,
|
||||||
|
ClientId = change.TargetEntityId
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ResourceQueryHelperResult<PermissionLevelChangedResponse>
|
||||||
|
{
|
||||||
|
Results = await audits.Skip(query.Offset).Take(query.Count).ToListAsync()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using Data.Models.Client;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Dtos.Meta.Responses;
|
||||||
|
|
||||||
|
public class PermissionLevelChangedResponse : BaseMetaResponse
|
||||||
|
{
|
||||||
|
public EFClient.Permission PreviousPermissionLevel =>
|
||||||
|
(EFClient.Permission)Enum.Parse(typeof(EFClient.Permission),
|
||||||
|
PreviousPermissionLevelValue ?? EFClient.Permission.User.ToString());
|
||||||
|
|
||||||
|
public string PreviousPermissionLevelValue { get; set; }
|
||||||
|
|
||||||
|
public EFClient.Permission CurrentPermissionLevel => (EFClient.Permission)Enum.Parse(typeof(EFClient.Permission),
|
||||||
|
CurrentPermissionLevelValue ?? EFClient.Permission.User.ToString());
|
||||||
|
|
||||||
|
public string CurrentPermissionLevelValue { get; set; }
|
||||||
|
public int ChangedById { get; set; }
|
||||||
|
public string ChangedByName { get; set; }
|
||||||
|
}
|
@ -27,6 +27,7 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
Penalized,
|
Penalized,
|
||||||
ReceivedPenalty,
|
ReceivedPenalty,
|
||||||
QuickMessage,
|
QuickMessage,
|
||||||
ConnectionHistory
|
ConnectionHistory,
|
||||||
|
PermissionLevel,
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -69,6 +69,10 @@ namespace WebfrontCore.ViewComponents
|
|||||||
case MetaType.ConnectionHistory:
|
case MetaType.ConnectionHistory:
|
||||||
meta = await metaService.GetRuntimeMeta<ConnectionHistoryResponse>(request, metaType.Value);
|
meta = await metaService.GetRuntimeMeta<ConnectionHistoryResponse>(request, metaType.Value);
|
||||||
break;
|
break;
|
||||||
|
case MetaType.PermissionLevel:
|
||||||
|
meta = await metaService.GetRuntimeMeta<PermissionLevelChangedResponse>(request,
|
||||||
|
metaType.Value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
var match = System.Text.RegularExpressions.Regex.Match(Model.Name.ToUpper(), "[A-Z]").Value;
|
var match = System.Text.RegularExpressions.Regex.Match(Model.Name.ToUpper(), "[A-Z]").Value;
|
||||||
var shortCode = match == string.Empty ? "?" : match;
|
var shortCode = match == string.Empty ? "?" : match;
|
||||||
var gravatarUrl = Model.Meta.FirstOrDefault(m => m.Key == "GravatarEmail")?.Value;
|
var gravatarUrl = Model.Meta.FirstOrDefault(m => m.Key == "GravatarEmail")?.Value;
|
||||||
var isFlagged = Model.LevelInt == (int) EFClient.Permission.Flagged;
|
var isFlagged = Model.LevelInt == (int)EFClient.Permission.Flagged;
|
||||||
var isPermBanned = Model.LevelInt == (int) EFClient.Permission.Banned;
|
var isPermBanned = Model.LevelInt == (int)EFClient.Permission.Banned;
|
||||||
var isTempBanned = Model.ActivePenalty?.Type == EFPenalty.PenaltyType.TempBan;
|
var isTempBanned = Model.ActivePenalty?.Type == EFPenalty.PenaltyType.TempBan;
|
||||||
var translationKey = $"WEBFRONT_PROFILE_{Model.ActivePenalty?.Type.ToString().ToUpper()}_INFO";
|
var translationKey = $"WEBFRONT_PROFILE_{Model.ActivePenalty?.Type.ToString().ToUpper()}_INFO";
|
||||||
var ignoredMetaTypes = new[] {MetaType.Information, MetaType.Other, MetaType.QuickMessage};
|
var ignoredMetaTypes = new[] { MetaType.Information, MetaType.Other, MetaType.QuickMessage };
|
||||||
}
|
}
|
||||||
|
|
||||||
<div id="profile_wrapper" class="pb-3 row d-flex flex-column flex-lg-row">
|
<div id="profile_wrapper" class="pb-3 row d-flex flex-column flex-lg-row">
|
||||||
@ -44,7 +44,7 @@
|
|||||||
@foreach (var linked in Model.LinkedAccounts)
|
@foreach (var linked in Model.LinkedAccounts)
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
@Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new {id = linked.Key}, new {@class = "link-inverse"})
|
@Html.ActionLink(linked.Value.ToString("X"), "ProfileAsync", "Client", new { id = linked.Key }, new { @class = "link-inverse" })
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@foreach (var alias in Model.Aliases)
|
@foreach (var alias in Model.Aliases)
|
||||||
@ -110,12 +110,12 @@
|
|||||||
<div class="profile-action oi oi-flag h3 ml-2 @(isFlagged ? "text-secondary" : "text-success")" data-action="@(isFlagged ? "unflag" : "flag")" aria-hidden="true"></div>
|
<div class="profile-action oi oi-flag h3 ml-2 @(isFlagged ? "text-secondary" : "text-success")" data-action="@(isFlagged ? "unflag" : "flag")" aria-hidden="true"></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (Model.LevelInt < (int) ViewBag.User.Level && !Model.HasActivePenalty)
|
@if (Model.LevelInt < (int)ViewBag.User.Level && !Model.HasActivePenalty)
|
||||||
{
|
{
|
||||||
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
<div id="profile_action_ban_btn" class="profile-action oi oi-lock-unlocked text-success h3 ml-2" title="Ban Client" data-action="ban" aria-hidden="true"></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (Model.LevelInt < (int) ViewBag.User.Level && Model.HasActivePenalty)
|
@if (Model.LevelInt < (int)ViewBag.User.Level && Model.HasActivePenalty)
|
||||||
{
|
{
|
||||||
@if (isTempBanned)
|
@if (isTempBanned)
|
||||||
{
|
{
|
||||||
@ -145,31 +145,46 @@
|
|||||||
<partial name="Meta/_Information.cshtml" model="@Model.Meta"/>
|
<partial name="Meta/_Information.cshtml" model="@Model.Meta"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row border-bottom">
|
<div class="row border-bottom bg-dark">
|
||||||
<div class="text-center bg-dark p-2 pl-3 pr-4 text-muted col-12 col-md-auto" id="filter_meta_container_button">
|
<div class="d-md-flex flex-fill align-items-center bg-dark">
|
||||||
<span class="oi oi-sort-ascending"></span>
|
<div class="text-center bg-dark p-2 pl-3 pr-4 text-muted" id="filter_meta_container_button">
|
||||||
<a>@ViewBag.Localization["WEBFRONT_CLIENT_META_FILTER"]</a>
|
<span class="oi oi-sort-ascending"></span>
|
||||||
</div>
|
<a>@ViewBag.Localization["WEBFRONT_CLIENT_META_FILTER"]</a>
|
||||||
<div class="d-none d-md-flex flex-fill" id="filter_meta_container">
|
</div>
|
||||||
<a asp-action="ProfileAsync" asp-controller="Client"
|
<div id="filter_meta_container" class="d-none d-md-flex flex-md-fill flex-md-wrap">
|
||||||
class="nav-link p-2 pl-3 pr-3 text-center col-12 col-md-auto text-md-left @(!Model.MetaFilterType.HasValue ? "btn-primary text-white" : "text-muted")"
|
<a asp-action="ProfileAsync" asp-controller="Client"
|
||||||
asp-route-id="@Model.ClientId">
|
class="nav-link p-2 pl-3 pr-3 text-center col-12 col-md-auto text-md-left @(!Model.MetaFilterType.HasValue ? "btn-primary text-white" : "text-muted")"
|
||||||
@ViewBag.Localization["META_TYPE_ALL_NAME"]
|
asp-route-id="@Model.ClientId">
|
||||||
</a>
|
@ViewBag.Localization["META_TYPE_ALL_NAME"]
|
||||||
|
</a>
|
||||||
@foreach (MetaType type in Enum.GetValues(typeof(MetaType)))
|
@{ var metaTypes = Enum.GetValues(typeof(MetaType))
|
||||||
{
|
.Cast<MetaType>()
|
||||||
if (!ignoredMetaTypes.Contains(type))
|
.Where(type => !ignoredMetaTypes.Contains(type))
|
||||||
|
.ToList(); }
|
||||||
|
@foreach (var type in metaTypes.Take(4))
|
||||||
{
|
{
|
||||||
<a asp-action="ProfileAsync" asp-controller="Client"
|
<a asp-action="ProfileAsync" asp-controller="Client"
|
||||||
class="nav-link p-2 pl-3 pr-3 text-center col-12 col-md-auto text-md-left @(Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-white" : "text-muted")"
|
class="meta-filter nav-link p-2 pl-3 pr-3 text-center @(Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-white" : "text-muted")"
|
||||||
asp-route-id="@Model.ClientId"
|
asp-route-id="@Model.ClientId"
|
||||||
asp-route-metaFilterType="@type"
|
asp-route-metaFilterType="@type"
|
||||||
data-meta-type="@type">
|
data-meta-type="@type">
|
||||||
@type.ToTranslatedName()
|
@type.ToTranslatedName()
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
}
|
<a href="#" class="nav-link p-2 pl-3 pr-3 text-center text-muted d-none d-md-block" id="expand-meta-filters">...</a>
|
||||||
|
<div class="d-block d-md-none" id="additional-meta-filter">
|
||||||
|
@foreach (var type in metaTypes.Skip(4))
|
||||||
|
{
|
||||||
|
<a asp-action="ProfileAsync" asp-controller="Client"
|
||||||
|
class="meta-filter nav-link p-2 pl-3 pr-3 text-center @(Model.MetaFilterType.HasValue && Model.MetaFilterType.Value.ToString() == type.ToString() ? "btn-primary text-white" : "text-muted")"
|
||||||
|
asp-route-id="@Model.ClientId"
|
||||||
|
asp-route-metaFilterType="@type"
|
||||||
|
data-meta-type="@type">
|
||||||
|
@type.ToTranslatedName()
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -177,7 +192,7 @@
|
|||||||
{
|
{
|
||||||
<div class="row d-md-flex pt-2">
|
<div class="row d-md-flex pt-2">
|
||||||
<div id="profile_events" class="text-muted text-left pl-4 pr-4 pl-md-0 pr-md-0">
|
<div id="profile_events" class="text-muted text-left pl-4 pr-4 pl-md-0 pr-md-0">
|
||||||
@await Component.InvokeAsync("ProfileMetaList", new {clientId = Model.ClientId, count = 30, offset = 0, startAt = DateTime.UtcNow, metaType = Model.MetaFilterType})
|
@await Component.InvokeAsync("ProfileMetaList", new { clientId = Model.ClientId, count = 30, offset = 0, startAt = DateTime.UtcNow, metaType = Model.MetaFilterType })
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
@model SharedLibraryCore.Dtos.Meta.Responses.PermissionLevelChangedResponse
|
||||||
|
|
||||||
|
@foreach (var token in Utilities.SplitTranslationTokens("WEBFRONT_CLIENT_META_PERMISSION_CHANGED"))
|
||||||
|
{
|
||||||
|
if (token.IsInterpolation)
|
||||||
|
{
|
||||||
|
switch (token.MatchValue)
|
||||||
|
{
|
||||||
|
case "permission":
|
||||||
|
<span class="level-color-@((int)Model.CurrentPermissionLevel)">@Model.CurrentPermissionLevel.ToLocalizedLevelName()</span>
|
||||||
|
break;
|
||||||
|
case "originClient":
|
||||||
|
<span class="text-highlight">
|
||||||
|
<a class="link-inverse" href="@Model.ChangedById">
|
||||||
|
<color-code value="@Model.ChangedByName"></color-code>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
break;
|
||||||
|
case "type":
|
||||||
|
<span class="text-white-50">@token.TranslationValue</span>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="text-muted">@token.MatchValue</span>
|
||||||
|
}
|
||||||
|
}
|
@ -35,12 +35,16 @@
|
|||||||
startAt = $('.loader-data-time').last().data('time');
|
startAt = $('.loader-data-time').last().data('time');
|
||||||
|
|
||||||
$('#filter_meta_container_button').click(function () {
|
$('#filter_meta_container_button').click(function () {
|
||||||
$('#filter_meta_container').hide();
|
$('#filter_meta_container').hide().removeClass('d-none').addClass('d-block').slideDown();
|
||||||
$('#filter_meta_container').removeClass('d-none');
|
$('#additional-meta-filter').removeClass('d-md-none').addClass('d-flex').slideDown();
|
||||||
$('#filter_meta_container').addClass('d-block');
|
$('#expand-meta-filters').removeClass('d-md-block');
|
||||||
$('#filter_meta_container').slideDown();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#expand-meta-filters').click(function () {
|
||||||
|
$('#additional-meta-filter').removeClass('d-md-none').addClass('d-flex').slideDown();
|
||||||
|
$('#expand-meta-filters').removeClass('d-md-block');
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* load context of chat
|
* load context of chat
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user