mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #4621 from thornbill/no-notifications
Remove notifications pages from dashboard
This commit is contained in:
commit
ee99d3e1d8
13 changed files with 53 additions and 299 deletions
|
@ -1,4 +1,11 @@
|
|||
import { Lan, VpnKey, Article, EditNotifications, Extension, Schedule, ExpandLess, ExpandMore } from '@mui/icons-material';
|
||||
import Article from '@mui/icons-material/Article';
|
||||
import EditNotifications from '@mui/icons-material/EditNotifications';
|
||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import Extension from '@mui/icons-material/Extension';
|
||||
import Lan from '@mui/icons-material/Lan';
|
||||
import Schedule from '@mui/icons-material/Schedule';
|
||||
import VpnKey from '@mui/icons-material/VpnKey';
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
|
@ -63,7 +70,7 @@ const AdvancedDrawerSection = () => {
|
|||
<ListItemIcon>
|
||||
<EditNotifications />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={globalize.translate('TabNotifications')} />
|
||||
<ListItemText primary={globalize.translate('Notifications')} />
|
||||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'notificationsettings.html', page: 'dashboard/notifications' },
|
||||
{ path: 'usernew.html', page: 'user/usernew' },
|
||||
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
||||
{ path: 'useredit.html', page: 'user/useredit' },
|
||||
|
|
|
@ -103,18 +103,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
|||
controller: 'dashboard/metadatanfo',
|
||||
view: 'dashboard/metadatanfo.html'
|
||||
}
|
||||
}, {
|
||||
path: 'notificationsetting.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/notifications/notification/index',
|
||||
view: 'dashboard/notifications/notification/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'notificationsettings.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/notifications/notifications/index',
|
||||
view: 'dashboard/notifications/notifications/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'playbackconfiguration.html',
|
||||
pageProps: {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { AsyncRoute } from '../../../../components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'notificationsettings.html', page: 'dashboard/notifications' },
|
||||
{ path: 'usernew.html', page: 'user/usernew' },
|
||||
{ path: 'userprofiles.html', page: 'user/userprofiles' },
|
||||
{ path: 'useredit.html', page: 'user/useredit' },
|
||||
|
|
36
src/apps/stable/routes/dashboard/notifications.tsx
Normal file
36
src/apps/stable/routes/dashboard/notifications.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React from 'react';
|
||||
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
const PluginLink = () => (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<a
|
||||
is='emby-linkbutton'
|
||||
class='button-link'
|
||||
href='#/addplugin.html?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
|
||||
>
|
||||
${globalize.translate('GetThePlugin')}
|
||||
</a>`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const Notifications = () => (
|
||||
<Page
|
||||
id='notificationSettingPage'
|
||||
title={globalize.translate('Notifications')}
|
||||
className='mainAnimatedPage type-interior'
|
||||
>
|
||||
<div className='content-primary'>
|
||||
<h2>{globalize.translate('Notifications')}</h2>
|
||||
<p>
|
||||
{globalize.translate('NotificationsMovedMessage')}
|
||||
</p>
|
||||
<PluginLink />
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default Notifications;
|
|
@ -103,18 +103,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
|||
controller: 'dashboard/metadatanfo',
|
||||
view: 'dashboard/metadatanfo.html'
|
||||
}
|
||||
}, {
|
||||
path: 'notificationsetting.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/notifications/notification/index',
|
||||
view: 'dashboard/notifications/notification/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'notificationsettings.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/notifications/notifications/index',
|
||||
view: 'dashboard/notifications/notifications/index.html'
|
||||
}
|
||||
}, {
|
||||
path: 'playbackconfiguration.html',
|
||||
pageProps: {
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
<div id="notificationSettingPage" data-role="page" class="page type-interior notificationConfigurationPage withTabs">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<form class="notificationSettingForm">
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="notificationType sectionTitle"></h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/notifications">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnabled" />
|
||||
<span>${LabelNotificationEnabled}</span>
|
||||
</label>
|
||||
<br />
|
||||
|
||||
<div class="monitorUsers" style="display: none;">
|
||||
<div class="paperListLabel">${LabelMonitorUsers}</div>
|
||||
<div class="monitorUsersList">
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectUsers" label="${LabelSendNotificationToUsers}">
|
||||
<option value="All">${OptionAllUsers}</option>
|
||||
<option value="Admins">${OptionAdminUsers}</option>
|
||||
<option value="Custom">${OptionCustomUsers}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="selectCustomUsers" style="display: none;">
|
||||
<br />
|
||||
<label>${LabelSelectUsers}</label>
|
||||
<div class="sendToUsersList">
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>${LabelUseNotificationServices}</label>
|
||||
<div class="servicesList">
|
||||
</div>
|
||||
<div class="fieldDescription">${AdditionalNotificationServices}</div>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<br />
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
|
||||
<button is="emby-button" type="button" class="raised button-cancel block btnCancel" onclick="history.back();">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,121 +0,0 @@
|
|||
import escapeHtml from 'escape-html';
|
||||
import 'jquery';
|
||||
import '../../../../elements/emby-checkbox/emby-checkbox';
|
||||
import Dashboard from '../../../../utils/dashboard';
|
||||
import { getParameterByName } from '../../../../utils/url.ts';
|
||||
|
||||
function fillItems(elem, items, cssClass, idPrefix, currentList, isEnabledList) {
|
||||
let html = '<div class="checkboxList paperList" style="padding: .5em 1em;">';
|
||||
html += items.map(function (u) {
|
||||
const isChecked = isEnabledList ? currentList.indexOf(u.Id) != -1 : currentList.indexOf(u.Id) == -1;
|
||||
const checkedHtml = isChecked ? ' checked="checked"' : '';
|
||||
return '<label><input is="emby-checkbox" class="' + cssClass + '" type="checkbox" data-itemid="' + u.Id + '"' + checkedHtml + '/><span>' + escapeHtml(u.Name) + '</span></label>';
|
||||
}).join('');
|
||||
html += '</div>';
|
||||
elem.html(html).trigger('create');
|
||||
}
|
||||
|
||||
function reload(page) {
|
||||
const type = getParameterByName('type');
|
||||
const promise1 = ApiClient.getUsers();
|
||||
const promise2 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
||||
const promise3 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
||||
const promise4 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Services'));
|
||||
Promise.all([promise1, promise2, promise3, promise4]).then(function (responses) {
|
||||
const users = responses[0];
|
||||
const notificationOptions = responses[1];
|
||||
const types = responses[2];
|
||||
const services = responses[3];
|
||||
let notificationConfig = notificationOptions.Options.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0];
|
||||
const typeInfo = types.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0] || {};
|
||||
|
||||
if (typeInfo.IsBasedOnUserEvent) {
|
||||
$('.monitorUsers', page).show();
|
||||
} else {
|
||||
$('.monitorUsers', page).hide();
|
||||
}
|
||||
|
||||
$('.notificationType', page).html(escapeHtml(typeInfo.Name || '') || 'Unknown Notification');
|
||||
|
||||
if (!notificationConfig) {
|
||||
notificationConfig = {
|
||||
DisabledMonitorUsers: [],
|
||||
SendToUsers: [],
|
||||
DisabledServices: [],
|
||||
SendToUserMode: 'Admins'
|
||||
};
|
||||
}
|
||||
|
||||
fillItems($('.monitorUsersList', page), users, 'chkMonitor', 'chkMonitor', notificationConfig.DisabledMonitorUsers);
|
||||
fillItems($('.sendToUsersList', page), users, 'chkSendTo', 'chkSendTo', notificationConfig.SendToUsers, true);
|
||||
fillItems($('.servicesList', page), services, 'chkService', 'chkService', notificationConfig.DisabledServices);
|
||||
$('#chkEnabled', page).prop('checked', notificationConfig.Enabled || false);
|
||||
$('#selectUsers', page).val(notificationConfig.SendToUserMode).trigger('change');
|
||||
});
|
||||
}
|
||||
|
||||
function save(page) {
|
||||
const type = getParameterByName('type');
|
||||
const promise1 = ApiClient.getNamedConfiguration(notificationsConfigurationKey);
|
||||
// TODO: Check if this promise is really needed, as it's unused.
|
||||
const promise2 = ApiClient.getJSON(ApiClient.getUrl('Notifications/Types'));
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
const notificationOptions = responses[0];
|
||||
let notificationConfig = notificationOptions.Options.filter(function (n) {
|
||||
return n.Type == type;
|
||||
})[0];
|
||||
|
||||
if (!notificationConfig) {
|
||||
notificationConfig = {
|
||||
Type: type
|
||||
};
|
||||
notificationOptions.Options.push(notificationConfig);
|
||||
}
|
||||
|
||||
notificationConfig.Enabled = $('#chkEnabled', page).is(':checked');
|
||||
notificationConfig.SendToUserMode = $('#selectUsers', page).val();
|
||||
notificationConfig.DisabledMonitorUsers = $('.chkMonitor', page).get().filter(function (c) {
|
||||
return !c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
notificationConfig.SendToUsers = $('.chkSendTo', page).get().filter(function (c) {
|
||||
return c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
notificationConfig.DisabledServices = $('.chkService', page).get().filter(function (c) {
|
||||
return !c.checked;
|
||||
}).map(function (c) {
|
||||
return c.getAttribute('data-itemid');
|
||||
});
|
||||
ApiClient.updateNamedConfiguration(notificationsConfigurationKey, notificationOptions).then(function () {
|
||||
Dashboard.processServerConfigurationUpdateResult();
|
||||
Dashboard.navigate('notificationsettings.html');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
save($(this).parents('.page'));
|
||||
return false;
|
||||
}
|
||||
|
||||
const notificationsConfigurationKey = 'notifications';
|
||||
$(document).on('pageinit', '#notificationSettingPage', function () {
|
||||
const page = this;
|
||||
$('#selectUsers', page).on('change', function () {
|
||||
if (this.value == 'Custom') {
|
||||
$('.selectCustomUsers', page).show();
|
||||
} else {
|
||||
$('.selectCustomUsers', page).hide();
|
||||
}
|
||||
});
|
||||
$('.notificationSettingForm').off('submit', onSubmit).on('submit', onSubmit);
|
||||
}).on('pageshow', '#notificationSettingPage', function () {
|
||||
reload(this);
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
<div id="notificationSettingsPage" data-role="page" class="page type-interior notificationConfigurationPage">
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
<div class="readOnlyContent">
|
||||
<div class="notificationList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,61 +0,0 @@
|
|||
import loading from '../../../../components/loading/loading';
|
||||
import globalize from '../../../../scripts/globalize';
|
||||
import '../../../../components/listview/listview.scss';
|
||||
import '../../../../elements/emby-button/emby-button';
|
||||
|
||||
function reload(page) {
|
||||
loading.show();
|
||||
ApiClient.getJSON(ApiClient.getUrl('Notifications/Types')).then(function (list) {
|
||||
let html = '';
|
||||
let lastCategory = '';
|
||||
let showHelp = true;
|
||||
html += list.map(function (notification) {
|
||||
let itemHtml = '';
|
||||
if (notification.Category !== lastCategory) {
|
||||
lastCategory = notification.Category;
|
||||
if (lastCategory) {
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '</div>';
|
||||
}
|
||||
itemHtml += '<div class="verticalSection verticalSection-extrabottompadding">';
|
||||
itemHtml += '<div class="sectionTitleContainer" style="margin-bottom:1em;">';
|
||||
itemHtml += '<h2 class="sectionTitle">';
|
||||
itemHtml += notification.Category;
|
||||
itemHtml += '</h2>';
|
||||
if (showHelp) {
|
||||
showHelp = false;
|
||||
itemHtml += '<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/server/notifications">';
|
||||
itemHtml += globalize.translate('Help');
|
||||
itemHtml += '</a>';
|
||||
}
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '<div class="paperList">';
|
||||
}
|
||||
itemHtml += '<a class="listItem listItem-border" is="emby-linkbutton" data-ripple="false" href="notificationsetting.html?type=' + notification.Type + '">';
|
||||
if (notification.Enabled) {
|
||||
itemHtml += '<span class="listItemIcon material-icons notifications_active" aria-hidden="true"></span>';
|
||||
} else {
|
||||
itemHtml += '<span class="listItemIcon material-icons notifications_off" aria-hidden="true" style="background-color:#999;"></span>';
|
||||
}
|
||||
itemHtml += '<div class="listItemBody">';
|
||||
itemHtml += '<div class="listItemBodyText">' + notification.Name + '</div>';
|
||||
itemHtml += '</div>';
|
||||
itemHtml += '<button type="button" is="paper-icon-button-light"><span class="material-icons mode_edit" aria-hidden="true"></span></button>';
|
||||
itemHtml += '</a>';
|
||||
return itemHtml;
|
||||
}).join('');
|
||||
|
||||
if (list.length) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
page.querySelector('.notificationList').innerHTML = html;
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
|
||||
export default function (view) {
|
||||
view.addEventListener('viewshow', function () {
|
||||
reload(view);
|
||||
});
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<div id="btnInstallDiv" class="hide">
|
||||
<button is="emby-button" type="submit" id="btnInstall" class="raised button-submit block">
|
||||
<span>${Install}</span>
|
||||
<span>${HeaderInstall}</span>
|
||||
</button>
|
||||
<div class="fieldDescription">${ServerRestartNeededAfterPluginInstall}</div>
|
||||
</div>
|
||||
|
|
|
@ -516,10 +516,9 @@ function createToolsMenuList(pluginItems) {
|
|||
icon: 'bug_report'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('TabNotifications'),
|
||||
name: globalize.translate('Notifications'),
|
||||
icon: 'notifications',
|
||||
href: '#/notificationsettings.html',
|
||||
pageIds: ['notificationSettingsPage', 'notificationSettingPage']
|
||||
href: '#/notificationsettings.html'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('TabPlugins'),
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
"Actor": "Actor",
|
||||
"Add": "Add",
|
||||
"AddedOnValue": "Added {0}",
|
||||
"AdditionalNotificationServices": "Browse the plugin catalog to install additional notification services.",
|
||||
"AddToCollection": "Add to collection",
|
||||
"AddToFavorites": "Add to favorites",
|
||||
"AddToPlaylist": "Add to playlist",
|
||||
|
@ -293,6 +292,7 @@
|
|||
"General": "General",
|
||||
"Genre": "Genre",
|
||||
"Genres": "Genres",
|
||||
"GetThePlugin": "Get the Plugin",
|
||||
"GoogleCastUnsupported": "Google Cast Unsupported",
|
||||
"GroupBySeries": "Group by series",
|
||||
"GroupVersions": "Group versions",
|
||||
|
@ -773,7 +773,6 @@
|
|||
"LabelModelName": "Model name",
|
||||
"LabelModelNumber": "Model number",
|
||||
"LabelModelUrl": "Model URL",
|
||||
"LabelMonitorUsers": "Monitor activity from",
|
||||
"LabelMovieCategories": "Movie categories",
|
||||
"LabelMoviePrefix": "Movie prefix",
|
||||
"LabelMoviePrefixHelp": "If a prefix is applied to movie titles, enter it here so the server can handle it properly.",
|
||||
|
@ -785,7 +784,6 @@
|
|||
"LabelNewPassword": "New password",
|
||||
"LabelNewPasswordConfirm": "New password confirm",
|
||||
"LabelNewsCategories": "News categories",
|
||||
"LabelNotificationEnabled": "Enable this notification",
|
||||
"LabelNumber": "Number",
|
||||
"LabelNumberOfGuideDays": "Number of days of guide data to download",
|
||||
"LabelNumberOfGuideDaysHelp": "Downloading more days worth of guide data provides the ability to schedule out further in advance and view more listings, but it will also take longer to download. Auto will pick based on the number of channels.",
|
||||
|
@ -858,9 +856,7 @@
|
|||
"LabelSeasonNumber": "Season number",
|
||||
"LabelSelectFolderGroups": "Automatically group content from the following folders into views such as 'Movies', 'Music' and 'TV'",
|
||||
"LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.",
|
||||
"LabelSelectUsers": "Select users",
|
||||
"LabelSelectVersionToInstall": "Select version to install",
|
||||
"LabelSendNotificationToUsers": "Send the notification to",
|
||||
"LabelSerialNumber": "Serial number",
|
||||
"LabelSeriesRecordingPath": "Series recording path",
|
||||
"LabelServerHost": "Host",
|
||||
|
@ -971,7 +967,6 @@
|
|||
"LabelUDPPortRange": "UDP Communication Range",
|
||||
"LabelUDPPortRangeHelp": "Restrict Jellyfin to use this port range when making UDP connections. (Default is 1024 - 645535).<br/> Note: Certain function require fixed ports that may be outside of this range.",
|
||||
"LabelUnstable": "Unstable",
|
||||
"LabelUseNotificationServices": "Use the following services",
|
||||
"LabelUser": "User",
|
||||
"LabelUserAgent": "User agent",
|
||||
"LabelUserLibrary": "User library",
|
||||
|
@ -1175,6 +1170,8 @@
|
|||
"Normal": "Normal",
|
||||
"NoSubtitleSearchResultsFound": "No results found.",
|
||||
"NoSubtitlesHelp": "Subtitles will not be loaded by default. They can still be turned on manually during playback.",
|
||||
"Notifications": "Notifications",
|
||||
"NotificationsMovedMessage": "The notifications functionality has moved to the Webhook plugin.",
|
||||
"NumLocationsValue": "{0} folders",
|
||||
"Off": "Off",
|
||||
"OnApplicationStartup": "On application startup",
|
||||
|
@ -1184,7 +1181,6 @@
|
|||
"OnlyImageFormats": "Only Image Formats (VobSub, PGS, SUB)",
|
||||
"OnWakeFromSleep": "On wake from sleep",
|
||||
"Option3D": "3D",
|
||||
"OptionAdminUsers": "Administrators",
|
||||
"OptionAllowAudioPlaybackTranscoding": "Allow audio playback that requires transcoding",
|
||||
"OptionAllowBrowsingLiveTv": "Allow Live TV access",
|
||||
"OptionAllowContentDownload": "Allow media downloads",
|
||||
|
@ -1201,14 +1197,12 @@
|
|||
"OptionAllowUserToManageServer": "Allow this user to manage the server",
|
||||
"OptionAllowVideoPlaybackRemuxing": "Allow video playback that requires conversion without re-encoding",
|
||||
"OptionAllowVideoPlaybackTranscoding": "Allow video playback that requires transcoding",
|
||||
"OptionAllUsers": "All users",
|
||||
"OptionAutomaticallyGroupSeries": "Automatically merge series that are spread across multiple folders",
|
||||
"OptionAutomaticallyGroupSeriesHelp": "Series that are spread across multiple folders within this library will be automatically merged into a single series.",
|
||||
"OptionBluray": "BD",
|
||||
"OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)",
|
||||
"OptionCommunityRating": "Community Rating",
|
||||
"OptionCriticRating": "Critics Rating",
|
||||
"OptionCustomUsers": "Custom",
|
||||
"OptionDaily": "Daily",
|
||||
"OptionDateAdded": "Date Added",
|
||||
"OptionDateEpisodeAdded": "Date Episode Added",
|
||||
|
@ -1507,7 +1501,6 @@
|
|||
"TabNetworking": "Networking",
|
||||
"TabNetworks": "TV Networks",
|
||||
"TabNfoSettings": "NFO Settings",
|
||||
"TabNotifications": "Notifications",
|
||||
"TabOther": "Other",
|
||||
"TabParentalControl": "Parental Control",
|
||||
"TabPlugins": "Plugins",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue