|
|
|
@ -1,37 +1,54 @@
|
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
|
|
|
import { Listbox, Transition } from '@headlessui/react';
|
|
|
|
|
import { countryCodeEmoji } from 'country-code-emoji';
|
|
|
|
|
import useSWR from 'swr';
|
|
|
|
|
import type { Region } from '../../../server/lib/settings';
|
|
|
|
|
import { defineMessages, useIntl } from 'react-intl';
|
|
|
|
|
import useSettings from '../../hooks/useSettings';
|
|
|
|
|
|
|
|
|
|
const messages = defineMessages({
|
|
|
|
|
regionDefault: 'All Regions',
|
|
|
|
|
regionServerDefault: '{applicationTitle} Default ({region})',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
interface RegionSelectorProps {
|
|
|
|
|
value: string;
|
|
|
|
|
name: string;
|
|
|
|
|
isUserSetting?: boolean;
|
|
|
|
|
onChange?: (fieldName: string, region: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const RegionSelector: React.FC<RegionSelectorProps> = ({
|
|
|
|
|
name,
|
|
|
|
|
value,
|
|
|
|
|
isUserSetting = false,
|
|
|
|
|
onChange,
|
|
|
|
|
}) => {
|
|
|
|
|
const { currentSettings } = useSettings();
|
|
|
|
|
const intl = useIntl();
|
|
|
|
|
const { data: regions } = useSWR<Region[]>('/api/v1/regions');
|
|
|
|
|
const [selectedRegion, setSelectedRegion] = useState<Region | null>(null);
|
|
|
|
|
|
|
|
|
|
const allRegion: Region = useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
iso_3166_1: 'all',
|
|
|
|
|
english_name: 'All',
|
|
|
|
|
}),
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (regions && value) {
|
|
|
|
|
const matchedRegion = regions.find(
|
|
|
|
|
(region) => region.iso_3166_1 === value
|
|
|
|
|
);
|
|
|
|
|
setSelectedRegion(matchedRegion ?? null);
|
|
|
|
|
if (value === 'all') {
|
|
|
|
|
setSelectedRegion(allRegion);
|
|
|
|
|
} else {
|
|
|
|
|
const matchedRegion = regions.find(
|
|
|
|
|
(region) => region.iso_3166_1 === value
|
|
|
|
|
);
|
|
|
|
|
setSelectedRegion(matchedRegion ?? null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [value, regions]);
|
|
|
|
|
}, [value, regions, allRegion]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (onChange && regions) {
|
|
|
|
@ -47,15 +64,26 @@ const RegionSelector: React.FC<RegionSelectorProps> = ({
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<span className="inline-block w-full rounded-md shadow-sm">
|
|
|
|
|
<Listbox.Button className="relative flex items-center w-full py-2 pl-3 pr-10 text-left text-white transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5">
|
|
|
|
|
{selectedRegion && (
|
|
|
|
|
{selectedRegion && selectedRegion.iso_3166_1 !== 'all' && (
|
|
|
|
|
<span className="h-4 mr-2 overflow-hidden text-lg leading-4">
|
|
|
|
|
{countryCodeEmoji(selectedRegion.iso_3166_1)}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
<span className="block truncate">
|
|
|
|
|
{selectedRegion
|
|
|
|
|
{selectedRegion && selectedRegion.iso_3166_1 !== 'all'
|
|
|
|
|
? intl.formatDisplayName(selectedRegion.iso_3166_1, {
|
|
|
|
|
type: 'region',
|
|
|
|
|
fallback: 'none',
|
|
|
|
|
}) ?? selectedRegion.english_name
|
|
|
|
|
: isUserSetting && selectedRegion?.iso_3166_1 !== 'all'
|
|
|
|
|
? intl.formatMessage(messages.regionServerDefault, {
|
|
|
|
|
applicationTitle: currentSettings.applicationTitle,
|
|
|
|
|
region: currentSettings.region
|
|
|
|
|
? intl.formatDisplayName(currentSettings.region, {
|
|
|
|
|
type: 'region',
|
|
|
|
|
fallback: 'none',
|
|
|
|
|
}) ?? currentSettings.region
|
|
|
|
|
: intl.formatMessage(messages.regionDefault),
|
|
|
|
|
})
|
|
|
|
|
: intl.formatMessage(messages.regionDefault)}
|
|
|
|
|
</span>
|
|
|
|
@ -89,7 +117,60 @@ const RegionSelector: React.FC<RegionSelectorProps> = ({
|
|
|
|
|
static
|
|
|
|
|
className="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5"
|
|
|
|
|
>
|
|
|
|
|
<Listbox.Option value={null}>
|
|
|
|
|
{isUserSetting && (
|
|
|
|
|
<Listbox.Option value={null}>
|
|
|
|
|
{({ selected, active }) => (
|
|
|
|
|
<div
|
|
|
|
|
className={`${
|
|
|
|
|
active
|
|
|
|
|
? 'text-white bg-indigo-600'
|
|
|
|
|
: 'text-gray-300'
|
|
|
|
|
} cursor-default select-none relative py-2 pl-8 pr-4`}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
className={`${
|
|
|
|
|
selected ? 'font-semibold' : 'font-normal'
|
|
|
|
|
} block truncate`}
|
|
|
|
|
>
|
|
|
|
|
{intl.formatMessage(messages.regionServerDefault, {
|
|
|
|
|
applicationTitle:
|
|
|
|
|
currentSettings.applicationTitle,
|
|
|
|
|
region: currentSettings.region
|
|
|
|
|
? intl.formatDisplayName(
|
|
|
|
|
currentSettings.region,
|
|
|
|
|
{
|
|
|
|
|
type: 'region',
|
|
|
|
|
fallback: 'none',
|
|
|
|
|
}
|
|
|
|
|
) ?? currentSettings.region
|
|
|
|
|
: intl.formatMessage(messages.regionDefault),
|
|
|
|
|
})}
|
|
|
|
|
</span>
|
|
|
|
|
{selected && (
|
|
|
|
|
<span
|
|
|
|
|
className={`${
|
|
|
|
|
active ? 'text-white' : 'text-indigo-600'
|
|
|
|
|
} absolute inset-y-0 left-0 flex items-center pl-1.5`}
|
|
|
|
|
>
|
|
|
|
|
<svg
|
|
|
|
|
className="w-5 h-5"
|
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
viewBox="0 0 20 20"
|
|
|
|
|
fill="currentColor"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
fillRule="evenodd"
|
|
|
|
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
|
|
|
clipRule="evenodd"
|
|
|
|
|
/>
|
|
|
|
|
</svg>
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</Listbox.Option>
|
|
|
|
|
)}
|
|
|
|
|
<Listbox.Option value={isUserSetting ? allRegion : null}>
|
|
|
|
|
{({ selected, active }) => (
|
|
|
|
|
<div
|
|
|
|
|
className={`${
|
|
|
|
|