Extracted utils

This commit is contained in:
Nick Sandstrom 2026-04-08 23:13:34 -07:00
parent 93a4846924
commit 30b97c4fc5
7 changed files with 865 additions and 0 deletions

View file

@ -45,6 +45,32 @@ export const toFriendlyDuration = (dateTime, unit) =>
export const fromNow = (dateTime) => dayjs(dateTime).fromNow();
export const setTz = (dateTime, timeZone) => dayjs(dateTime).tz(timeZone);
export const setMonth = (dateTime, value) => dayjs(dateTime).month(value);
export const setYear = (dateTime, value) => dayjs(dateTime).year(value);
export const setDay = (dateTime, value) => dayjs(dateTime).date(value);
export const setHour = (dateTime, value) => dayjs(dateTime).hour(value);
export const setMinute = (dateTime, value) => dayjs(dateTime).minute(value);
export const setSecond = (dateTime, value) => dayjs(dateTime).second(value);
export const getMonth = (dateTime) => dayjs(dateTime).month();
export const getYear = (dateTime) => dayjs(dateTime).year();
export const getDay = (dateTime) => dayjs(dateTime).date();
export const getHour = (dateTime) => dayjs(dateTime).hour();
export const getMinute = (dateTime) => dayjs(dateTime).minute();
export const getSecond = (dateTime) => dayjs(dateTime).second();
export const getNowMs = () => Date.now();
export const roundToNearest = (dateTime, minutes) => {
@ -281,3 +307,33 @@ export const getDefaultTimeZone = () => {
return 'UTC';
}
};
export const MONTH_NAMES = [
'january',
'february',
'march',
'april',
'may',
'june',
'july',
'august',
'september',
'october',
'november',
'december',
];
export const MONTH_ABBR = [
'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
'sep',
'oct',
'nov',
'dec',
];

View file

@ -0,0 +1,54 @@
// Helper function to format timestamps
import API from '../../api.js';
export const formatTimestamp = (timestamp) => {
if (!timestamp) return 'Unknown';
try {
const date =
typeof timestamp === 'string' && timestamp.includes('T')
? new Date(timestamp) // This should handle ISO format properly
: new Date(parseInt(timestamp) * 1000);
// Convert to user's local time and display with timezone
return date.toLocaleString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short',
});
} catch {
return 'Invalid date';
}
};
// Helper function to get time remaining
export const getTimeRemaining = (expTimestamp) => {
if (!expTimestamp) return null;
try {
const now = new Date();
const expDate = new Date(parseInt(expTimestamp) * 1000);
const diffMs = expDate - now;
if (diffMs <= 0) return 'Expired';
const MS_PER_HOUR = 1000 * 60 * 60;
const MS_PER_DAY = MS_PER_HOUR * 24;
const days = Math.floor(diffMs / MS_PER_DAY);
const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR);
const dayLabel = `${days} ${days === 1 ? 'day' : 'days'}`;
const hourLabel = `${hours} ${hours === 1 ? 'hour' : 'hours'}`;
return days > 0 ? `${dayLabel} ${hourLabel}` : hourLabel;
} catch {
return 'Unknown';
}
};
export const refreshAccountInfo = (currentProfile) => {
return API.refreshAccountInfo(currentProfile.id);
};

View file

