availabilityStatus info from websocket
This commit is contained in:
parent
2b15faf4e8
commit
a566f8d661
@ -145,7 +145,7 @@ export class GameRoom {
|
||||
joinRoomMessage.getIpaddress(),
|
||||
position,
|
||||
this.positionNotifier,
|
||||
joinRoomMessage.getStatus(),
|
||||
joinRoomMessage.getAvailabilitystatus(),
|
||||
socket,
|
||||
joinRoomMessage.getTagList(),
|
||||
joinRoomMessage.getVisitcardurl(),
|
||||
|
@ -32,7 +32,7 @@ export class User implements Movable {
|
||||
public readonly IPAddress: string,
|
||||
private position: PointInterface,
|
||||
private positionNotifier: PositionNotifier,
|
||||
private status: AvailabilityStatus,
|
||||
private availabilityStatus: AvailabilityStatus,
|
||||
public readonly socket: UserSocket,
|
||||
public readonly tags: string[],
|
||||
public readonly visitCardUrl: string | null,
|
||||
@ -90,12 +90,15 @@ export class User implements Movable {
|
||||
return this.outlineColor;
|
||||
}
|
||||
|
||||
public getStatus(): AvailabilityStatus {
|
||||
return this.status;
|
||||
public getAvailabilityStatus(): AvailabilityStatus {
|
||||
return this.availabilityStatus;
|
||||
}
|
||||
|
||||
public get silent(): boolean {
|
||||
return this.status === AvailabilityStatus.SILENT || this.status === AvailabilityStatus.JITSI;
|
||||
return (
|
||||
this.availabilityStatus === AvailabilityStatus.SILENT ||
|
||||
this.availabilityStatus === AvailabilityStatus.JITSI
|
||||
);
|
||||
}
|
||||
|
||||
get following(): User | undefined {
|
||||
@ -138,10 +141,10 @@ export class User implements Movable {
|
||||
}
|
||||
this.voiceIndicatorShown = details.getShowvoiceindicator()?.getValue();
|
||||
|
||||
const status = details.getStatus();
|
||||
const availabilityStatus = details.getAvailabilitystatus();
|
||||
let sendStatusUpdate = false;
|
||||
if (status && status !== this.status) {
|
||||
this.status = status;
|
||||
if (availabilityStatus && availabilityStatus !== this.availabilityStatus) {
|
||||
this.availabilityStatus = availabilityStatus;
|
||||
sendStatusUpdate = true;
|
||||
}
|
||||
|
||||
@ -157,7 +160,7 @@ export class User implements Movable {
|
||||
playerDetails.setShowvoiceindicator(new BoolValue().setValue(this.voiceIndicatorShown));
|
||||
}
|
||||
if (sendStatusUpdate) {
|
||||
playerDetails.setStatus(details.getStatus());
|
||||
playerDetails.setAvailabilitystatus(details.getAvailabilitystatus());
|
||||
}
|
||||
|
||||
this.positionNotifier.updatePlayerDetails(this, playerDetails);
|
||||
|
@ -323,7 +323,7 @@ export class SocketManager {
|
||||
userJoinedZoneMessage.setUserid(thing.id);
|
||||
userJoinedZoneMessage.setUseruuid(thing.uuid);
|
||||
userJoinedZoneMessage.setName(thing.name);
|
||||
userJoinedZoneMessage.setStatus(thing.getStatus());
|
||||
userJoinedZoneMessage.setAvailabilitystatus(thing.getAvailabilityStatus());
|
||||
userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
||||
userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
||||
userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone));
|
||||
@ -651,7 +651,7 @@ export class SocketManager {
|
||||
userJoinedMessage.setUserid(thing.id);
|
||||
userJoinedMessage.setUseruuid(thing.uuid);
|
||||
userJoinedMessage.setName(thing.name);
|
||||
userJoinedMessage.setStatus(thing.getStatus());
|
||||
userJoinedMessage.setAvailabilitystatus(thing.getAvailabilityStatus());
|
||||
userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers));
|
||||
userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition()));
|
||||
if (thing.visitCardUrl) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {RoomConnection} from "../front/src/Connexion/RoomConnection";
|
||||
import {connectionManager} from "../front/src/Connexion/ConnectionManager";
|
||||
import * as WebSocket from "ws"
|
||||
import { AvailabilityStatus } from '../front/src/Messages/ts-proto-generated/protos/messages';
|
||||
|
||||
let userMovedCount = 0;
|
||||
|
||||
@ -22,7 +23,9 @@ async function startOneUser(): Promise<void> {
|
||||
bottom: 200,
|
||||
left: 500,
|
||||
right: 800
|
||||
}, null);
|
||||
},
|
||||
null,
|
||||
AvailabilityStatus.ONLINE);
|
||||
|
||||
const connection = onConnect.connection;
|
||||
|
||||
|
@ -19,6 +19,7 @@ import { gameManager } from "../Phaser/Game/GameManager";
|
||||
import { locales } from "../i18n/i18n-util";
|
||||
import type { Locales } from "../i18n/i18n-types";
|
||||
import { setCurrentLocale } from "../i18n/locales";
|
||||
import { AvailabilityStatus } from "../Messages/ts-proto-generated/protos/messages";
|
||||
|
||||
class ConnectionManager {
|
||||
private localUser!: LocalUser;
|
||||
@ -276,7 +277,8 @@ class ConnectionManager {
|
||||
characterLayers: string[],
|
||||
position: PositionInterface,
|
||||
viewport: ViewportInterface,
|
||||
companion: string | null
|
||||
companion: string | null,
|
||||
availabilityStatus: AvailabilityStatus
|
||||
): Promise<OnConnectInterface> {
|
||||
return new Promise<OnConnectInterface>((resolve, reject) => {
|
||||
const connection = new RoomConnection(
|
||||
@ -286,7 +288,8 @@ class ConnectionManager {
|
||||
characterLayers,
|
||||
position,
|
||||
viewport,
|
||||
companion
|
||||
companion,
|
||||
availabilityStatus
|
||||
);
|
||||
|
||||
connection.onConnectError((error: object) => {
|
||||
@ -340,9 +343,15 @@ class ConnectionManager {
|
||||
this.reconnectingTimeout = setTimeout(() => {
|
||||
//todo: allow a way to break recursion?
|
||||
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely.
|
||||
void this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then(
|
||||
(connection) => resolve(connection)
|
||||
);
|
||||
void this.connectToRoomSocket(
|
||||
roomUrl,
|
||||
name,
|
||||
characterLayers,
|
||||
position,
|
||||
viewport,
|
||||
companion,
|
||||
availabilityStatus
|
||||
).then((connection) => resolve(connection));
|
||||
}, 4000 + Math.floor(Math.random() * 2000));
|
||||
});
|
||||
});
|
||||
|
@ -15,7 +15,7 @@ export interface MessageUserPositionInterface {
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
status: AvailabilityStatus;
|
||||
availabilityStatus: AvailabilityStatus;
|
||||
visitCardUrl: string | null;
|
||||
companion: string | null;
|
||||
userUuid: string;
|
||||
@ -31,7 +31,7 @@ export interface MessageUserJoined {
|
||||
name: string;
|
||||
characterLayers: BodyResourceDescriptionInterface[];
|
||||
position: PointInterface;
|
||||
status: AvailabilityStatus;
|
||||
availabilityStatus: AvailabilityStatus;
|
||||
visitCardUrl: string | null;
|
||||
companion: string | null;
|
||||
userUuid: string;
|
||||
|
@ -154,6 +154,7 @@ export class RoomConnection implements RoomConnection {
|
||||
* @param position
|
||||
* @param viewport
|
||||
* @param companion
|
||||
* @param availabilityStatus
|
||||
*/
|
||||
public constructor(
|
||||
token: string | null,
|
||||
@ -162,7 +163,8 @@ export class RoomConnection implements RoomConnection {
|
||||
characterLayers: string[],
|
||||
position: PositionInterface,
|
||||
viewport: ViewportInterface,
|
||||
companion: string | null
|
||||
companion: string | null,
|
||||
availabilityStatus: AvailabilityStatus
|
||||
) {
|
||||
let url = new URL(PUSHER_URL, window.location.toString()).toString();
|
||||
url = url.replace("http://", "ws://").replace("https://", "wss://");
|
||||
@ -185,6 +187,9 @@ export class RoomConnection implements RoomConnection {
|
||||
if (typeof companion === "string") {
|
||||
url += "&companion=" + encodeURIComponent(companion);
|
||||
}
|
||||
if (typeof availabilityStatus === "number") {
|
||||
url += "&availabilityStatus=" + availabilityStatus;
|
||||
}
|
||||
|
||||
if (RoomConnection.websocketFactory) {
|
||||
this.socket = RoomConnection.websocketFactory(url);
|
||||
@ -537,9 +542,9 @@ export class RoomConnection implements RoomConnection {
|
||||
this.socket.send(bytes);
|
||||
}
|
||||
|
||||
public emitPlayerStatusChange(status: AvailabilityStatus): void {
|
||||
public emitPlayerStatusChange(availabilityStatus: AvailabilityStatus): void {
|
||||
const message = SetPlayerDetailsMessageTsProto.fromPartial({
|
||||
status,
|
||||
availabilityStatus,
|
||||
});
|
||||
const bytes = ClientToServerMessageTsProto.encode({
|
||||
message: {
|
||||
@ -673,7 +678,7 @@ export class RoomConnection implements RoomConnection {
|
||||
characterLayers,
|
||||
visitCardUrl: message.visitCardUrl,
|
||||
position: ProtobufClientUtils.toPointInterface(position),
|
||||
status: message.status,
|
||||
availabilityStatus: message.availabilityStatus,
|
||||
companion: companion ? companion.name : null,
|
||||
userUuid: message.userUuid,
|
||||
outlineColor: message.hasOutline ? message.outlineColor : undefined,
|
||||
|
@ -5,7 +5,7 @@ export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
||||
private statusImage: Phaser.GameObjects.Image;
|
||||
private statusImageOutline: Phaser.GameObjects.Image;
|
||||
|
||||
private status: AvailabilityStatus;
|
||||
private availabilityStatus: AvailabilityStatus;
|
||||
|
||||
private readonly COLORS: Record<AvailabilityStatus, { filling: number; outline: number }> = {
|
||||
[AvailabilityStatus.AWAY]: { filling: 0xf5931e, outline: 0x875d13 },
|
||||
@ -19,7 +19,7 @@ export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
||||
constructor(scene: Phaser.Scene, x: number, y: number) {
|
||||
super(scene, x, y);
|
||||
|
||||
this.status = AvailabilityStatus.ONLINE;
|
||||
this.availabilityStatus = AvailabilityStatus.ONLINE;
|
||||
|
||||
this.statusImage = this.scene.add.image(0, 0, "iconStatusIndicatorInside");
|
||||
this.statusImageOutline = this.scene.add.image(0, 0, "iconStatusIndicatorOutline");
|
||||
@ -31,11 +31,11 @@ export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
||||
this.scene.add.existing(this);
|
||||
}
|
||||
|
||||
public setStatus(status: AvailabilityStatus, instant: boolean = false): void {
|
||||
if (this.status === status || status === AvailabilityStatus.UNCHANGED) {
|
||||
public setAvailabilityStatus(availabilityStatus: AvailabilityStatus, instant: boolean = false): void {
|
||||
if (this.availabilityStatus === availabilityStatus || availabilityStatus === AvailabilityStatus.UNCHANGED) {
|
||||
return;
|
||||
}
|
||||
this.status = status;
|
||||
this.availabilityStatus = availabilityStatus;
|
||||
if (instant) {
|
||||
this.redraw();
|
||||
} else {
|
||||
@ -61,7 +61,7 @@ export class PlayerStatusDot extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
private redraw(): void {
|
||||
const colors = this.COLORS[this.status];
|
||||
const colors = this.COLORS[this.availabilityStatus];
|
||||
this.statusImage.setTintFill(colors.filling);
|
||||
this.statusImageOutline.setTintFill(colors.outline);
|
||||
}
|
||||
|
@ -236,8 +236,8 @@ export abstract class Character extends Container implements OutlineableInterfac
|
||||
this.talkIcon.show(show, forceClose);
|
||||
}
|
||||
|
||||
public setStatus(status: AvailabilityStatus, instant: boolean = false): void {
|
||||
this.statusDot.setStatus(status, instant);
|
||||
public setAvailabilityStatus(availabilityStatus: AvailabilityStatus, instant: boolean = false): void {
|
||||
this.statusDot.setAvailabilityStatus(availabilityStatus, instant);
|
||||
}
|
||||
|
||||
public addCompanion(name: string, texturePromise?: CancelablePromise<string>): void {
|
||||
|
@ -687,7 +687,8 @@ export class GameScene extends DirtyScene {
|
||||
right: camera.scrollX + camera.width,
|
||||
bottom: camera.scrollY + camera.height,
|
||||
},
|
||||
this.companion
|
||||
this.companion,
|
||||
get(availabilityStatusStore)
|
||||
)
|
||||
.then((onConnect: OnConnectInterface) => {
|
||||
this.connection = onConnect.connection;
|
||||
@ -711,7 +712,7 @@ export class GameScene extends DirtyScene {
|
||||
characterLayers: message.characterLayers,
|
||||
name: message.name,
|
||||
position: message.position,
|
||||
status: message.status,
|
||||
availabilityStatus: message.availabilityStatus,
|
||||
visitCardUrl: message.visitCardUrl,
|
||||
companion: message.companion,
|
||||
userUuid: message.userUuid,
|
||||
@ -889,9 +890,10 @@ export class GameScene extends DirtyScene {
|
||||
this.tryChangeShowVoiceIndicatorState(this.jitsiDominantSpeaker && this.jitsiParticipantsCount > 1);
|
||||
});
|
||||
|
||||
this.availabilityStatusStoreUnsubscriber = availabilityStatusStore.subscribe((status) => {
|
||||
this.connection?.emitPlayerStatusChange(status);
|
||||
this.CurrentPlayer.setStatus(status);
|
||||
this.availabilityStatusStoreUnsubscriber = availabilityStatusStore.subscribe((availabilityStatus) => {
|
||||
console.log(availabilityStatus);
|
||||
this.connection?.emitPlayerStatusChange(availabilityStatus);
|
||||
this.CurrentPlayer.setAvailabilityStatus(availabilityStatus);
|
||||
});
|
||||
|
||||
this.emoteUnsubscriber = emoteStore.subscribe((emote) => {
|
||||
@ -1993,8 +1995,8 @@ ${escapedMessage}
|
||||
if (addPlayerData.outlineColor !== undefined) {
|
||||
player.setApiOutlineColor(addPlayerData.outlineColor);
|
||||
}
|
||||
if (addPlayerData.status !== undefined) {
|
||||
player.setStatus(addPlayerData.status, true);
|
||||
if (addPlayerData.availabilityStatus !== undefined) {
|
||||
player.setAvailabilityStatus(addPlayerData.availabilityStatus, true);
|
||||
}
|
||||
this.MapPlayers.add(player);
|
||||
this.MapPlayersByKey.set(player.userId, player);
|
||||
@ -2145,8 +2147,8 @@ ${escapedMessage}
|
||||
if (message.details?.showVoiceIndicator !== undefined) {
|
||||
character.showTalkIcon(message.details?.showVoiceIndicator);
|
||||
}
|
||||
if (message.details?.status !== undefined) {
|
||||
character.setStatus(message.details?.status);
|
||||
if (message.details?.availabilityStatus !== undefined) {
|
||||
character.setAvailabilityStatus(message.details?.availabilityStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ export interface PlayerInterface {
|
||||
visitCardUrl: string | null;
|
||||
companion: string | null;
|
||||
userUuid: string;
|
||||
status: AvailabilityStatus;
|
||||
availabilityStatus: AvailabilityStatus;
|
||||
color?: string;
|
||||
outlineColor?: number;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ function createPlayersStore() {
|
||||
visitCardUrl: message.visitCardUrl,
|
||||
companion: message.companion,
|
||||
userUuid: message.userUuid,
|
||||
status: message.status,
|
||||
availabilityStatus: message.status,
|
||||
color: getRandomColor(),
|
||||
});
|
||||
return users;
|
||||
@ -59,7 +59,7 @@ function createPlayersStore() {
|
||||
characterLayers: [],
|
||||
visitCardUrl: null,
|
||||
companion: null,
|
||||
status: AvailabilityStatus.ONLINE,
|
||||
availabilityStatus: AvailabilityStatus.ONLINE,
|
||||
userUuid: "dummy",
|
||||
color: getRandomColor(),
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ message SetPlayerDetailsMessage {
|
||||
google.protobuf.UInt32Value outlineColor = 3;
|
||||
google.protobuf.BoolValue removeOutlineColor = 4;
|
||||
google.protobuf.BoolValue showVoiceIndicator = 5;
|
||||
AvailabilityStatus status = 6;
|
||||
AvailabilityStatus availabilityStatus = 6;
|
||||
}
|
||||
|
||||
message UserMovesMessage {
|
||||
@ -210,7 +210,7 @@ message UserJoinedMessage {
|
||||
string userUuid = 7;
|
||||
uint32 outlineColor = 8;
|
||||
bool hasOutline = 9;
|
||||
AvailabilityStatus status = 10;
|
||||
AvailabilityStatus availabilityStatus = 10;
|
||||
}
|
||||
|
||||
message UserLeftMessage {
|
||||
@ -369,7 +369,7 @@ message JoinRoomMessage {
|
||||
CompanionMessage companion = 8;
|
||||
string visitCardUrl = 9;
|
||||
string userRoomToken = 10;
|
||||
AvailabilityStatus status = 11;
|
||||
AvailabilityStatus availabilityStatus = 11;
|
||||
}
|
||||
|
||||
message UserJoinedZoneMessage {
|
||||
@ -383,7 +383,7 @@ message UserJoinedZoneMessage {
|
||||
string userUuid = 8;
|
||||
uint32 outlineColor = 9;
|
||||
bool hasOutline = 10;
|
||||
AvailabilityStatus status = 11;
|
||||
AvailabilityStatus availabilityStatus = 11;
|
||||
}
|
||||
|
||||
message UserLeftZoneMessage {
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
FollowAbortMessage,
|
||||
VariableMessage,
|
||||
LockGroupPromptMessage,
|
||||
AvailabilityStatus,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import { UserMovesMessage } from "../Messages/generated/messages_pb";
|
||||
import { parse } from "query-string";
|
||||
@ -53,6 +54,7 @@ interface UpgradeData {
|
||||
roomId: string;
|
||||
name: string;
|
||||
companion: CompanionMessage | undefined;
|
||||
availabilityStatus: AvailabilityStatus;
|
||||
characterLayers: WokaDetail[];
|
||||
messages: unknown[];
|
||||
tags: string[];
|
||||
@ -259,6 +261,7 @@ export class IoSocketController {
|
||||
const left = Number(query.left);
|
||||
const right = Number(query.right);
|
||||
const name = query.name;
|
||||
const availabilityStatus = Number(query.availabilityStatus);
|
||||
|
||||
let companion: CompanionMessage | undefined = undefined;
|
||||
|
||||
@ -270,6 +273,9 @@ export class IoSocketController {
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("Expecting name");
|
||||
}
|
||||
if (typeof availabilityStatus !== "number") {
|
||||
throw new Error("Expecting availability status");
|
||||
}
|
||||
if (name === "") {
|
||||
throw new Error("No empty name");
|
||||
}
|
||||
@ -411,6 +417,7 @@ export class IoSocketController {
|
||||
roomId,
|
||||
name,
|
||||
companion,
|
||||
availabilityStatus,
|
||||
characterLayers: characterLayerObjs,
|
||||
messages: memberMessages,
|
||||
tags: memberTags,
|
||||
@ -612,6 +619,7 @@ export class IoSocketController {
|
||||
client.visitCardUrl = ws.visitCardUrl;
|
||||
client.characterLayers = ws.characterLayers;
|
||||
client.companion = ws.companion;
|
||||
client.availabilityStatus = ws.availabilityStatus;
|
||||
client.roomId = ws.roomId;
|
||||
client.listenedZones = new Set<Zone>();
|
||||
return client;
|
||||
|
@ -2,6 +2,7 @@ import { PointInterface } from "./PointInterface";
|
||||
import { Identificable } from "./Identificable";
|
||||
import { ViewportInterface } from "../../Model/Websocket/ViewportMessage";
|
||||
import {
|
||||
AvailabilityStatus,
|
||||
BatchMessage,
|
||||
CompanionMessage,
|
||||
PusherToBackMessage,
|
||||
@ -26,6 +27,7 @@ export interface ExSocketInterface extends compressors.WebSocket, Identificable
|
||||
position: PointInterface;
|
||||
viewport: ViewportInterface;
|
||||
companion?: CompanionMessage;
|
||||
availabilityStatus: AvailabilityStatus;
|
||||
/**
|
||||
* Pushes an event that will be sent in the next batch of events
|
||||
*/
|
||||
|
@ -50,7 +50,7 @@ export class UserDescriptor {
|
||||
private name: string,
|
||||
private characterLayers: CharacterLayerMessage[],
|
||||
private position: PositionMessage,
|
||||
private status: AvailabilityStatus,
|
||||
private availabilityStatus: AvailabilityStatus,
|
||||
private visitCardUrl: string | null,
|
||||
private companion?: CompanionMessage,
|
||||
private outlineColor?: number
|
||||
@ -71,7 +71,7 @@ export class UserDescriptor {
|
||||
message.getName(),
|
||||
message.getCharacterlayersList(),
|
||||
position,
|
||||
message.getStatus(),
|
||||
message.getAvailabilitystatus(),
|
||||
message.getVisitcardurl(),
|
||||
message.getCompanion(),
|
||||
message.getHasoutline() ? message.getOutlinecolor() : undefined
|
||||
@ -92,9 +92,9 @@ export class UserDescriptor {
|
||||
} else {
|
||||
this.outlineColor = playerDetails.getOutlinecolor()?.getValue();
|
||||
}
|
||||
const status = playerDetails.getStatus();
|
||||
if (status !== undefined) {
|
||||
this.status = status;
|
||||
const availabilityStatus = playerDetails.getAvailabilitystatus();
|
||||
if (availabilityStatus !== undefined) {
|
||||
this.availabilityStatus = availabilityStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ export class UserDescriptor {
|
||||
userJoinedMessage.setName(this.name);
|
||||
userJoinedMessage.setCharacterlayersList(this.characterLayers);
|
||||
userJoinedMessage.setPosition(this.position);
|
||||
userJoinedMessage.setStatus(this.status);
|
||||
userJoinedMessage.setAvailabilitystatus(this.availabilityStatus);
|
||||
if (this.visitCardUrl) {
|
||||
userJoinedMessage.setVisitcardurl(this.visitCardUrl);
|
||||
}
|
||||
|
@ -175,6 +175,7 @@ export class SocketManager implements ZoneEventListener {
|
||||
joinRoomMessage.setIpaddress(client.IPAddress);
|
||||
joinRoomMessage.setRoomid(client.roomId);
|
||||
joinRoomMessage.setName(client.name);
|
||||
joinRoomMessage.setAvailabilitystatus(client.availabilityStatus);
|
||||
joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position));
|
||||
joinRoomMessage.setTagList(client.tags);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user