骰子和猜拳
This commit is contained in:
parent
a23e2a1007
commit
bb133314cd
@ -785,9 +785,38 @@ var default_api = 'https://api.global-interaction.com/';
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function analyticsEventID(prefix) {
|
||||||
|
var random = '';
|
||||||
|
if (window.crypto && window.crypto.getRandomValues) {
|
||||||
|
var bytes = new Uint32Array(2);
|
||||||
|
window.crypto.getRandomValues(bytes);
|
||||||
|
random = bytes[0].toString(36) + bytes[1].toString(36);
|
||||||
|
} else {
|
||||||
|
random = Math.random().toString(36).slice(2);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
String(prefix || 'self_game') +
|
||||||
|
'_' +
|
||||||
|
Date.now().toString(36) +
|
||||||
|
'_' +
|
||||||
|
random
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportSelfGameEvent(payload) {
|
||||||
|
var body = Object.assign({}, payload || {});
|
||||||
|
body.event_id = body.event_id || analyticsEventID(body.event_name);
|
||||||
|
body.occurred_at_ms = body.occurred_at_ms || Date.now();
|
||||||
|
return request('/api/v1/games/self/report-events', {
|
||||||
|
method: 'POST',
|
||||||
|
body: body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var gameAPI = {
|
var gameAPI = {
|
||||||
dice: diceGameAPI,
|
dice: diceGameAPI,
|
||||||
rps: rpsGameAPI,
|
rps: rpsGameAPI,
|
||||||
|
reportEvent: reportSelfGameEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
window.HyAppAPI = {
|
window.HyAppAPI = {
|
||||||
|
|||||||
@ -1556,6 +1556,7 @@
|
|||||||
var frame = document.querySelector('.rps-frame');
|
var frame = document.querySelector('.rps-frame');
|
||||||
var timers = [];
|
var timers = [];
|
||||||
var pollTimer = 0;
|
var pollTimer = 0;
|
||||||
|
var pageStartedAt = performance.now();
|
||||||
var transparentPixel =
|
var transparentPixel =
|
||||||
'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E';
|
'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%221%22 height=%221%22%2F%3E';
|
||||||
var gestures = {
|
var gestures = {
|
||||||
@ -1595,6 +1596,26 @@
|
|||||||
serverTimeMS: 0,
|
serverTimeMS: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function trackSelfGameEvent(name, extra) {
|
||||||
|
var api =
|
||||||
|
window.HyAppAPI &&
|
||||||
|
window.HyAppAPI.game &&
|
||||||
|
window.HyAppAPI.game.reportEvent;
|
||||||
|
if (typeof api !== 'function') return;
|
||||||
|
var payload = Object.assign(
|
||||||
|
{
|
||||||
|
event_name: name,
|
||||||
|
game_id: GAME_ID,
|
||||||
|
page: 'game/rock',
|
||||||
|
h5_version: 'rock-h5-20260611',
|
||||||
|
entry_source: currentRoomID() ? 'room' : 'direct',
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
extra || {}
|
||||||
|
);
|
||||||
|
api(payload).catch(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
function updateScale() {
|
function updateScale() {
|
||||||
var scale = Math.min(
|
var scale = Math.min(
|
||||||
window.innerWidth / DESIGN_WIDTH,
|
window.innerWidth / DESIGN_WIDTH,
|
||||||
@ -2263,12 +2284,24 @@
|
|||||||
resetToLobby();
|
resetToLobby();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
trackSelfGameEvent('reveal_enter', {
|
||||||
|
match_id: matchID,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
});
|
||||||
|
var rollStartedAt = performance.now();
|
||||||
rpsAPI()
|
rpsAPI()
|
||||||
.roll(matchID, {
|
.roll(matchID, {
|
||||||
game_id: GAME_ID,
|
game_id: GAME_ID,
|
||||||
gesture: state.selectedGesture,
|
gesture: state.selectedGesture,
|
||||||
})
|
})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
|
trackSelfGameEvent('roll_api', {
|
||||||
|
match_id: matchID,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - rollStartedAt
|
||||||
|
),
|
||||||
|
});
|
||||||
var nextMatch = matchFromData(data);
|
var nextMatch = matchFromData(data);
|
||||||
if (roundID !== state.roundID) return;
|
if (roundID !== state.roundID) return;
|
||||||
state.match = nextMatch || match;
|
state.match = nextMatch || match;
|
||||||
@ -2276,6 +2309,15 @@
|
|||||||
showBattle(roundID, state.match);
|
showBattle(roundID, state.match);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
|
trackSelfGameEvent('roll_api', {
|
||||||
|
match_id: matchID,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - rollStartedAt
|
||||||
|
),
|
||||||
|
success: false,
|
||||||
|
error_code: error && error.message,
|
||||||
|
});
|
||||||
showMessage(error.message);
|
showMessage(error.message);
|
||||||
resetToLobby();
|
resetToLobby();
|
||||||
});
|
});
|
||||||
@ -2329,6 +2371,11 @@
|
|||||||
stake_coin: state.selectedStake,
|
stake_coin: state.selectedStake,
|
||||||
gesture: state.selectedGesture,
|
gesture: state.selectedGesture,
|
||||||
});
|
});
|
||||||
|
trackSelfGameEvent('match_click', {
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
mode: matchRequestMode(),
|
||||||
|
});
|
||||||
var payload = {
|
var payload = {
|
||||||
game_id: GAME_ID,
|
game_id: GAME_ID,
|
||||||
room_id: currentRoomID(),
|
room_id: currentRoomID(),
|
||||||
@ -2348,7 +2395,30 @@
|
|||||||
} else {
|
} else {
|
||||||
request = rpsAPI().match(payload);
|
request = rpsAPI().match(payload);
|
||||||
}
|
}
|
||||||
request.then(handleMatchData).catch(function (error) {
|
var matchStartedAt = performance.now();
|
||||||
|
request
|
||||||
|
.then(function (data) {
|
||||||
|
var match = matchFromData(data);
|
||||||
|
trackSelfGameEvent('match_success', {
|
||||||
|
match_id: match && (match.match_id || match.matchId),
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - matchStartedAt
|
||||||
|
),
|
||||||
|
});
|
||||||
|
handleMatchData(data);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
trackSelfGameEvent('match_api', {
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - matchStartedAt
|
||||||
|
),
|
||||||
|
success: false,
|
||||||
|
error_code: error && error.message,
|
||||||
|
});
|
||||||
state.busy = false;
|
state.busy = false;
|
||||||
showMessage(error.message);
|
showMessage(error.message);
|
||||||
setScreen('lobby');
|
setScreen('lobby');
|
||||||
@ -2440,6 +2510,12 @@
|
|||||||
if (match) renderMatch(match);
|
if (match) renderMatch(match);
|
||||||
renderVictory(result, match);
|
renderVictory(result, match);
|
||||||
setScreen('victory');
|
setScreen('victory');
|
||||||
|
trackSelfGameEvent('settlement_show', {
|
||||||
|
match_id: match && (match.match_id || match.matchId),
|
||||||
|
result: result,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
});
|
||||||
postBridge('gameRPSSettled', {
|
postBridge('gameRPSSettled', {
|
||||||
game_code: GAME_CODE,
|
game_code: GAME_CODE,
|
||||||
game_id: GAME_ID,
|
game_id: GAME_ID,
|
||||||
@ -2471,7 +2547,18 @@
|
|||||||
}
|
}
|
||||||
return rpsAPI()
|
return rpsAPI()
|
||||||
.cancel(matchID)
|
.cancel(matchID)
|
||||||
.catch(function () {});
|
.then(function () {
|
||||||
|
trackSelfGameEvent('match_cancel', {
|
||||||
|
match_id: matchID,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
trackSelfGameEvent('cancel_api', {
|
||||||
|
match_id: matchID,
|
||||||
|
success: false,
|
||||||
|
error_code: 'cancel_failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openRecharge() {
|
function openRecharge() {
|
||||||
@ -2544,9 +2631,15 @@
|
|||||||
renderStakeOptions(state.config);
|
renderStakeOptions(state.config);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
var configStartedAt = performance.now();
|
||||||
return rpsAPI()
|
return rpsAPI()
|
||||||
.config(GAME_ID)
|
.config(GAME_ID)
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
|
trackSelfGameEvent('config_load_success', {
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - configStartedAt
|
||||||
|
),
|
||||||
|
});
|
||||||
state.config =
|
state.config =
|
||||||
(data && (data.config || data.rps_config)) ||
|
(data && (data.config || data.rps_config)) ||
|
||||||
data ||
|
data ||
|
||||||
@ -2554,6 +2647,13 @@
|
|||||||
renderStakeOptions(state.config);
|
renderStakeOptions(state.config);
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
|
trackSelfGameEvent('config_load_fail', {
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - configStartedAt
|
||||||
|
),
|
||||||
|
success: false,
|
||||||
|
error_code: 'config_failed',
|
||||||
|
});
|
||||||
renderStakeOptions(state.config);
|
renderStakeOptions(state.config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2591,6 +2691,9 @@
|
|||||||
state.selectedGesture =
|
state.selectedGesture =
|
||||||
target.getAttribute('data-gesture') || 'rock';
|
target.getAttribute('data-gesture') || 'rock';
|
||||||
renderSelection();
|
renderSelection();
|
||||||
|
trackSelfGameEvent('gesture_select', {
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (action === 'stake') {
|
if (action === 'stake') {
|
||||||
@ -2609,6 +2712,9 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
trackSelfGameEvent('stake_select', {
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (action === 'start') {
|
if (action === 'start') {
|
||||||
@ -2627,11 +2733,21 @@
|
|||||||
if (action === 'rematch') {
|
if (action === 'rematch') {
|
||||||
cancelMatch().then(function () {
|
cancelMatch().then(function () {
|
||||||
resetToLobby();
|
resetToLobby();
|
||||||
|
trackSelfGameEvent('play_again', {
|
||||||
|
source: 'rematch',
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
});
|
||||||
startMatch();
|
startMatch();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (action === 'play-again') {
|
if (action === 'play-again') {
|
||||||
|
trackSelfGameEvent('play_again', {
|
||||||
|
source: 'settlement',
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
gesture: state.selectedGesture,
|
||||||
|
});
|
||||||
resetToLobby();
|
resetToLobby();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -2669,6 +2785,9 @@
|
|||||||
|
|
||||||
updateScale();
|
updateScale();
|
||||||
initActions();
|
initActions();
|
||||||
|
trackSelfGameEvent('page_open', {
|
||||||
|
duration_ms: Math.round(performance.now() - pageStartedAt),
|
||||||
|
});
|
||||||
window.addEventListener('beforeunload', function () {
|
window.addEventListener('beforeunload', function () {
|
||||||
cancelMatch();
|
cancelMatch();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2434,6 +2434,7 @@
|
|||||||
var timers = [];
|
var timers = [];
|
||||||
var pollTimer = 0;
|
var pollTimer = 0;
|
||||||
var roundSequence = 0;
|
var roundSequence = 0;
|
||||||
|
var pageStartedAt = performance.now();
|
||||||
var rollEffectPlayers = {
|
var rollEffectPlayers = {
|
||||||
self: null,
|
self: null,
|
||||||
opponent: null,
|
opponent: null,
|
||||||
@ -2450,6 +2451,26 @@
|
|||||||
serverTimeMS: 0,
|
serverTimeMS: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function trackSelfGameEvent(name, extra) {
|
||||||
|
var api =
|
||||||
|
window.HyAppAPI &&
|
||||||
|
window.HyAppAPI.game &&
|
||||||
|
window.HyAppAPI.game.reportEvent;
|
||||||
|
if (typeof api !== 'function') return;
|
||||||
|
var payload = Object.assign(
|
||||||
|
{
|
||||||
|
event_name: name,
|
||||||
|
game_id: 'dice',
|
||||||
|
page: 'game/touzi',
|
||||||
|
h5_version: 'dice-h5-20260611',
|
||||||
|
entry_source: currentRoomID() ? 'room' : 'direct',
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
extra || {}
|
||||||
|
);
|
||||||
|
api(payload).catch(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
function updateScale() {
|
function updateScale() {
|
||||||
var scale = Math.min(
|
var scale = Math.min(
|
||||||
window.innerWidth / DESIGN_WIDTH,
|
window.innerWidth / DESIGN_WIDTH,
|
||||||
@ -3266,6 +3287,9 @@
|
|||||||
setPhaseText(i18nText('diceMatch.rolling', 'Rolling...'));
|
setPhaseText(i18nText('diceMatch.rolling', 'Rolling...'));
|
||||||
frame.classList.remove('has-scores');
|
frame.classList.remove('has-scores');
|
||||||
playRollEffects();
|
playRollEffects();
|
||||||
|
trackSelfGameEvent('reveal_enter', {
|
||||||
|
match_id: matchID,
|
||||||
|
});
|
||||||
postBridge('gameDiceRolling', {
|
postBridge('gameDiceRolling', {
|
||||||
game_code: 'touzi',
|
game_code: 'touzi',
|
||||||
match_id: matchID,
|
match_id: matchID,
|
||||||
@ -3273,9 +3297,18 @@
|
|||||||
var rollPromise = game
|
var rollPromise = game
|
||||||
.roll(matchID)
|
.roll(matchID)
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
|
trackSelfGameEvent('roll_api', {
|
||||||
|
match_id: matchID,
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
return matchFromData(data);
|
return matchFromData(data);
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
|
trackSelfGameEvent('roll_api', {
|
||||||
|
match_id: matchID,
|
||||||
|
success: false,
|
||||||
|
error_code: 'roll_failed',
|
||||||
|
});
|
||||||
return game.getMatch(matchID).then(function (data) {
|
return game.getMatch(matchID).then(function (data) {
|
||||||
return matchFromData(data);
|
return matchFromData(data);
|
||||||
});
|
});
|
||||||
@ -3370,6 +3403,10 @@
|
|||||||
frame.classList.add('is-victory');
|
frame.classList.add('is-victory');
|
||||||
renderVictory(match);
|
renderVictory(match);
|
||||||
refreshBalance();
|
refreshBalance();
|
||||||
|
trackSelfGameEvent('settlement_show', {
|
||||||
|
match_id: match.match_id,
|
||||||
|
result: match.result,
|
||||||
|
});
|
||||||
postBridge('gameDiceSettled', {
|
postBridge('gameDiceSettled', {
|
||||||
game_code: 'touzi',
|
game_code: 'touzi',
|
||||||
match_id: match.match_id,
|
match_id: match.match_id,
|
||||||
@ -3450,13 +3487,35 @@
|
|||||||
game_code: 'touzi',
|
game_code: 'touzi',
|
||||||
stake: stake,
|
stake: stake,
|
||||||
});
|
});
|
||||||
|
trackSelfGameEvent('match_click', {
|
||||||
|
stake_coin: stake,
|
||||||
|
});
|
||||||
|
var matchStartedAt = performance.now();
|
||||||
game.match({
|
game.match({
|
||||||
game_id: 'dice',
|
game_id: 'dice',
|
||||||
room_id: currentRoomID(),
|
room_id: currentRoomID(),
|
||||||
stake_coin: stake,
|
stake_coin: stake,
|
||||||
})
|
})
|
||||||
.then(handleMatchData)
|
.then(function (data) {
|
||||||
|
var match = matchFromData(data);
|
||||||
|
trackSelfGameEvent('match_success', {
|
||||||
|
match_id: match && match.match_id,
|
||||||
|
stake_coin: stake,
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - matchStartedAt
|
||||||
|
),
|
||||||
|
});
|
||||||
|
handleMatchData(data);
|
||||||
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
|
trackSelfGameEvent('match_api', {
|
||||||
|
stake_coin: stake,
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - matchStartedAt
|
||||||
|
),
|
||||||
|
success: false,
|
||||||
|
error_code: error && error.message,
|
||||||
|
});
|
||||||
showMessage(error.message);
|
showMessage(error.message);
|
||||||
resetToLobby(true);
|
resetToLobby(true);
|
||||||
});
|
});
|
||||||
@ -3473,7 +3532,20 @@
|
|||||||
) {
|
) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return game.cancel(match.match_id).catch(function () {});
|
return game
|
||||||
|
.cancel(match.match_id)
|
||||||
|
.then(function () {
|
||||||
|
trackSelfGameEvent('match_cancel', {
|
||||||
|
match_id: match.match_id,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
trackSelfGameEvent('cancel_api', {
|
||||||
|
match_id: match.match_id,
|
||||||
|
success: false,
|
||||||
|
error_code: 'cancel_failed',
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetToLobby(keepStake) {
|
function resetToLobby(keepStake) {
|
||||||
@ -3537,6 +3609,7 @@
|
|||||||
true,
|
true,
|
||||||
i18nText('diceMatch.loading', 'Loading')
|
i18nText('diceMatch.loading', 'Loading')
|
||||||
);
|
);
|
||||||
|
var configStartedAt = performance.now();
|
||||||
Promise.all([
|
Promise.all([
|
||||||
api()
|
api()
|
||||||
.user.me()
|
.user.me()
|
||||||
@ -3555,7 +3628,22 @@
|
|||||||
}),
|
}),
|
||||||
diceAPI()
|
diceAPI()
|
||||||
.config('dice')
|
.config('dice')
|
||||||
|
.then(function (data) {
|
||||||
|
trackSelfGameEvent('config_load_success', {
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - configStartedAt
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
|
trackSelfGameEvent('config_load_fail', {
|
||||||
|
duration_ms: Math.round(
|
||||||
|
performance.now() - configStartedAt
|
||||||
|
),
|
||||||
|
success: false,
|
||||||
|
error_code: 'config_failed',
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
]).then(function (results) {
|
]).then(function (results) {
|
||||||
@ -3589,6 +3677,9 @@
|
|||||||
if (action === 'stake') {
|
if (action === 'stake') {
|
||||||
if (state.busy) return;
|
if (state.busy) return;
|
||||||
selectStake(target);
|
selectStake(target);
|
||||||
|
trackSelfGameEvent('stake_select', {
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (action === 'match') {
|
if (action === 'match') {
|
||||||
@ -3601,6 +3692,10 @@
|
|||||||
postBridge('gameRematch', {
|
postBridge('gameRematch', {
|
||||||
game_code: 'touzi',
|
game_code: 'touzi',
|
||||||
});
|
});
|
||||||
|
trackSelfGameEvent('play_again', {
|
||||||
|
source: 'rematch',
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
});
|
||||||
startMatch();
|
startMatch();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -3611,6 +3706,10 @@
|
|||||||
postBridge('gamePlayAgain', {
|
postBridge('gamePlayAgain', {
|
||||||
game_code: 'touzi',
|
game_code: 'touzi',
|
||||||
});
|
});
|
||||||
|
trackSelfGameEvent('play_again', {
|
||||||
|
source: 'settlement',
|
||||||
|
stake_coin: state.selectedStake,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -3621,6 +3720,9 @@
|
|||||||
|
|
||||||
updateScale();
|
updateScale();
|
||||||
initActions();
|
initActions();
|
||||||
|
trackSelfGameEvent('page_open', {
|
||||||
|
duration_ms: Math.round(performance.now() - pageStartedAt),
|
||||||
|
});
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
'hyapp:i18n-ready',
|
'hyapp:i18n-ready',
|
||||||
refreshLocalizedRuntimeText
|
refreshLocalizedRuntimeText
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user