@ -0,0 +1,186 @@
import API from '../../api.js';
export const getChannelGroupChange = (selectedChannelGroup, channelGroups) => {
if (!selectedChannelGroup || selectedChannelGroup === '-1') return null;
const groupName = channelGroups[selectedChannelGroup]?.name || 'Unknown';
return `• Channel Group: ${groupName}`;
};
export const getLogoChange = (selectedLogoId, channelLogos) => {
if (!selectedLogoId || selectedLogoId === '-1') return null;
if (selectedLogoId === '0') return `• Logo: Use Default`;
const logoName = channelLogos[selectedLogoId]?.name || 'Selected Logo';
return `• Logo: ${logoName}`;
};
export const getStreamProfileChange = (streamProfileId, streamProfiles) => {
if (!streamProfileId || streamProfileId === '-1') return null;
if (streamProfileId === '0') return `• Stream Profile: Use Default`;
const profile = streamProfiles.find(
(p) => `${p.id}` === `${streamProfileId}`
);
return `• Stream Profile: ${profile?.name || 'Selected Profile'}`;
};
export const getUserLevelChange = (userLevel, userLevelLabels) => {
if (!userLevel || userLevel === '-1') return null;
return `• User Level: ${userLevelLabels[userLevel] || userLevel}`;
};
export const getMatureContentChange = (isAdult) => {
if (!isAdult || isAdult === '-1') return null;
return `• Mature Content: ${isAdult === 'true' ? 'Yes' : 'No'}`;
};
export const getRegexNameChange = (regexFind, regexReplace) => {
if (!regexFind?.trim()) return null;
return `• Name Change: Apply regex find "${regexFind}" replace with "${regexReplace || ''}"`;
};
export const getEpgChange = (selectedDummyEpgId, epgs) => {
if (!selectedDummyEpgId) return null;
if (selectedDummyEpgId === 'clear')
return `• EPG: Clear Assignment (use default dummy)`;
const epgName = epgs[selectedDummyEpgId]?.name || 'Selected EPG';
return `• Dummy EPG: ${epgName}`;
};
export const updateChannels = (channelIds, values) => {
return API.updateChannels(channelIds, values);
};
export const bulkRegexRenameChannels = (
channelIds,
regexFind,
regexReplace,
flags
) => {
return API.bulkRegexRenameChannels(
channelIds,
regexFind,
regexReplace ?? '',
flags
);
};
export const batchSetEPG = (associations) => {
return API.batchSetEPG(associations);
};
export const getEpgData = () => {
return API.getEPGData();
};
export const setChannelNamesFromEpg = (channelIds) => {
return API.setChannelNamesFromEpg(channelIds);
};
export const setChannelLogosFromEpg = (channelIds) => {
return API.setChannelLogosFromEpg(channelIds);
};
export const setChannelTvgIdsFromEpg = (channelIds) => {
return API.setChannelTvgIdsFromEpg(channelIds);
};
export const computeRegexPreview = (
channelIds,
nameById,
find,
replace,
limit = 25
) => {
if (!find) return [];
let re;
try {
re = new RegExp(find, 'g');
} catch (error) {
console.error('Invalid regex:', error);
return [{ before: 'Invalid regex', after: '' }];
}
// Limit preview to items that exist on the current page
const pageOnlyIds = channelIds.filter((id) => nameById[id] !== undefined);
const items = [];
for (let i = 0; i < Math.min(pageOnlyIds.length, limit); i++) {
const before = nameById[pageOnlyIds[i]] ?? '';
const after = before.replace(re, replace ?? '');
if (before !== after) items.push({ before, after });
}
return items;
};
export const buildSubmitValues = (
formValues,
selectedChannelGroup,
selectedLogoId
) => {
const values = { ...formValues };
// Handle channel group ID - convert to integer if it exists
if (selectedChannelGroup && selectedChannelGroup !== '-1') {
values.channel_group_id = parseInt(selectedChannelGroup);
} else {
delete values.channel_group_id;
}
if (selectedLogoId && selectedLogoId !== '-1') {
values.logo_id = selectedLogoId === '0' ? null : parseInt(selectedLogoId);
}
delete values.logo;
// Remove the channel_group field from form values as we use channel_group_id
delete values.channel_group;
// Handle stream profile ID - convert special values
if (!values.stream_profile_id || values.stream_profile_id === '-1') {
delete values.stream_profile_id;
} else if (
values.stream_profile_id === '0' ||
values.stream_profile_id === 0
) {
values.stream_profile_id = null; // Convert "use default" to null
}
if (values.user_level == '-1') delete values.user_level;
if (values.is_adult === '-1') {
delete values.is_adult;
} else {
values.is_adult = values.is_adult === 'true';
}
return values;
};
export const buildEpgAssociations = async (
selectedDummyEpgId,
channelIds,
epgs,
tvgs
) => {
if (!selectedDummyEpgId) return null;
if (selectedDummyEpgId === 'clear') {
// Clear EPG assignments
return channelIds.map((id) => ({ channel_id: id, epg_data_id: null }));
}
// Assign the selected dummy EPG
const selectedEpg = epgs[selectedDummyEpgId];
if (!selectedEpg?.epg_data_count) return null;
const epgSourceId = parseInt(selectedDummyEpgId, 10);
// Check if we already have EPG data loaded in the store
let epgData = tvgs.find((data) => data.epg_source === epgSourceId);
if (!epgData) {
const epgDataList = await getEpgData();
epgData = epgDataList.find((data) => data.epg_source === epgSourceId);
}
if (!epgData) return null;
return channelIds.map((id) => ({ channel_id: id, epg_data_id: epgData.id }));
};

