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_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_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)
|
||||||
- `NEXT_PUBLIC_SOCIAL_NETWORKS`
|
- `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:
|
- Accepted values:
|
||||||
- `x` (default)
|
- `x` (default)
|
||||||
- `threads`
|
- `threads`
|
||||||
- `facebook`
|
- `facebook`
|
||||||
- `linkedin`
|
- `linkedin`
|
||||||
|
- `qrcode`
|
||||||
- `all`
|
- `all`
|
||||||
- `none`
|
- `none`
|
||||||
- `NEXT_PUBLIC_SITE_FEEDS = 1` enables feeds at `/feed.json` and `/rss.xml`
|
- `NEXT_PUBLIC_SITE_FEEDS = 1` enables feeds at `/feed.json` and `/rss.xml`
|
||||||
|
|||||||
@ -39,7 +39,14 @@ const generateRemotePattern = (
|
|||||||
pathname: '/**',
|
pathname: '/**',
|
||||||
});
|
});
|
||||||
|
|
||||||
const remotePatterns: RemotePattern[] = [];
|
const remotePatterns: RemotePattern[] = [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'api.qrserver.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/v1/create-qr-code/**',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if (HOSTNAME_VERCEL_BLOB) {
|
if (HOSTNAME_VERCEL_BLOB) {
|
||||||
remotePatterns.push(generateRemotePattern(HOSTNAME_VERCEL_BLOB));
|
remotePatterns.push(generateRemotePattern(HOSTNAME_VERCEL_BLOB));
|
||||||
|
|||||||
@ -912,14 +912,14 @@ export default function AdminAppConfigurationClient({
|
|||||||
{renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
|
{renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
|
||||||
</ChecklistRow>
|
</ChecklistRow>
|
||||||
<ChecklistRow
|
<ChecklistRow
|
||||||
title="Social networks"
|
title="Social networks and sharing"
|
||||||
status={hasSocialKeys}
|
status={hasSocialKeys}
|
||||||
optional
|
optional
|
||||||
>
|
>
|
||||||
{renderOrderedKeyList(socialKeys, SOCIAL_KEYS)}
|
{renderOrderedKeyList(socialKeys, SOCIAL_KEYS)}
|
||||||
<div>
|
<div>
|
||||||
Configure order and visibility of social networks
|
Configure order and visibility of social networks and sharing
|
||||||
(seen in share modal) by storing comma-separated values
|
options (seen in share modal) by storing comma-separated values
|
||||||
(accepts {'"all"'} or {'"none"'},
|
(accepts {'"all"'} or {'"none"'},
|
||||||
defaults to {renderCommaSeparatedList(DEFAULT_SOCIAL_KEYS)})
|
defaults to {renderCommaSeparatedList(DEFAULT_SOCIAL_KEYS)})
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Threads এ শেয়ার করুন',
|
shareThreads: 'Threads এ শেয়ার করুন',
|
||||||
shareFacebook: 'Facebook এ শেয়ার করুন',
|
shareFacebook: 'Facebook এ শেয়ার করুন',
|
||||||
shareLinkedIn: 'LinkedIn এ শেয়ার করুন',
|
shareLinkedIn: 'LinkedIn এ শেয়ার করুন',
|
||||||
|
shareQRCode: 'QR কোড টগল করুন',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'থিম',
|
theme: 'থিম',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Share on Threads',
|
shareThreads: 'Share on Threads',
|
||||||
shareFacebook: 'Share on Facebook',
|
shareFacebook: 'Share on Facebook',
|
||||||
shareLinkedIn: 'Share on LinkedIn',
|
shareLinkedIn: 'Share on LinkedIn',
|
||||||
|
shareQRCode: 'Toggle QR Code',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
|
|||||||
@ -95,6 +95,7 @@ export const TEXT = {
|
|||||||
shareThreads: 'Share on Threads',
|
shareThreads: 'Share on Threads',
|
||||||
shareFacebook: 'Share on Facebook',
|
shareFacebook: 'Share on Facebook',
|
||||||
shareLinkedIn: 'Share on LinkedIn',
|
shareLinkedIn: 'Share on LinkedIn',
|
||||||
|
shareQRCode: 'Toggle QR Code',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Threads पर साझा करें',
|
shareThreads: 'Threads पर साझा करें',
|
||||||
shareFacebook: 'Facebook पर साझा करें',
|
shareFacebook: 'Facebook पर साझा करें',
|
||||||
shareLinkedIn: 'LinkedIn पर साझा करें',
|
shareLinkedIn: 'LinkedIn पर साझा करें',
|
||||||
|
shareQRCode: 'QR कोड टॉगल करें',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'थीम',
|
theme: 'थीम',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Bagikan di Threads',
|
shareThreads: 'Bagikan di Threads',
|
||||||
shareFacebook: 'Bagikan di Facebook',
|
shareFacebook: 'Bagikan di Facebook',
|
||||||
shareLinkedIn: 'Bagikan di LinkedIn',
|
shareLinkedIn: 'Bagikan di LinkedIn',
|
||||||
|
shareQRCode: 'Alihkan Kode QR',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Compartilhar no Threads',
|
shareThreads: 'Compartilhar no Threads',
|
||||||
shareFacebook: 'Compartilhar no Facebook',
|
shareFacebook: 'Compartilhar no Facebook',
|
||||||
shareLinkedIn: 'Compartilhar no LinkedIn',
|
shareLinkedIn: 'Compartilhar no LinkedIn',
|
||||||
|
shareQRCode: 'Alternar Código QR',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Partilhar no Threads',
|
shareThreads: 'Partilhar no Threads',
|
||||||
shareFacebook: 'Partilhar no Facebook',
|
shareFacebook: 'Partilhar no Facebook',
|
||||||
shareLinkedIn: 'Partilhar no LinkedIn',
|
shareLinkedIn: 'Partilhar no LinkedIn',
|
||||||
|
shareQRCode: 'Alternar Código QR',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: 'Threads\'te Paylaş',
|
shareThreads: 'Threads\'te Paylaş',
|
||||||
shareFacebook: 'Facebook\'ta Paylaş',
|
shareFacebook: 'Facebook\'ta Paylaş',
|
||||||
shareLinkedIn: 'LinkedIn\'de Paylaş',
|
shareLinkedIn: 'LinkedIn\'de Paylaş',
|
||||||
|
shareQRCode: 'QR Kodunu Göster/Gizle',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: 'Tema',
|
theme: 'Tema',
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export const TEXT: I18N = {
|
|||||||
shareThreads: '在 Threads 上分享',
|
shareThreads: '在 Threads 上分享',
|
||||||
shareFacebook: '在 Facebook 上分享',
|
shareFacebook: '在 Facebook 上分享',
|
||||||
shareLinkedIn: '在 LinkedIn 上分享',
|
shareLinkedIn: '在 LinkedIn 上分享',
|
||||||
|
shareQRCode: '切换二维码',
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
theme: '主题',
|
theme: '主题',
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import { TbPhotoShare } from 'react-icons/tb';
|
import { TbPhotoShare, TbQrcode } from 'react-icons/tb';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import { BiCopy } from 'react-icons/bi';
|
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 { shortenUrl } from '@/utility/url';
|
||||||
import { toastSuccess } from '@/toast';
|
import { toastSuccess } from '@/toast';
|
||||||
import { SOCIAL_NETWORKS } from '@/app/config';
|
import { SOCIAL_NETWORKS } from '@/app/config';
|
||||||
@ -15,6 +15,7 @@ import MaskedScroll from '@/components/MaskedScroll';
|
|||||||
import { useAppText } from '@/i18n/state/client';
|
import { useAppText } from '@/i18n/state/client';
|
||||||
import SocialButton from '@/social/SocialButton';
|
import SocialButton from '@/social/SocialButton';
|
||||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
const BUTTON_COLOR_CLASSNAMES = clsx(
|
const BUTTON_COLOR_CLASSNAMES = clsx(
|
||||||
'border-gray-200 bg-gray-50 active:bg-gray-100',
|
'border-gray-200 bg-gray-50 active:bg-gray-100',
|
||||||
@ -43,6 +44,7 @@ export default function ShareModal({
|
|||||||
} = useAppState();
|
} = useAppState();
|
||||||
|
|
||||||
const appText = useAppText();
|
const appText = useAppText();
|
||||||
|
const [showQR, setShowQR] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShouldRespondToKeyboardCommands?.(false);
|
setShouldRespondToKeyboardCommands?.(false);
|
||||||
@ -90,7 +92,25 @@ export default function ShareModal({
|
|||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
</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="flex items-stretch h-10 gap-2">
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
'rounded-md',
|
'rounded-md',
|
||||||
@ -119,6 +139,14 @@ export default function ShareModal({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{SOCIAL_NETWORKS.map(key =>
|
{SOCIAL_NETWORKS.map(key =>
|
||||||
|
key === 'qrcode' ? (
|
||||||
|
renderButton(
|
||||||
|
<TbQrcode size={18} />,
|
||||||
|
() => setShowQR(q => !q),
|
||||||
|
false,
|
||||||
|
appText.tooltip.shareQRCode,
|
||||||
|
)
|
||||||
|
) : (
|
||||||
<SocialButton
|
<SocialButton
|
||||||
key={key}
|
key={key}
|
||||||
socialKey={key}
|
socialKey={key}
|
||||||
@ -128,7 +156,9 @@ export default function ShareModal({
|
|||||||
'h-full',
|
'h-full',
|
||||||
BUTTON_COLOR_CLASSNAMES,
|
BUTTON_COLOR_CLASSNAMES,
|
||||||
)}
|
)}
|
||||||
/>)}
|
/>
|
||||||
|
),
|
||||||
|
)}
|
||||||
{typeof navigator !== 'undefined' && navigator.share &&
|
{typeof navigator !== 'undefined' && navigator.share &&
|
||||||
renderButton(
|
renderButton(
|
||||||
<IoArrowUp size={18} />,
|
<IoArrowUp size={18} />,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import Link from 'next/link';
|
|||||||
import clsx from 'clsx/lite';
|
import clsx from 'clsx/lite';
|
||||||
import Tooltip from '@/components/Tooltip';
|
import Tooltip from '@/components/Tooltip';
|
||||||
import { useAppText } from '@/i18n/state/client';
|
import { useAppText } from '@/i18n/state/client';
|
||||||
|
import { TbQrcode } from 'react-icons/tb';
|
||||||
|
|
||||||
const iconForSocialKey = (key: SocialKey) => {
|
const iconForSocialKey = (key: SocialKey) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -12,6 +13,7 @@ const iconForSocialKey = (key: SocialKey) => {
|
|||||||
case 'threads': return <FaThreads size={18} />;
|
case 'threads': return <FaThreads size={18} />;
|
||||||
case 'facebook': return <FaFacebookF size={14} />;
|
case 'facebook': return <FaFacebookF size={14} />;
|
||||||
case 'linkedin': return <FaLinkedin size={16} />;
|
case 'linkedin': return <FaLinkedin size={16} />;
|
||||||
|
case 'qrcode': return <TbQrcode size={16} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export const SOCIAL_KEYS = [
|
|||||||
'threads',
|
'threads',
|
||||||
'facebook',
|
'facebook',
|
||||||
'linkedin',
|
'linkedin',
|
||||||
|
'qrcode',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type SocialKey = (typeof SOCIAL_KEYS)[number];
|
export type SocialKey = (typeof SOCIAL_KEYS)[number];
|
||||||
@ -49,6 +50,12 @@ export const urlForSocial = (
|
|||||||
url.searchParams.set('text', text);
|
url.searchParams.set('text', text);
|
||||||
return url.toString();
|
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 'threads': return tooltip.shareThreads;
|
||||||
case 'facebook': return tooltip.shareFacebook;
|
case 'facebook': return tooltip.shareFacebook;
|
||||||
case 'linkedin': return tooltip.shareLinkedIn;
|
case 'linkedin': return tooltip.shareLinkedIn;
|
||||||
|
case 'qrcode': return tooltip.shareQRCode;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user