parent
76650af9fd
commit
d46f4b2154
@ -0,0 +1,3 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
export default function getIndexOfFirstCharacter(items, character) {
|
||||
return items.findIndex((item) => {
|
||||
const firstCharacter = item.sortTitle.charAt(0);
|
||||
|
||||
if (character === '#') {
|
||||
return !isNaN(firstCharacter);
|
||||
}
|
||||
|
||||
return firstCharacter === character;
|
||||
});
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import Series from 'Series/Series';
|
||||
|
||||
const STARTS_WITH_NUMBER_REGEX = /^\d/;
|
||||
|
||||
export default function getIndexOfFirstCharacter(
|
||||
items: Series[],
|
||||
character: string
|
||||
) {
|
||||
return items.findIndex((item) => {
|
||||
const firstCharacter = item.sortTitle.charAt(0);
|
||||
|
||||
if (character === '#') {
|
||||
return STARTS_WITH_NUMBER_REGEX.test(firstCharacter);
|
||||
}
|
||||
|
||||
return firstCharacter === character;
|
||||
});
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
import _ from 'lodash';
|
||||
import Command, { CommandBody } from 'Commands/Command';
|
||||
import isSameCommand from './isSameCommand';
|
||||
|
||||
function findCommand(commands, options) {
|
||||
function findCommand(commands: Command[], options: Partial<CommandBody>) {
|
||||
return _.findLast(commands, (command) => {
|
||||
return isSameCommand(command.body, options);
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
function isCommandComplete(command) {
|
||||
if (!command) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return command.status === 'complete';
|
||||
}
|
||||
|
||||
export default isCommandComplete;
|
@ -0,0 +1,11 @@
|
||||
import Command from 'Commands/Command';
|
||||
|
||||
function isCommandComplete(command: Command) {
|
||||
if (!command) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return command.status === 'completed';
|
||||
}
|
||||
|
||||
export default isCommandComplete;
|
@ -1,4 +1,6 @@
|
||||
function isCommandExecuting(command) {
|
||||
import Command from 'Commands/Command';
|
||||
|
||||
function isCommandExecuting(command?: Command) {
|
||||
if (!command) {
|
||||
return false;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
function isCommandFailed(command) {
|
||||
if (!command) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return command.status === 'failed' ||
|
||||
command.status === 'aborted' ||
|
||||
command.status === 'cancelled' ||
|
||||
command.status === 'orphaned';
|
||||
}
|
||||
|
||||
export default isCommandFailed;
|
@ -0,0 +1,16 @@
|
||||
import Command from 'Commands/Command';
|
||||
|
||||
function isCommandFailed(command: Command) {
|
||||
if (!command) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
command.status === 'failed' ||
|
||||
command.status === 'aborted' ||
|
||||
command.status === 'cancelled' ||
|
||||
command.status === 'orphaned'
|
||||
);
|
||||
}
|
||||
|
||||
export default isCommandFailed;
|
@ -1,24 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
function isSameCommand(commandA, commandB) {
|
||||
if (commandA.name.toLocaleLowerCase() !== commandB.name.toLocaleLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const key in commandB) {
|
||||
if (key !== 'name') {
|
||||
const value = commandB[key];
|
||||
if (Array.isArray(value)) {
|
||||
if (_.difference(value, commandA[key]).length > 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (value !== commandA[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default isSameCommand;
|
@ -0,0 +1,50 @@
|
||||
import { CommandBody } from 'Commands/Command';
|
||||
|
||||
function isSameCommand(
|
||||
commandA: Partial<CommandBody>,
|
||||
commandB: Partial<CommandBody>
|
||||
) {
|
||||
if (
|
||||
commandA.name?.toLocaleLowerCase() !== commandB.name?.toLocaleLowerCase()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const key in commandB) {
|
||||
if (key !== 'name') {
|
||||
const value = commandB[key];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const sortedB = [...value].sort((a, b) => a - b);
|
||||
const commandAProp = commandA[key];
|
||||
const sortedA = Array.isArray(commandAProp)
|
||||
? [...commandAProp].sort((a, b) => a - b)
|
||||
: [];
|
||||
|
||||
if (sortedA === sortedB) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sortedA == null || sortedB == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sortedA.length !== sortedB.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < sortedB.length; ++i) {
|
||||
if (sortedB[i] !== sortedA[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (value !== commandA[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default isSameCommand;
|
@ -1,12 +1,17 @@
|
||||
import _ from 'lodash';
|
||||
import Episode from 'Episode/Episode';
|
||||
import { update } from 'Store/Actions/baseActions';
|
||||
|
||||
function updateEpisodes(section, episodes, episodeIds, options) {
|
||||
const data = _.reduce(episodes, (result, item) => {
|
||||
function updateEpisodes(
|
||||
section: string,
|
||||
episodes: Episode[],
|
||||
episodeIds: number[],
|
||||
options: Partial<Episode>
|
||||
) {
|
||||
const data = episodes.reduce<Episode[]>((result, item) => {
|
||||
if (episodeIds.indexOf(item.id) > -1) {
|
||||
result.push({
|
||||
...item,
|
||||
...options
|
||||
...options,
|
||||
});
|
||||
} else {
|
||||
result.push(item);
|
@ -1,19 +0,0 @@
|
||||
export default function findSelectedFilters(selectedFilterKey, filters = [], customFilters = []) {
|
||||
if (!selectedFilterKey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let selectedFilter = filters.find((f) => f.key === selectedFilterKey);
|
||||
|
||||
if (!selectedFilter) {
|
||||
selectedFilter = customFilters.find((f) => f.id === selectedFilterKey);
|
||||
}
|
||||
|
||||
if (!selectedFilter) {
|
||||
// TODO: throw in dev
|
||||
console.error('Matching filter not found');
|
||||
return [];
|
||||
}
|
||||
|
||||
return selectedFilter.filters;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { CustomFilter, Filter } from 'App/State/AppState';
|
||||
|
||||
export default function findSelectedFilters(
|
||||
selectedFilterKey: string | number,
|
||||
filters: Filter[] = [],
|
||||
customFilters: CustomFilter[] = []
|
||||
) {
|
||||
if (!selectedFilterKey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let selectedFilter: Filter | CustomFilter | undefined = filters.find(
|
||||
(f) => f.key === selectedFilterKey
|
||||
);
|
||||
|
||||
if (!selectedFilter) {
|
||||
selectedFilter = customFilters.find((f) => f.id === selectedFilterKey);
|
||||
}
|
||||
|
||||
if (!selectedFilter) {
|
||||
// TODO: throw in dev
|
||||
console.error('Matching filter not found');
|
||||
return [];
|
||||
}
|
||||
|
||||
return selectedFilter.filters;
|
||||
}
|
@ -1,4 +1,11 @@
|
||||
export default function getFilterValue(filters, filterKey, filterValueKey, defaultValue) {
|
||||
import { Filter } from 'App/State/AppState';
|
||||
|
||||
export default function getFilterValue(
|
||||
filters: Filter[],
|
||||
filterKey: string | number,
|
||||
filterValueKey: string,
|
||||
defaultValue: string | number | boolean
|
||||
) {
|
||||
const filter = filters.find((f) => f.key === filterKey);
|
||||
|
||||
if (!filter) {
|
@ -1,5 +1,4 @@
|
||||
|
||||
function convertToBytes(input, power, binaryPrefix) {
|
||||
function convertToBytes(input: number, power: number, binaryPrefix: boolean) {
|
||||
const size = Number(input);
|
||||
|
||||
if (isNaN(size)) {
|
@ -1,19 +0,0 @@
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function formatAge(age, ageHours, ageMinutes) {
|
||||
age = Math.round(age);
|
||||
ageHours = parseFloat(ageHours);
|
||||
ageMinutes = ageMinutes && parseFloat(ageMinutes);
|
||||
|
||||
if (age < 2 && ageHours) {
|
||||
if (ageHours < 2 && !!ageMinutes) {
|
||||
return `${ageMinutes.toFixed(0)} ${ageHours === 1 ? translate('FormatAgeMinute') : translate('FormatAgeMinutes')}`;
|
||||
}
|
||||
|
||||
return `${ageHours.toFixed(1)} ${ageHours === 1 ? translate('FormatAgeHour') : translate('FormatAgeHours')}`;
|
||||
}
|
||||
|
||||
return `${age} ${age === 1 ? translate('FormatAgeDay') : translate('FormatAgeDays')}`;
|
||||
}
|
||||
|
||||
export default formatAge;
|
@ -0,0 +1,33 @@
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
function formatAge(
|
||||
age: string | number,
|
||||
ageHours: string | number,
|
||||
ageMinutes: string | number
|
||||
) {
|
||||
const ageRounded = Math.round(Number(age));
|
||||
const ageHoursFloat = parseFloat(String(ageHours));
|
||||
const ageMinutesFloat = ageMinutes && parseFloat(String(ageMinutes));
|
||||
|
||||
if (ageRounded < 2 && ageHoursFloat) {
|
||||
if (ageHoursFloat < 2 && !!ageMinutesFloat) {
|
||||
return `${ageMinutesFloat.toFixed(0)} ${
|
||||
ageHoursFloat === 1
|
||||
? translate('FormatAgeMinute')
|
||||
: translate('FormatAgeMinutes')
|
||||
}`;
|
||||
}
|
||||
|
||||
return `${ageHoursFloat.toFixed(1)} ${
|
||||
ageHoursFloat === 1
|
||||
? translate('FormatAgeHour')
|
||||
: translate('FormatAgeHours')
|
||||
}`;
|
||||
}
|
||||
|
||||
return `${ageRounded} ${
|
||||
ageRounded === 1 ? translate('FormatAgeDay') : translate('FormatAgeDays')
|
||||
}`;
|
||||
}
|
||||
|
||||
export default formatAge;
|
@ -1,10 +0,0 @@
|
||||
function padNumber(input, width, paddingCharacter = 0) {
|
||||
if (input == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
input = `${input}`;
|
||||
return input.length >= width ? input : new Array(width - input.length + 1).join(paddingCharacter) + input;
|
||||
}
|
||||
|
||||
export default padNumber;
|
@ -0,0 +1,13 @@
|
||||
function padNumber(input: number, width: number, paddingCharacter = '0') {
|
||||
if (input == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const result = `${input}`;
|
||||
|
||||
return result.length >= width
|
||||
? result
|
||||
: new Array(width - result.length + 1).join(paddingCharacter) + result;
|
||||
}
|
||||
|
||||
export default padNumber;
|
@ -1,4 +1,4 @@
|
||||
export default function roundNumber(input, decimalPlaces = 1) {
|
||||
export default function roundNumber(input: number, decimalPlaces = 1) {
|
||||
const multiplier = Math.pow(10, decimalPlaces);
|
||||
|
||||
return Math.round(input * multiplier) / multiplier;
|
@ -1,4 +1,12 @@
|
||||
function getErrorMessage(xhr, fallbackErrorMessage) {
|
||||
interface AjaxResponse {
|
||||
responseJSON:
|
||||
| {
|
||||
message: string | undefined;
|
||||
}
|
||||
| undefined;
|
||||
}
|
||||
|
||||
function getErrorMessage(xhr: AjaxResponse, fallbackErrorMessage?: string) {
|
||||
if (!xhr || !xhr.responseJSON || !xhr.responseJSON.message) {
|
||||
return fallbackErrorMessage;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
function getRemovedItems(prevItems, currentItems, idProp = 'id') {
|
||||
if (prevItems === currentItems) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const currentItemIds = new Set();
|
||||
|
||||
currentItems.forEach((currentItem) => {
|
||||
currentItemIds.add(currentItem[idProp]);
|
||||
});
|
||||
|
||||
return prevItems.filter((prevItem) => !currentItemIds.has(prevItem[idProp]));
|
||||
}
|
||||
|
||||
export default getRemovedItems;
|
@ -1,4 +1,10 @@
|
||||
function hasDifferentItems(prevItems, currentItems, idProp = 'id') {
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
function hasDifferentItems<T extends ModelBase>(
|
||||
prevItems: T[],
|
||||
currentItems: T[],
|
||||
idProp: keyof T = 'id'
|
||||
) {
|
||||
if (prevItems === currentItems) {
|
||||
return false;
|
||||
}
|
@ -1,4 +1,10 @@
|
||||
function hasDifferentItemsOrOrder(prevItems, currentItems, idProp = 'id') {
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
function hasDifferentItemsOrOrder<T extends ModelBase>(
|
||||
prevItems: T[],
|
||||
currentItems: T[],
|
||||
idProp: keyof T = 'id'
|
||||
) {
|
||||
if (prevItems === currentItems) {
|
||||
return false;
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
export default function getQualities(qualities) {
|
||||
if (!qualities) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return qualities.reduce((acc, item) => {
|
||||
if (item.quality) {
|
||||
acc.push(item.quality);
|
||||
} else {
|
||||
const groupQualities = item.items.map((i) => i.quality);
|
||||
acc.push(...groupQualities);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import Quality from 'Quality/Quality';
|
||||
import { QualityProfileQualityItem } from 'typings/QualityProfile';
|
||||
|
||||
export default function getQualities(qualities?: QualityProfileQualityItem[]) {
|
||||
if (!qualities) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return qualities.reduce<Quality[]>((acc, item) => {
|
||||
if (item.quality) {
|
||||
acc.push(item.quality);
|
||||
} else {
|
||||
const groupQualities = item.items.reduce<Quality[]>((acc, i) => {
|
||||
if (i.quality) {
|
||||
acc.push(i.quality);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
acc.push(...groupQualities);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
module.exports = {
|
||||
resolutions: {
|
||||
desktopLarge: 1200,
|
||||
desktop: 992,
|
||||
tablet: 768,
|
||||
mobile: 480
|
||||
},
|
||||
|
||||
isDesktopLarge() {
|
||||
return $(window).width() < this.resolutions.desktopLarge;
|
||||
},
|
||||
|
||||
isDesktop() {
|
||||
return $(window).width() < this.resolutions.desktop;
|
||||
},
|
||||
|
||||
isTablet() {
|
||||
return $(window).width() < this.resolutions.tablet;
|
||||
},
|
||||
|
||||
isMobile() {
|
||||
return $(window).width() < this.resolutions.mobile;
|
||||
}
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
resolutions: {
|
||||
desktopLarge: 1200,
|
||||
desktop: 992,
|
||||
tablet: 768,
|
||||
mobile: 480,
|
||||
},
|
||||
|
||||
isDesktopLarge() {
|
||||
return window.innerWidth < this.resolutions.desktopLarge;
|
||||
},
|
||||
|
||||
isDesktop() {
|
||||
return window.innerWidth < this.resolutions.desktop;
|
||||
},
|
||||
|
||||
isTablet() {
|
||||
return window.innerWidth < this.resolutions.tablet;
|
||||
},
|
||||
|
||||
isMobile() {
|
||||
return window.innerWidth < this.resolutions.mobile;
|
||||
},
|
||||
};
|
@ -1,53 +0,0 @@
|
||||
|
||||
function filterAlternateTitles(alternateTitles, seriesTitle, useSceneNumbering, seasonNumber, sceneSeasonNumber) {
|
||||
const globalTitles = [];
|
||||
const seasonTitles = [];
|
||||
|
||||
if (alternateTitles) {
|
||||
alternateTitles.forEach((alternateTitle) => {
|
||||
if (alternateTitle.sceneOrigin === 'unknown' || alternateTitle.sceneOrigin === 'unknown:tvdb') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (alternateTitle.sceneOrigin === 'mixed') {
|
||||
// For now filter out 'mixed' from the UI, the user will get an rejection during manual search.
|
||||
return;
|
||||
}
|
||||
|
||||
const hasAltSeasonNumber = (alternateTitle.seasonNumber !== -1 && alternateTitle.seasonNumber !== undefined);
|
||||
const hasAltSceneSeasonNumber = (alternateTitle.sceneSeasonNumber !== -1 && alternateTitle.sceneSeasonNumber !== undefined);
|
||||
|
||||
// Global alias that should be displayed global
|
||||
if (!hasAltSeasonNumber && !hasAltSceneSeasonNumber &&
|
||||
(alternateTitle.title !== seriesTitle) &&
|
||||
(!alternateTitle.sceneOrigin || !useSceneNumbering)) {
|
||||
globalTitles.push(alternateTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Global alias that should be displayed per episode
|
||||
if (!hasAltSeasonNumber && !hasAltSceneSeasonNumber && alternateTitle.sceneOrigin && useSceneNumbering) {
|
||||
seasonTitles.push(alternateTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the alternative mapping (release to scene)
|
||||
const mappedAltSeasonNumber = hasAltSeasonNumber ? alternateTitle.seasonNumber : alternateTitle.sceneSeasonNumber;
|
||||
// Select scene or tvdb on the episode
|
||||
const mappedSeasonNumber = alternateTitle.sceneOrigin === 'tvdb' ? seasonNumber : sceneSeasonNumber;
|
||||
|
||||
if (mappedSeasonNumber !== undefined && mappedSeasonNumber === mappedAltSeasonNumber) {
|
||||
seasonTitles.push(alternateTitle);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (seasonNumber === undefined) {
|
||||
return globalTitles;
|
||||
}
|
||||
|
||||
return seasonTitles;
|
||||
}
|
||||
|
||||
export default filterAlternateTitles;
|
@ -0,0 +1,83 @@
|
||||
import { AlternateTitle } from 'Series/Series';
|
||||
|
||||
function filterAlternateTitles(
|
||||
alternateTitles: AlternateTitle[],
|
||||
seriesTitle: string | null,
|
||||
useSceneNumbering: boolean,
|
||||
seasonNumber?: number,
|
||||
sceneSeasonNumber?: number
|
||||
) {
|
||||
const globalTitles: AlternateTitle[] = [];
|
||||
const seasonTitles: AlternateTitle[] = [];
|
||||
|
||||
if (alternateTitles) {
|
||||
alternateTitles.forEach((alternateTitle) => {
|
||||
if (
|
||||
alternateTitle.sceneOrigin === 'unknown' ||
|
||||
alternateTitle.sceneOrigin === 'unknown:tvdb'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (alternateTitle.sceneOrigin === 'mixed') {
|
||||
// For now filter out 'mixed' from the UI, the user will get an rejection during manual search.
|
||||
return;
|
||||
}
|
||||
|
||||
const hasAltSeasonNumber =
|
||||
alternateTitle.seasonNumber !== -1 &&
|
||||
alternateTitle.seasonNumber !== undefined;
|
||||
const hasAltSceneSeasonNumber =
|
||||
alternateTitle.sceneSeasonNumber !== -1 &&
|
||||
alternateTitle.sceneSeasonNumber !== undefined;
|
||||
|
||||
// Global alias that should be displayed global
|
||||
if (
|
||||
!hasAltSeasonNumber &&
|
||||
!hasAltSceneSeasonNumber &&
|
||||
alternateTitle.title !== seriesTitle &&
|
||||
(!alternateTitle.sceneOrigin || !useSceneNumbering)
|
||||
) {
|
||||
globalTitles.push(alternateTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Global alias that should be displayed per episode
|
||||
if (
|
||||
!hasAltSeasonNumber &&
|
||||
!hasAltSceneSeasonNumber &&
|
||||
alternateTitle.sceneOrigin &&
|
||||
useSceneNumbering
|
||||
) {
|
||||
seasonTitles.push(alternateTitle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the alternative mapping (release to scene)
|
||||
const mappedAltSeasonNumber = hasAltSeasonNumber
|
||||
? alternateTitle.seasonNumber
|
||||
: alternateTitle.sceneSeasonNumber;
|
||||
// Select scene or tvdb on the episode
|
||||
const mappedSeasonNumber =
|
||||
alternateTitle.sceneOrigin === 'tvdb'
|
||||
? seasonNumber
|
||||
: sceneSeasonNumber;
|
||||
|
||||
if (
|
||||
mappedSeasonNumber !== undefined &&
|
||||
mappedSeasonNumber === mappedAltSeasonNumber
|
||||
) {
|
||||
seasonTitles.push(alternateTitle);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (seasonNumber === undefined) {
|
||||
return globalTitles;
|
||||
}
|
||||
|
||||
return seasonTitles;
|
||||
}
|
||||
|
||||
export default filterAlternateTitles;
|
@ -1,5 +0,0 @@
|
||||
function getNextId(items) {
|
||||
return items.reduce((id, x) => Math.max(id, x.id), 1) + 1;
|
||||
}
|
||||
|
||||
export default getNextId;
|
@ -0,0 +1,7 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
function getNextId<T extends ModelBase>(items: T[]) {
|
||||
return items.reduce((id, x) => Math.max(id, x.id), 1) + 1;
|
||||
}
|
||||
|
||||
export default getNextId;
|
@ -1,5 +0,0 @@
|
||||
export default function combinePath(isWindows, basePath, paths = []) {
|
||||
const slash = isWindows ? '\\' : '/';
|
||||
|
||||
return `${basePath}${slash}${paths.join(slash)}`;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
export default function combinePath(
|
||||
isWindows: boolean,
|
||||
basePath: string,
|
||||
paths: string[] = []
|
||||
) {
|
||||
const slash = isWindows ? '\\' : '/';
|
||||
|
||||
return `${basePath}${slash}${paths.join(slash)}`;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
export default function generateUUIDv4() {
|
||||
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, (c) =>
|
||||
// eslint-disable-next-line no-bitwise
|
||||
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||
);
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
export default function isString(possibleString) {
|
||||
export default function isString(possibleString: unknown) {
|
||||
return typeof possibleString === 'string' || possibleString instanceof String;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
const regex = /\d+/g;
|
||||
|
||||
function naturalExpansion(input) {
|
||||
function naturalExpansion(input: string) {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
function split(input, separator = ',') {
|
||||
if (!input) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _.reduce(input.split(separator), (result, s) => {
|
||||
if (s) {
|
||||
result.push(s);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default split;
|
@ -0,0 +1,15 @@
|
||||
function split(input: string, separator = ',') {
|
||||
if (!input) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return input.split(separator).reduce<string[]>((acc, s) => {
|
||||
if (s) {
|
||||
acc.push(s);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default split;
|
@ -1,6 +1,6 @@
|
||||
const regex = /\b\w+/g;
|
||||
|
||||
function titleCase(input) {
|
||||
function titleCase(input: string | undefined) {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
export default function areAllSelected(selectedState) {
|
||||
let allSelected = true;
|
||||
let allUnselected = true;
|
||||
|
||||
Object.keys(selectedState).forEach((key) => {
|
||||
if (selectedState[key]) {
|
||||
allUnselected = false;
|
||||
} else {
|
||||
allSelected = false;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
allSelected,
|
||||
allUnselected
|
||||
};
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { SelectedState } from 'Helpers/Hooks/useSelectState';
|
||||
|
||||
export default function areAllSelected(selectedState: SelectedState) {
|
||||
let allSelected = true;
|
||||
let allUnselected = true;
|
||||
|
||||
Object.values(selectedState).forEach((value) => {
|
||||
if (value) {
|
||||
allUnselected = false;
|
||||
} else {
|
||||
allSelected = false;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
allSelected,
|
||||
allUnselected,
|
||||
};
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
function getToggledRange(items, id, lastToggled) {
|
||||
const lastToggledIndex = _.findIndex(items, { id: lastToggled });
|
||||
const changedIndex = _.findIndex(items, { id });
|
||||
let lower = 0;
|
||||
let upper = 0;
|
||||
|
||||
if (lastToggledIndex > changedIndex) {
|
||||
lower = changedIndex;
|
||||
upper = lastToggledIndex + 1;
|
||||
} else {
|
||||
lower = lastToggledIndex;
|
||||
upper = changedIndex;
|
||||
}
|
||||
|
||||
return {
|
||||
lower,
|
||||
upper
|
||||
};
|
||||
}
|
||||
|
||||
export default getToggledRange;
|
@ -0,0 +1,27 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
|
||||
function getToggledRange<T extends ModelBase>(
|
||||
items: T[],
|
||||
id: number,
|
||||
lastToggled: number
|
||||
) {
|
||||
const lastToggledIndex = items.findIndex((item) => item.id === lastToggled);
|
||||
const changedIndex = items.findIndex((item) => item.id === id);
|
||||
let lower = 0;
|
||||
let upper = 0;
|
||||
|
||||
if (lastToggledIndex > changedIndex) {
|
||||
lower = changedIndex;
|
||||
upper = lastToggledIndex + 1;
|
||||
} else {
|
||||
lower = lastToggledIndex;
|
||||
upper = changedIndex;
|
||||
}
|
||||
|
||||
return {
|
||||
lower,
|
||||
upper,
|
||||
};
|
||||
}
|
||||
|
||||
export default getToggledRange;
|
@ -1,16 +0,0 @@
|
||||
import areAllSelected from './areAllSelected';
|
||||
|
||||
export default function removeOldSelectedState(state, prevItems) {
|
||||
const selectedState = {
|
||||
...state.selectedState
|
||||
};
|
||||
|
||||
prevItems.forEach((item) => {
|
||||
delete selectedState[item.id];
|
||||
});
|
||||
|
||||
return {
|
||||
...areAllSelected(selectedState),
|
||||
selectedState
|
||||
};
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
import { SelectState } from 'Helpers/Hooks/useSelectState';
|
||||
import areAllSelected from './areAllSelected';
|
||||
|
||||
export default function removeOldSelectedState<T extends ModelBase>(
|
||||
state: SelectState,
|
||||
prevItems: T[]
|
||||
) {
|
||||
const selectedState = {
|
||||
...state.selectedState,
|
||||
};
|
||||
|
||||
prevItems.forEach((item) => {
|
||||
delete selectedState[item.id];
|
||||
});
|
||||
|
||||
return {
|
||||
...areAllSelected(selectedState),
|
||||
selectedState,
|
||||
};
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
function selectAll(selectedState, selected) {
|
||||
const newSelectedState = _.reduce(Object.keys(selectedState), (result, item) => {
|
||||
result[item] = selected;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
allSelected: selected,
|
||||
allUnselected: !selected,
|
||||
lastToggled: null,
|
||||
selectedState: newSelectedState
|
||||
};
|
||||
}
|
||||
|
||||
export default selectAll;
|
@ -0,0 +1,19 @@
|
||||
import { SelectedState } from 'Helpers/Hooks/useSelectState';
|
||||
|
||||
function selectAll(selectedState: SelectedState, selected: boolean) {
|
||||
const newSelectedState = Object.keys(selectedState).reduce<
|
||||
Record<number, boolean>
|
||||
>((acc, item) => {
|
||||
acc[Number(item)] = selected;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
allSelected: selected,
|
||||
allUnselected: !selected,
|
||||
lastToggled: null,
|
||||
selectedState: newSelectedState,
|
||||
};
|
||||
}
|
||||
|
||||
export default selectAll;
|
@ -1,3 +0,0 @@
|
||||
export default function getPathWithUrlBase(path) {
|
||||
return `${window.Sonarr.urlBase}${path}`;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export default function getPathWithUrlBase(path: string) {
|
||||
return `${window.Sonarr.urlBase}${path}`;
|
||||
}
|
@ -1,19 +1,24 @@
|
||||
let currentPopulator = null;
|
||||
let currentReasons = [];
|
||||
type Populator = () => void;
|
||||
|
||||
export function registerPagePopulator(populator, reasons = []) {
|
||||
let currentPopulator: Populator | null = null;
|
||||
let currentReasons: string[] = [];
|
||||
|
||||
export function registerPagePopulator(
|
||||
populator: Populator,
|
||||
reasons: string[] = []
|
||||
) {
|
||||
currentPopulator = populator;
|
||||
currentReasons = reasons;
|
||||
}
|
||||
|
||||
export function unregisterPagePopulator(populator) {
|
||||
export function unregisterPagePopulator(populator: Populator) {
|
||||
if (currentPopulator === populator) {
|
||||
currentPopulator = null;
|
||||
currentReasons = [];
|
||||
}
|
||||
}
|
||||
|
||||
export function repopulatePage(reason) {
|
||||
export function repopulatePage(reason: string) {
|
||||
if (!currentPopulator) {
|
||||
return;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
const sectionTypes = {
|
||||
COLLECTION: 'collection',
|
||||
MODEL: 'model'
|
||||
};
|
||||
|
||||
export default sectionTypes;
|
Loading…
Reference in new issue