feat: tawk.to
This commit is contained in:
parent
65c2e04589
commit
5a90dc1570
@ -7,6 +7,23 @@
|
||||
|
||||
@{
|
||||
var isDevelopment = HostEnvironment?.IsDevelopment() ?? false;
|
||||
var isPremiumUser = false;
|
||||
|
||||
if (User?.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
try
|
||||
{
|
||||
isPremiumUser = await AdService.HasValidPremiumSubscription(userId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
isPremiumUser = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
@ -390,6 +407,37 @@
|
||||
<!-- Cookie Consent Banner -->
|
||||
@await Html.PartialAsync("_CookieConsent")
|
||||
|
||||
<script type="text/javascript">
|
||||
var Tawk_API = Tawk_API || {}, Tawk_LoadStart = new Date();
|
||||
(function () {
|
||||
var s1 = document.createElement("script"), s0 = document.getElementsByTagName("script")[0];
|
||||
s1.async = true;
|
||||
s1.src = "https://embed.tawk.to/68f825b20cfb8619514cf42c/1j84l6gqp";
|
||||
s1.charset = "UTF-8";
|
||||
s1.setAttribute("crossorigin", "*");
|
||||
s0.parentNode.insertBefore(s1, s0);
|
||||
})();
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
(function (isPremium) {
|
||||
Tawk_API = Tawk_API || {};
|
||||
var originalOnLoad = Tawk_API.onLoad;
|
||||
Tawk_API.onLoad = function () {
|
||||
if (typeof originalOnLoad === "function") {
|
||||
try { originalOnLoad(); } catch (error) { console.warn("Tawk original onLoad error:", error); }
|
||||
}
|
||||
|
||||
if (isPremium) {
|
||||
if (typeof Tawk_API.showWidget === "function") {
|
||||
Tawk_API.showWidget();
|
||||
}
|
||||
} else if (typeof Tawk_API.hideWidget === "function") {
|
||||
Tawk_API.hideWidget();
|
||||
}
|
||||
};
|
||||
})(@isPremiumUser.ToString().ToLower());
|
||||
</script>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
94
Views/Shared/_TelegramPremiumFab.cshtml
Normal file
94
Views/Shared/_TelegramPremiumFab.cshtml
Normal file
@ -0,0 +1,94 @@
|
||||
@using System.Globalization
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inject IStringLocalizer<QRRapidoApp.Resources.SharedResource> Localizer
|
||||
@inject Microsoft.Extensions.Configuration.IConfiguration Configuration
|
||||
|
||||
@{
|
||||
var culture = CultureInfo.CurrentUICulture;
|
||||
var telegramLang = culture.Name.Replace('-', '_');
|
||||
var botUsername = Configuration["Telegram:LoginWidgetBotUsername"] ?? string.Empty;
|
||||
var botId = Configuration["Telegram:BotId"] ?? string.Empty;
|
||||
var requestAccess = Configuration["Telegram:LoginWidgetRequestAccess"] ?? "write";
|
||||
}
|
||||
|
||||
<div id="telegramFabRoot"
|
||||
class="telegram-fab-root"
|
||||
data-telegram-lang="@telegramLang"
|
||||
data-bot-username="@botUsername"
|
||||
data-bot-id="@botId"
|
||||
data-request-access="@requestAccess"
|
||||
data-status-loading="@Localizer["TelegramPremiumStatusLoading"]"
|
||||
data-status-error="@Localizer["TelegramPremiumStatusError"]"
|
||||
data-status-connected="@Localizer["TelegramPremiumStatusConnected"]"
|
||||
data-status-disconnected="@Localizer["TelegramPremiumStatusDisconnected"]"
|
||||
data-link-success="@Localizer["TelegramPremiumLinkSuccess"]"
|
||||
data-unlink-success="@Localizer["TelegramPremiumUnlinkSuccess"]"
|
||||
data-link-error="@Localizer["TelegramPremiumLinkError"]"
|
||||
data-unlink-error="@Localizer["TelegramPremiumUnlinkError"]"
|
||||
data-retry-label="@Localizer["TelegramPremiumRetry"]"
|
||||
data-open-label="@Localizer["TelegramPremiumOpenTelegram"]"
|
||||
data-unlink-label="@Localizer["TelegramPremiumUnlink"]">
|
||||
<button id="telegramPremiumFab"
|
||||
type="button"
|
||||
class="btn btn-primary fab-telegram d-flex align-items-center justify-content-center"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#telegramPremiumOffcanvas"
|
||||
aria-controls="telegramPremiumOffcanvas"
|
||||
aria-label="@Localizer["TelegramPremiumHelp"]"
|
||||
title="@Localizer["TelegramPremiumHelp"]">
|
||||
<i class="fab fa-telegram-plane" aria-hidden="true"></i>
|
||||
<span class="visually-hidden">@Localizer["TelegramPremiumHelp"]</span>
|
||||
</button>
|
||||
|
||||
<div class="offcanvas offcanvas-end offcanvas-sm-bottom"
|
||||
tabindex="-1"
|
||||
id="telegramPremiumOffcanvas"
|
||||
aria-labelledby="telegramPremiumOffcanvasLabel">
|
||||
<div class="offcanvas-header border-bottom">
|
||||
<h5 class="offcanvas-title" id="telegramPremiumOffcanvasLabel">@Localizer["TelegramPremiumTitle"]</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="@Localizer["Close"]"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<div id="telegramFabAlert" class="alert alert-danger d-none" role="alert"></div>
|
||||
|
||||
<div id="telegramFabLoading" class="d-flex align-items-center gap-2 telegram-fab-loading" role="status">
|
||||
<div class="spinner-border spinner-border-sm text-primary" aria-hidden="true"></div>
|
||||
<span>@Localizer["TelegramPremiumStatusLoading"]</span>
|
||||
</div>
|
||||
|
||||
<div id="telegramFabConnected" class="telegram-fab-connected d-none">
|
||||
<p class="mb-3 text-body">@Html.Raw(Localizer["TelegramPremiumStatusConnected", "<span class=\"fw-semibold telegram-fab-username\"></span>"])</p>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="button" class="btn btn-success telegram-fab-open">
|
||||
<i class="fab fa-telegram-plane me-2" aria-hidden="true"></i>
|
||||
@Localizer["TelegramPremiumOpenTelegram"]
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary telegram-fab-unlink">
|
||||
@Localizer["TelegramPremiumUnlink"]
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="telegramFabDisconnected" class="telegram-fab-disconnected d-none">
|
||||
<p class="mb-3 text-body-secondary">@Localizer["TelegramPremiumLoginHint"]</p>
|
||||
<div id="telegramLoginWidgetPlaceholder" class="telegram-fab-widget-placeholder">
|
||||
<div class="telegram-fab-widget-skeleton">
|
||||
<div class="placeholder-wave">
|
||||
<span class="placeholder col-8"></span>
|
||||
<span class="placeholder col-6 mt-2"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-grid gap-2 mt-3">
|
||||
<button type="button" class="btn btn-outline-primary telegram-fab-retry d-none">
|
||||
@Localizer["TelegramPremiumRetry"]
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="telegramFabToasts" class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
<!-- Toasts injected dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
80
wwwroot/css/telegram-fab.css
Normal file
80
wwwroot/css/telegram-fab.css
Normal file
@ -0,0 +1,80 @@
|
||||
.fab-telegram {
|
||||
position: fixed;
|
||||
bottom: var(--telegram-fab-offset, 1.5rem);
|
||||
right: var(--telegram-fab-offset, 1.5rem);
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.18);
|
||||
z-index: 1052;
|
||||
opacity: 0.95;
|
||||
backdrop-filter: blur(2px);
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.fab-telegram:hover,
|
||||
.fab-telegram:focus-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.fab-telegram:focus-visible {
|
||||
outline: 2px solid rgba(0, 123, 255, 0.6);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.fab-telegram {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.telegram-fab-root .toast-container {
|
||||
z-index: 1080;
|
||||
}
|
||||
|
||||
.telegram-fab-loading .spinner-border {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.telegram-fab-widget-placeholder {
|
||||
min-height: 90px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.telegram-fab-widget-skeleton .placeholder {
|
||||
display: inline-block;
|
||||
height: 0.875rem;
|
||||
}
|
||||
|
||||
.telegram-fab-widget-skeleton .placeholder.col-8 {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.telegram-fab-widget-skeleton .placeholder.col-6 {
|
||||
width: 55%;
|
||||
}
|
||||
|
||||
.offcanvas-sm-bottom {
|
||||
--bs-offcanvas-width: min(420px, 90vw);
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.offcanvas-sm-bottom {
|
||||
--bs-offcanvas-width: 100%;
|
||||
--bs-offcanvas-height: min(80vh, 420px);
|
||||
--bs-offcanvas-transform: translateY(100%);
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
width: 100%;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
327
wwwroot/js/telegram-fab.js
Normal file
327
wwwroot/js/telegram-fab.js
Normal file
@ -0,0 +1,327 @@
|
||||
(function () {
|
||||
function initTelegramFab() {
|
||||
const root = document.getElementById('telegramFabRoot');
|
||||
if (!root || root.dataset.initialized === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
root.dataset.initialized = 'true';
|
||||
|
||||
const dataset = root.dataset;
|
||||
const offcanvasElement = document.getElementById('telegramPremiumOffcanvas');
|
||||
const loadingSection = document.getElementById('telegramFabLoading');
|
||||
const connectedSection = document.getElementById('telegramFabConnected');
|
||||
const disconnectedSection = document.getElementById('telegramFabDisconnected');
|
||||
const alertElement = document.getElementById('telegramFabAlert');
|
||||
const retryButton = root.querySelector('.telegram-fab-retry');
|
||||
const openButton = root.querySelector('.telegram-fab-open');
|
||||
const unlinkButton = root.querySelector('.telegram-fab-unlink');
|
||||
const usernameElement = root.querySelector('.telegram-fab-username');
|
||||
const widgetPlaceholder = document.getElementById('telegramLoginWidgetPlaceholder');
|
||||
const toastContainer = document.getElementById('telegramFabToasts');
|
||||
const fabButton = document.getElementById('telegramPremiumFab');
|
||||
|
||||
if (!offcanvasElement || !loadingSection || !connectedSection || !disconnectedSection) {
|
||||
console.warn('[Telegram FAB] Missing required DOM nodes. Aborting initialization.');
|
||||
return;
|
||||
}
|
||||
|
||||
const offcanvas = bootstrap.Offcanvas.getOrCreateInstance(offcanvasElement);
|
||||
let currentStatus = null;
|
||||
let currentDeepLink = null;
|
||||
let widgetMounted = false;
|
||||
|
||||
const messages = {
|
||||
loading: dataset.statusLoading || 'Loading...',
|
||||
error: dataset.statusError || 'Something went wrong.',
|
||||
connectedTemplate: dataset.statusConnected || 'Connected as {0}',
|
||||
disconnected: dataset.statusDisconnected || '',
|
||||
linkSuccess: dataset.linkSuccess || 'Linked successfully.',
|
||||
unlinkSuccess: dataset.unlinkSuccess || 'Unlinked successfully.',
|
||||
linkError: dataset.linkError || 'Unable to link. Try again.',
|
||||
unlinkError: dataset.unlinkError || 'Unable to unlink. Try again.',
|
||||
retry: dataset.retryLabel || 'Try again',
|
||||
open: dataset.openLabel || 'Open',
|
||||
unlink: dataset.unlinkLabel || 'Unlink'
|
||||
};
|
||||
|
||||
function toggleSections(state) {
|
||||
loadingSection.classList.toggle('d-none', state !== 'loading');
|
||||
connectedSection.classList.toggle('d-none', state !== 'connected');
|
||||
disconnectedSection.classList.toggle('d-none', state !== 'disconnected');
|
||||
}
|
||||
|
||||
function showAlert(message) {
|
||||
if (!alertElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
alertElement.textContent = message;
|
||||
alertElement.classList.remove('d-none');
|
||||
}
|
||||
|
||||
function hideAlert() {
|
||||
if (!alertElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
alertElement.classList.add('d-none');
|
||||
alertElement.textContent = '';
|
||||
}
|
||||
|
||||
function showToast(message, type) {
|
||||
if (!toastContainer || !message) {
|
||||
return;
|
||||
}
|
||||
|
||||
const palette = {
|
||||
success: 'text-bg-success',
|
||||
error: 'text-bg-danger',
|
||||
info: 'text-bg-info'
|
||||
};
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast align-items-center ${palette[type] || palette.info} border-0`;
|
||||
toast.role = 'status';
|
||||
toast.setAttribute('aria-live', 'polite');
|
||||
toast.innerHTML = [
|
||||
'<div class="d-flex">',
|
||||
`<div class="toast-body">${message}</div>`,
|
||||
'<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
toastContainer.appendChild(toast);
|
||||
|
||||
const toastInstance = bootstrap.Toast.getOrCreateInstance(toast, {
|
||||
delay: 4000,
|
||||
autohide: true
|
||||
});
|
||||
|
||||
toast.addEventListener('hidden.bs.toast', function () {
|
||||
toast.remove();
|
||||
});
|
||||
|
||||
toastInstance.show();
|
||||
}
|
||||
|
||||
function updateConnectedView(username, deepLink) {
|
||||
toggleSections('connected');
|
||||
hideAlert();
|
||||
|
||||
if (usernameElement) {
|
||||
usernameElement.textContent = username || '';
|
||||
}
|
||||
|
||||
currentDeepLink = deepLink || null;
|
||||
}
|
||||
|
||||
function updateDisconnectedView() {
|
||||
toggleSections('disconnected');
|
||||
hideAlert();
|
||||
|
||||
if (!widgetMounted) {
|
||||
mountLoginWidget();
|
||||
}
|
||||
}
|
||||
|
||||
function applyErrorState(message) {
|
||||
toggleSections('disconnected');
|
||||
showAlert(message || messages.error);
|
||||
|
||||
if (retryButton) {
|
||||
retryButton.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchStatus() {
|
||||
toggleSections('loading');
|
||||
hideAlert();
|
||||
|
||||
try {
|
||||
const response = await fetch('/telegram/status', {
|
||||
method: 'GET',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
if (fabButton) {
|
||||
fabButton.setAttribute('hidden', 'hidden');
|
||||
fabButton.setAttribute('tabindex', '-1');
|
||||
}
|
||||
root.setAttribute('aria-hidden', 'true');
|
||||
root.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('status_request_failed');
|
||||
}
|
||||
|
||||
currentStatus = await response.json();
|
||||
|
||||
if (currentStatus && currentStatus.connected) {
|
||||
updateConnectedView(currentStatus.username || '', currentStatus.deepLink || null);
|
||||
} else {
|
||||
updateDisconnectedView();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[Telegram FAB] Status request failed:', error);
|
||||
applyErrorState(messages.error);
|
||||
}
|
||||
}
|
||||
|
||||
function mountLoginWidget() {
|
||||
const botUsernameRaw = (dataset.botUsername || '').trim();
|
||||
|
||||
if (!botUsernameRaw) {
|
||||
console.warn('[Telegram FAB] Telegram bot username missing. Configure Telegram:LoginWidgetBotUsername.');
|
||||
applyErrorState(messages.error);
|
||||
return;
|
||||
}
|
||||
|
||||
const botUsername = botUsernameRaw.startsWith('@')
|
||||
? botUsernameRaw.slice(1)
|
||||
: botUsernameRaw;
|
||||
|
||||
if (!widgetPlaceholder) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgetPlaceholder.innerHTML = '';
|
||||
|
||||
const widgetScript = document.createElement('script');
|
||||
widgetScript.src = 'https://telegram.org/js/telegram-widget.js?22';
|
||||
widgetScript.async = true;
|
||||
widgetScript.dataset.telegramLogin = botUsername;
|
||||
widgetScript.dataset.size = 'large';
|
||||
widgetScript.dataset.userpic = 'false';
|
||||
widgetScript.dataset.requestAccess = dataset.requestAccess || 'write';
|
||||
|
||||
const lang = (dataset.telegramLang || 'en').replace('-', '_');
|
||||
widgetScript.dataset.lang = lang;
|
||||
|
||||
widgetScript.dataset.onauth = 'telegramFabOnAuth';
|
||||
|
||||
widgetPlaceholder.appendChild(widgetScript);
|
||||
widgetMounted = true;
|
||||
}
|
||||
|
||||
async function linkTelegram(payload) {
|
||||
toggleSections('loading');
|
||||
hideAlert();
|
||||
|
||||
try {
|
||||
const response = await fetch('/telegram/link', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('link_failed');
|
||||
}
|
||||
|
||||
showToast(messages.linkSuccess, 'success');
|
||||
await fetchStatus();
|
||||
} catch (error) {
|
||||
console.error('[Telegram FAB] Link failed:', error);
|
||||
showToast(messages.linkError, 'error');
|
||||
applyErrorState(messages.linkError);
|
||||
}
|
||||
}
|
||||
|
||||
async function unlinkTelegram() {
|
||||
toggleSections('loading');
|
||||
hideAlert();
|
||||
|
||||
try {
|
||||
const response = await fetch('/telegram/unlink', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('unlink_failed');
|
||||
}
|
||||
|
||||
showToast(messages.unlinkSuccess, 'success');
|
||||
widgetMounted = false;
|
||||
await fetchStatus();
|
||||
} catch (error) {
|
||||
console.error('[Telegram FAB] Unlink failed:', error);
|
||||
showToast(messages.unlinkError, 'error');
|
||||
toggleSections('connected');
|
||||
showAlert(messages.unlinkError);
|
||||
}
|
||||
}
|
||||
|
||||
offcanvasElement.addEventListener('show.bs.offcanvas', function () {
|
||||
fetchStatus();
|
||||
});
|
||||
|
||||
if (retryButton) {
|
||||
retryButton.addEventListener('click', function () {
|
||||
hideAlert();
|
||||
fetchStatus();
|
||||
});
|
||||
}
|
||||
|
||||
if (openButton) {
|
||||
openButton.addEventListener('click', function () {
|
||||
if (currentDeepLink) {
|
||||
window.open(currentDeepLink, '_blank', 'noopener');
|
||||
} else {
|
||||
fetchStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (unlinkButton) {
|
||||
unlinkButton.addEventListener('click', function () {
|
||||
unlinkTelegram();
|
||||
});
|
||||
}
|
||||
|
||||
window.telegramFabOnAuth = function (user) {
|
||||
if (!user) {
|
||||
showToast(messages.linkError, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
id: user.id,
|
||||
hash: user.hash,
|
||||
first_name: user.first_name,
|
||||
last_name: user.last_name,
|
||||
username: user.username,
|
||||
photo_url: user.photo_url,
|
||||
auth_date: user.auth_date,
|
||||
bot_id: dataset.botId || null
|
||||
};
|
||||
|
||||
linkTelegram(payload);
|
||||
};
|
||||
|
||||
if (fabButton) {
|
||||
fabButton.addEventListener('keydown', function (event) {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
offcanvas.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initTelegramFab);
|
||||
window.initTelegramFab = initTelegramFab;
|
||||
})();
|
||||
Loading…
Reference in New Issue
Block a user