QR page sharing (#368)
* Add QR to ShareModal and update next image hostnames * Remove unused import * Add qrcode into socials and update i18n * Update README to include QR code option to social sharing * Include qrcode in AdminAppConfigurationClient
This commit is contained in:
parent
822a1b86e5
commit
43c6bceb94
@ -178,12 +178,13 @@ Create Upstash Redis store from storage tab of Vercel dashboard and link to your
|
||||
- `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information)
|
||||
- `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)
|
||||
- `NEXT_PUBLIC_SOCIAL_NETWORKS`
|
||||
- Comma-separated list of social networks to show in share modal
|
||||
- Comma-separated list of social networks and sharing options to show in share modal
|
||||
- Accepted values:
|
||||
- `x` (default)
|
||||
- `threads`
|
||||
- `facebook`
|
||||
- `linkedin`
|
||||
- `qrcode`
|
||||
- `all`
|
||||
- `none`
|
||||
- `NEXT_PUBLIC_SITE_FEEDS = 1` enables feeds at `/feed.json` and `/rss.xml`
|
||||
|
||||
@ -39,7 +39,14 @@ const generateRemotePattern = (
|
||||
pathname: '/**',
|
||||
});
|
||||
|
||||
const remotePatterns: RemotePattern[] = [];
|
||||
const remotePatterns: RemotePattern[] = [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'api.qrserver.com',
|
||||
port: '',
|
||||
pathname: '/v1/create-qr-code/**',
|
||||
},
|
||||
];
|
||||
|
||||
if (HOSTNAME_VERCEL_BLOB) {
|
||||
remotePatterns.push(generateRemotePattern(HOSTNAME_VERCEL_BLOB));
|
||||
|
||||
@ -912,14 +912,14 @@ export default function AdminAppConfigurationClient({
|
||||
{renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Social networks"
|
||||
title="Social networks and sharing"
|
||||
status={hasSocialKeys}
|
||||
optional
|
||||
>
|
||||
{renderOrderedKeyList(socialKeys, SOCIAL_KEYS)}
|
||||
<div>
|
||||
Configure order and visibility of social networks
|
||||
(seen in share modal) by storing comma-separated values
|
||||
Configure order and visibility of social networks and sharing
|
||||
options (seen in share modal) by storing comma-separated values
|
||||
(accepts {'"all"'} or {'"none"'},
|
||||
defaults to {renderCommaSeparatedList(DEFAULT_SOCIAL_KEYS)})
|
||||
</div>
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Threads এ শেয়ার করুন',
|
||||
shareFacebook: 'Facebook এ শেয়ার করুন',
|
||||
shareLinkedIn: 'LinkedIn এ শেয়ার করুন',
|
||||
shareQRCode: 'QR কোড টগল করুন',
|
||||
},
|
||||
theme: {
|
||||
theme: 'থিম',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Share on Threads',
|
||||
shareFacebook: 'Share on Facebook',
|
||||
shareLinkedIn: 'Share on LinkedIn',
|
||||
shareQRCode: 'Toggle QR Code',
|
||||
},
|
||||
theme: {
|
||||
theme: 'Theme',
|
||||
|
||||
@ -95,6 +95,7 @@ export const TEXT = {
|
||||
shareThreads: 'Share on Threads',
|
||||
shareFacebook: 'Share on Facebook',
|
||||
shareLinkedIn: 'Share on LinkedIn',
|
||||
shareQRCode: 'Toggle QR Code',
|
||||
},
|
||||
theme: {
|
||||
theme: 'Theme',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Threads पर साझा करें',
|
||||
shareFacebook: 'Facebook पर साझा करें',
|
||||
shareLinkedIn: 'LinkedIn पर साझा करें',
|
||||
shareQRCode: 'QR कोड टॉगल करें',
|
||||
},
|
||||
theme: {
|
||||
theme: 'थीम',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Bagikan di Threads',
|
||||
shareFacebook: 'Bagikan di Facebook',
|
||||
shareLinkedIn: 'Bagikan di LinkedIn',
|
||||
shareQRCode: 'Alihkan Kode QR',
|
||||
},
|
||||
theme: {
|
||||
theme: 'Tema',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Compartilhar no Threads',
|
||||
shareFacebook: 'Compartilhar no Facebook',
|
||||
shareLinkedIn: 'Compartilhar no LinkedIn',
|
||||
shareQRCode: 'Alternar Código QR',
|
||||
},
|
||||
theme: {
|
||||
theme: 'Tema',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Partilhar no Threads',
|
||||
shareFacebook: 'Partilhar no Facebook',
|
||||
shareLinkedIn: 'Partilhar no LinkedIn',
|
||||
shareQRCode: 'Alternar Código QR',
|
||||
},
|
||||
theme: {
|
||||
theme: 'Tema',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: 'Threads\'te Paylaş',
|
||||
shareFacebook: 'Facebook\'ta Paylaş',
|
||||
shareLinkedIn: 'LinkedIn\'de Paylaş',
|
||||
shareQRCode: 'QR Kodunu Göster/Gizle',
|
||||
},
|
||||
theme: {
|
||||
theme: 'Tema',
|
||||
|
||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
||||
shareThreads: '在 Threads 上分享',
|
||||
shareFacebook: '在 Facebook 上分享',
|
||||
shareLinkedIn: '在 LinkedIn 上分享',
|
||||
shareQRCode: '切换二维码',
|
||||
},
|
||||
theme: {
|
||||
theme: '主题',
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import Modal from '@/components/Modal';
|
||||
import { TbPhotoShare } from 'react-icons/tb';
|
||||
import { TbPhotoShare, TbQrcode } from 'react-icons/tb';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { BiCopy } from 'react-icons/bi';
|
||||
import { ReactNode, useCallback, useEffect } from 'react';
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import { shortenUrl } from '@/utility/url';
|
||||
import { toastSuccess } from '@/toast';
|
||||
import { SOCIAL_NETWORKS } from '@/app/config';
|
||||
@ -15,6 +15,7 @@ import MaskedScroll from '@/components/MaskedScroll';
|
||||
import { useAppText } from '@/i18n/state/client';
|
||||
import SocialButton from '@/social/SocialButton';
|
||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||
import Image from 'next/image';
|
||||
|
||||
const BUTTON_COLOR_CLASSNAMES = clsx(
|
||||
'border-gray-200 bg-gray-50 active:bg-gray-100',
|
||||
@ -43,6 +44,7 @@ export default function ShareModal({
|
||||
} = useAppState();
|
||||
|
||||
const appText = useAppText();
|
||||
const [showQR, setShowQR] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setShouldRespondToKeyboardCommands?.(false);
|
||||
@ -90,7 +92,25 @@ export default function ShareModal({
|
||||
{title}
|
||||
</div>
|
||||
</div>}
|
||||
{children}
|
||||
{!showQR ? (
|
||||
<>{children}</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center gap-4 p-4">
|
||||
<div className={clsx(
|
||||
'p-3 bg-white rounded-2xl shadow-lg border',
|
||||
'flex items-center justify-center',
|
||||
)}>
|
||||
<Image
|
||||
/* eslint-disable-next-line max-len */
|
||||
src={`https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(pathShare)}`}
|
||||
alt="QR Code"
|
||||
className="rounded-xl bg-white"
|
||||
width={300}
|
||||
height={300}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-stretch h-10 gap-2">
|
||||
<div className={clsx(
|
||||
'rounded-md',
|
||||
@ -119,16 +139,26 @@ export default function ShareModal({
|
||||
)}
|
||||
</div>
|
||||
{SOCIAL_NETWORKS.map(key =>
|
||||
<SocialButton
|
||||
key={key}
|
||||
socialKey={key}
|
||||
path={pathShare}
|
||||
text={socialText}
|
||||
className={clsx(
|
||||
'h-full',
|
||||
BUTTON_COLOR_CLASSNAMES,
|
||||
)}
|
||||
/>)}
|
||||
key === 'qrcode' ? (
|
||||
renderButton(
|
||||
<TbQrcode size={18} />,
|
||||
() => setShowQR(q => !q),
|
||||
false,
|
||||
appText.tooltip.shareQRCode,
|
||||
)
|
||||
) : (
|
||||
<SocialButton
|
||||
key={key}
|
||||
socialKey={key}
|
||||
path={pathShare}
|
||||
text={socialText}
|
||||
className={clsx(
|
||||
'h-full',
|
||||
BUTTON_COLOR_CLASSNAMES,
|
||||
)}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{typeof navigator !== 'undefined' && navigator.share &&
|
||||
renderButton(
|
||||
<IoArrowUp size={18} />,
|
||||
|
||||
@ -5,6 +5,7 @@ import Link from 'next/link';
|
||||
import clsx from 'clsx/lite';
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import { useAppText } from '@/i18n/state/client';
|
||||
import { TbQrcode } from 'react-icons/tb';
|
||||
|
||||
const iconForSocialKey = (key: SocialKey) => {
|
||||
switch (key) {
|
||||
@ -12,6 +13,7 @@ const iconForSocialKey = (key: SocialKey) => {
|
||||
case 'threads': return <FaThreads size={18} />;
|
||||
case 'facebook': return <FaFacebookF size={14} />;
|
||||
case 'linkedin': return <FaLinkedin size={16} />;
|
||||
case 'qrcode': return <TbQrcode size={16} />;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ export const SOCIAL_KEYS = [
|
||||
'threads',
|
||||
'facebook',
|
||||
'linkedin',
|
||||
'qrcode',
|
||||
] as const;
|
||||
|
||||
export type SocialKey = (typeof SOCIAL_KEYS)[number];
|
||||
@ -49,6 +50,12 @@ export const urlForSocial = (
|
||||
url.searchParams.set('text', text);
|
||||
return url.toString();
|
||||
}
|
||||
case 'qrcode': {
|
||||
const url = new URL('https://api.qrserver.com/v1/create-qr-code/');
|
||||
url.searchParams.set('data', path);
|
||||
url.searchParams.set('size', '200x200');
|
||||
return url.toString();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -61,5 +68,6 @@ export const tooltipForSocial = (
|
||||
case 'threads': return tooltip.shareThreads;
|
||||
case 'facebook': return tooltip.shareFacebook;
|
||||
case 'linkedin': return tooltip.shareLinkedIn;
|
||||
case 'qrcode': return tooltip.shareQRCode;
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user