View file

@ -0,0 +1,97 @@
import API from '../../api.js';
export const matchChannelEpg = (channel) => {
return API.matchChannelEpg(channel.id);
};
export const createLogo = (newLogoData) => {
return API.createLogo(newLogoData);
};
const setChannelEPG = (channel, values) => {
return API.setChannelEPG(channel.id, values.epg_data_id);
};
const updateChannel = (values) => {
return API.updateChannel(values);
};
export const addChannel = (channel) => {
return API.addChannel(channel);
};
export const requeryChannels = () => {
API.requeryChannels();
};
export const getChannelFormDefaultValues = (channel, channelGroups) => {
return {
name: channel?.name || '',
channel_number:
channel?.channel_number !== null && channel?.channel_number !== undefined
? channel.channel_number
: '',
channel_group_id: channel?.channel_group_id
? `${channel.channel_group_id}`
: Object.keys(channelGroups).length > 0
? Object.keys(channelGroups)[0]
: '',
stream_profile_id: channel?.stream_profile_id
? `${channel.stream_profile_id}`
: '0',
tvg_id: channel?.tvg_id || '',
tvc_guide_stationid: channel?.tvc_guide_stationid || '',
epg_data_id: channel?.epg_data_id ?? '',
logo_id: channel?.logo_id ? `${channel.logo_id}` : '',
user_level: `${channel?.user_level ?? '0'}`,
is_adult: channel?.is_adult ?? false,
};
};
export const getFormattedValues = (values) => {
const formattedValues = { ...values };
// Convert empty or "0" stream_profile_id to null for the API
if (
!formattedValues.stream_profile_id ||
formattedValues.stream_profile_id === '0'
) {
formattedValues.stream_profile_id = null;
}
// Ensure tvg_id is properly included (no empty strings)
formattedValues.tvg_id = formattedValues.tvg_id || null;
// Ensure tvc_guide_stationid is properly included (no empty strings)
formattedValues.tvc_guide_stationid =
formattedValues.tvc_guide_stationid || null;
return formattedValues;
};
export const handleEpgUpdate = async (
channel,
values,
formattedValues,
channelStreams
) => {
// If there's an EPG to set, use our enhanced endpoint
if (values.epg_data_id !== (channel.epg_data_id ?? '')) {
// Use the special endpoint to set EPG and trigger refresh
await setChannelEPG(channel, values);
// Remove epg_data_id from values since we've handled it separately
const { epg_data_id: _epg_data_id, ...otherValues } = formattedValues;
// Update other channel fields if needed
if (Object.keys(otherValues).length > 0) {
await updateChannel({
id: channel.id,
...otherValues,
streams: channelStreams.map((stream) => stream.id),
});
}
} else {
// No EPG change, regular update
await updateChannel({
id: channel.id,
...formattedValues,
streams: channelStreams.map((stream) => stream.id),
});
}
};

View file

@ -0,0 +1,75 @@
import API from '../../api.js';
import { SUBSCRIPTION_EVENTS } from '../../constants.js';
export const EVENT_OPTIONS = Object.entries(SUBSCRIPTION_EVENTS).map(
([value, label]) => ({
value,
label,
})
);
export const updateConnectIntegration = (connection, values, config) => {
return API.updateConnectIntegration(connection.id, {
name: values.name,
type: values.type,
config,
enabled: values.enabled,
});
};
export const createConnectIntegration = (values, config) => {
return API.createConnectIntegration({
name: values.name,
type: values.type,
config,
enabled: values.enabled,
});
};
export const setConnectSubscriptions = (connection, subs) => {
return API.setConnectSubscriptions(connection.id, subs);
};
const buildWebhookConfig = (url, headers) => {
const hdrs = {};
headers.forEach((h) => {
if (h.key && h.key.trim()) hdrs[h.key] = h.value;
});
const config = { url };
if (Object.keys(hdrs).length) config.headers = hdrs;
return config;
};
const buildScriptConfig = (scriptPath) => ({ path: scriptPath });
export const buildConfig = (values, headers) =>
values.type === 'webhook'
? buildWebhookConfig(values.url, headers)
: buildScriptConfig(values.script_path);
export const buildSubscriptions = (selectedEvents, payloadTemplates) =>
Object.keys(SUBSCRIPTION_EVENTS).map((event) => ({
event,
enabled: selectedEvents.includes(event),
payload_template: payloadTemplates[event] ?? null,
}));
export const parseApiError = (error) => {
const body = error?.body;
if (!body || typeof body !== 'object') {
return { fieldErrors: {}, apiError: error?.message ?? 'Unknown error' };
}
const knownFields = ['name', 'type'];
const fieldErrors = Object.fromEntries(
knownFields.filter((f) => body[f]).map((f) => [f, body[f]])
);
const nonField = body.non_field_errors ?? body.detail ?? null;
const apiError =
nonField ??
(Object.keys(fieldErrors).length === 0 ? JSON.stringify(body) : '');
return { fieldErrors, apiError };
};

View file

@ -0,0 +1,131 @@
export const PRESETS = [
{
label: 'Every hour',
value: '0 * * * *',
description: 'At the start of every hour',
},
{
label: 'Every 6 hours',
value: '0 */6 * * *',
description: 'Every 6 hours starting at midnight',
},
{
label: 'Every 12 hours',
value: '0 */12 * * *',
description: 'Twice daily at midnight and noon',
},
{
label: 'Daily at midnight',
value: '0 0 * * *',
description: 'Once per day at 12:00 AM',
},
{
label: 'Daily at 3 AM',
value: '0 3 * * *',
description: 'Once per day at 3:00 AM',
},
{
label: 'Daily at noon',
value: '0 12 * * *',
description: 'Once per day at 12:00 PM',
},
{
label: 'Weekly (Sunday midnight)',
value: '0 0 * * 0',
description: 'Once per week on Sunday',
},
{
label: 'Weekly (Monday 3 AM)',
value: '0 3 * * 1',
description: 'Once per week on Monday',
},
{
label: 'Monthly (1st at 2:30 AM)',
value: '30 2 1 * *',
description: 'First day of each month',
},
];
export const DAYS_OF_WEEK = [
{ value: '*', label: 'Every day' },
{ value: '0', label: 'Sunday' },
{ value: '1', label: 'Monday' },
{ value: '2', label: 'Tuesday' },
{ value: '3', label: 'Wednesday' },
{ value: '4', label: 'Thursday' },
{ value: '5', label: 'Friday' },
{ value: '6', label: 'Saturday' },
];
export const FREQUENCY_OPTIONS = [
{ value: 'hourly', label: 'Hourly' },
{ value: 'daily', label: 'Daily' },
{ value: 'weekly', label: 'Weekly' },
{ value: 'monthly', label: 'Monthly' },
];
export const buildCron = (frequency, minute, hour, dayOfWeek, dayOfMonth) => {
switch (frequency) {
case 'hourly':
return `${minute} * * * *`;
case 'daily':
return `${minute} ${hour} * * *`;
case 'weekly':
return `${minute} ${hour} * * ${dayOfWeek === '*' ? '0' : dayOfWeek}`;
case 'monthly':
return `${minute} ${hour} ${dayOfMonth} * *`;
default:
return '* * * * *';
}
};
const parseHour = (hr) => parseInt(hr.replace('*/', '').replace('*', '0')) || 0;
export const parseCronPreset = (cron) => {
const [min, hr, day, _month, weekday] = cron.split(' ');
const minute = parseInt(min) || 0;
const hour = parseHour(hr);
if (hr === '*')
return { frequency: 'hourly', minute, hour, dayOfWeek: '*', dayOfMonth: 1 };
if (weekday !== '*')
return {
frequency: 'weekly',
minute,
hour,
dayOfWeek: weekday,
dayOfMonth: 1,
};
if (day !== '*')
return {
frequency: 'monthly',
minute,
hour,
dayOfWeek: '*',
dayOfMonth: parseInt(day) || 1,
};
return { frequency: 'daily', minute, hour, dayOfWeek: '*', dayOfMonth: 1 };
};
export const CRON_FIELDS = [
{ index: 0, label: 'Minute (0-59)', placeholder: '*, 0, */15, 0,15,30,45' },
{ index: 1, label: 'Hour (0-23)', placeholder: '*, 0, 9-17, */6, 2,4,16' },
{
index: 2,
label: 'Day of Month (1-31)',
placeholder: '*, 1, 1-15, */2, 1,15',
},
{ index: 3, label: 'Month (1-12)', placeholder: '*, 1, 1-6, */3, 6,12' },
{
index: 4,
label: 'Day of Week (0-6, Sun-Sat)',
placeholder: '*, 0, 1-5, 0,6',
},
];
export const updateCronPart = (cron, index, value) => {
const parts =
cron.split(' ').length >= 5 ? cron.split(' ') : ['*', '*', '*', '*', '*'];
parts[index] = value || '*';
return parts.join(' ');
};

View file

@ -0,0 +1,266 @@
import API from '../../api.js';
import {
format,
getDay,
getHour,
getMinute,
getMonth,
getNow,
getYear,
MONTH_ABBR,
MONTH_NAMES,
setHour,
setMinute,
setSecond,
setTz,
} from '../dateTimeUtils.js';
export const getTimezones = () => {
return API.getTimezones();
};
export const updateEPG = (values, epg) => {
return API.updateEPG({ ...values, id: epg.id });
};
export const addEPG = (values) => {
return API.addEPG(values);
};
export const getDummyEpgFormInitialValues = () => {
return {
name: '',
is_active: true,
source_type: 'dummy',
custom_properties: buildCustomProperties({}),
};
};
export const buildCustomProperties = (custom = {}) => ({
title_pattern: custom.title_pattern || '',
time_pattern: custom.time_pattern || '',
date_pattern: custom.date_pattern || '',
timezone:
custom.timezone || custom.timezone_offset?.toString() || 'US/Eastern',
output_timezone: custom.output_timezone || '',
program_duration: custom.program_duration || 180,
sample_title: custom.sample_title || '',
title_template: custom.title_template || '',
subtitle_template: custom.subtitle_template || '',
description_template: custom.description_template || '',
upcoming_title_template: custom.upcoming_title_template || '',
upcoming_description_template: custom.upcoming_description_template || '',
ended_title_template: custom.ended_title_template || '',
ended_description_template: custom.ended_description_template || '',
fallback_title_template: custom.fallback_title_template || '',
fallback_description_template: custom.fallback_description_template || '',
channel_logo_url: custom.channel_logo_url || '',
program_poster_url: custom.program_poster_url || '',
name_source: custom.name_source || 'channel',
stream_index: custom.stream_index || 1,
category: custom.category || '',
include_date: custom.include_date ?? true,
include_live: custom.include_live ?? false,
include_new: custom.include_new ?? false,
});
export const validateCustomTitlePattern = (value) => {
if (!value?.trim()) return 'Title pattern is required';
try {
new RegExp(value);
return null;
} catch (e) {
return `Invalid regex: ${e.message}`;
}
};
export const validateCustomNameSource = (value) => {
if (!value) return 'Name source is required';
return null;
};
export const validateCustomStreamIndex = (values, value) => {
if (values.custom_properties?.name_source === 'stream') {
if (!value || value < 1) {
return 'Stream index must be at least 1';
}
}
return null;
};
export const matchPattern = (pattern, input, errorPrefix) => {
if (!pattern || !input) return { matched: false, groups: {}, error: null };
try {
const match = input.match(new RegExp(pattern));
return match
? { matched: true, groups: match.groups || {}, error: null }
: { matched: false, groups: {}, error: null };
} catch (e) {
return {
matched: false,
groups: {},
error: `${errorPrefix}: ${e.message}`,
};
}
};
export const addNormalizedGroups = (groups) => {
const result = { ...groups };
Object.keys(groups).forEach((key) => {
if (groups[key]) {
result[`${key}_normalize`] = String(groups[key])
.replace(/[^a-zA-Z0-9\s]/g, '')
.replace(/\s+/g, '')
.toLowerCase();
}
});
return result;
};
const formatTime12 = (h, m) => {
const period = h < 12 ? 'AM' : 'PM';
let h12 = h % 12 || 12;
return m > 0
? `${h12}:${String(m).padStart(2, '0')} ${period}`
: `${h12} ${period}`;
};
const formatTime12Long = (h, m) => {
const period = h < 12 ? 'AM' : 'PM';
let h12 = h % 12 || 12;
return `${h12}:${String(m).padStart(2, '0')} ${period}`;
};
const formatTime24 = (h, m) =>
m > 0
? `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`
: `${String(h).padStart(2, '0')}:00`;
export const buildTimePlaceholders = (
timeGroups,
dateGroups,
sourceTimezone,
outputTimezone,
programDuration
) => {
if (!timeGroups || !timeGroups.hour) return {};
try {
let hour24 = parseInt(timeGroups.hour);
const minute = timeGroups.minute ? parseInt(timeGroups.minute) : 0;
const ampm = timeGroups.ampm?.toLowerCase();
if (ampm === 'pm' && hour24 !== 12) hour24 += 12;
else if (ampm === 'am' && hour24 === 12) hour24 = 0;
let baseDate = setTz(getNow(), sourceTimezone);
if (dateGroups.month && dateGroups.day) {
const monthValue = dateGroups.month;
let extractedMonth;
if (/^\d+$/.test(monthValue)) {
extractedMonth = parseInt(monthValue);
} else {
const lower = monthValue.toLowerCase();
const idx =
MONTH_NAMES.indexOf(lower) !== -1
? MONTH_NAMES.indexOf(lower)
: MONTH_ABBR.indexOf(lower);
extractedMonth = idx !== -1 ? idx + 1 : getMonth(getNow()) + 1;
}
const extractedDay = parseInt(dateGroups.day);
const extractedYear = dateGroups.year
? parseInt(dateGroups.year)
: getYear(getNow());
if (
!isNaN(extractedMonth) &&
!isNaN(extractedDay) &&
!isNaN(extractedYear) &&
extractedMonth >= 1 &&
extractedMonth <= 12 &&
extractedDay >= 1 &&
extractedDay <= 31
) {
baseDate = setTz(
`${extractedYear}-${String(extractedMonth).padStart(2, '0')}-${String(extractedDay).padStart(2, '0')}`,
sourceTimezone
);
}
}
let sourceDate = setHour(baseDate, hour24);
sourceDate = setMinute(sourceDate, minute);
sourceDate = setSecond(sourceDate, 0);
const workDate =
outputTimezone && outputTimezone !== sourceTimezone
? setTz(sourceDate, outputTimezone)
: sourceDate;
const h24 = getHour(workDate);
const min = getMinute(workDate);
const endTotalMin = h24 * 60 + min + (programDuration || 180);
const endH24 = Math.floor(endTotalMin / 60) % 24;
const endMin = endTotalMin % 60;
return {
starttime: formatTime12(h24, min),
starttime_long: formatTime12Long(h24, min),
starttime24: formatTime24(h24, min),
starttime24_long: formatTime24(h24, min),
endtime: formatTime12(endH24, endMin),
endtime_long: formatTime12Long(endH24, endMin),
endtime24: formatTime24(endH24, endMin),
date: format(workDate, 'YYYY-MM-DD'),
month: getMonth(workDate) + 1,
day: getDay(workDate),
year: getYear(workDate),
};
} catch (e) {
console.error('Error building time placeholders:', e);
}
};
const PLAIN_TEMPLATES = [
{ stateKey: 'titleTemplate', resultKey: 'formattedTitle' },
{ stateKey: 'subtitleTemplate', resultKey: 'formattedSubtitle' },
{ stateKey: 'descriptionTemplate', resultKey: 'formattedDescription' },
{ stateKey: 'upcomingTitleTemplate', resultKey: 'formattedUpcomingTitle' },
{
stateKey: 'upcomingDescriptionTemplate',
resultKey: 'formattedUpcomingDescription',
},
{ stateKey: 'endedTitleTemplate', resultKey: 'formattedEndedTitle' },
{
stateKey: 'endedDescriptionTemplate',
resultKey: 'formattedEndedDescription',
},
];
const URL_TEMPLATES = [
{ stateKey: 'channelLogoUrl', resultKey: 'formattedChannelLogoUrl' },
{ stateKey: 'programPosterUrl', resultKey: 'formattedProgramPosterUrl' },
];
export const applyTemplates = (templateValues, groups, hasMatch) => {
const result = {};
if (!hasMatch) return result;
PLAIN_TEMPLATES.forEach(({ stateKey, resultKey }) => {
if (templateValues[stateKey]) {
result[resultKey] = templateValues[stateKey].replace(
/\{(\w+)\}/g,
(m, k) => groups[k] || m
);
}
});
URL_TEMPLATES.forEach(({ stateKey, resultKey }) => {
if (templateValues[stateKey]) {
result[resultKey] = templateValues[stateKey].replace(
/\{(\w+)\}/g,
(m, k) => (groups[k] ? encodeURIComponent(String(groups[k])) : m)
);
}
});
return result;
};