Allow some cross-domain settings sharing, handle settings inheritance, don't save auto-detected player index

This commit is contained in:
Tamius Han 2025-04-28 01:23:12 +02:00
parent 150a0a8a90
commit e67b5a227f
7 changed files with 275 additions and 34 deletions

View File

@ -93,6 +93,7 @@
:eventBus="eventBus"
:siteSettings="siteSettings"
:site="site.host"
:frames="activeFrames"
>
</BaseExtensionSettings>
<ChangelogPanel
@ -307,6 +308,7 @@ export default {
label: this.site.frames[frame].host,
host: this.site.frames[frame].host,
...this.site.frames[frame],
...this.settings.active.sites[this.site.frames[frame].host]
})
};
}

View File

@ -13,6 +13,7 @@
<small>{{ site }}</small>
</div>
<div
v-if="frames"
class="tab"
:class="{'active': tab === 'embeddedSites'}"
@click="setTab(tab = 'embeddedSites')"
@ -37,13 +38,12 @@
></SiteExtensionSettings>
</template>
<template v-if="tab === 'extensionSettings' && globalSettings">
<SiteExtensionSettings
<template v-if="frames && tab === 'embeddedSites' && globalSettings">
<FrameSiteSettings
v-if="settings"
:frames="frames"
:settings="settings"
:siteSettings="globalSettings"
:isDefaultConfiguration="true"
></SiteExtensionSettings>
></FrameSiteSettings>
</template>
<template v-if="tab === 'otherSites'">
@ -153,6 +153,7 @@
<script>
import SiteExtensionSettings from './PanelComponents/ExtensionSettings/SiteExtensionSettings.vue';
import FrameSiteSettings from './PanelComponents/ExtensionSettings/FrameSiteSettings.vue';
import OtherSiteSettings from './PanelComponents/ExtensionSettings/OtherSiteSettings.vue';
import Popup from '@csui/src/components/Popup';
import ConfirmButton from '@csui/src/components/ConfirmButton';
@ -162,6 +163,23 @@ import JsonEditor from '@csui/src/components/JsonEditor';
export default {
components: {
SiteExtensionSettings,
OtherSiteSettings,
Popup,
ConfirmButton,
UploadJsonFileButton,
JsonEditor,
FrameSiteSettings,
},
mixins: [],
props: [
'settings',
'site',
'enableSettingsEditor',
'frames',
],
data() {
return {
tab: 'siteSettings',
@ -172,22 +190,6 @@ export default {
settingsSnapshots: []
}
},
mixins: [
],
props: [
'settings',
'site',
'enableSettingsEditor'
],
components: {
SiteExtensionSettings,
OtherSiteSettings,
Popup,
ConfirmButton,
UploadJsonFileButton,
JsonEditor
},
computed: {
globalSettings() {
return this.settings?.getSiteSettings('@global') ?? null;

View File

@ -0,0 +1,107 @@
<template>
<div class="">
<template v-if="!selectedSite">
<div style="margin: 1rem 0rem" class="w-full">
<div class="flex flex-row items-baseline">
<div style="margin-right: 1rem">Search for site:</div>
<div class="input flex-grow">
<input v-model="siteFilter" />
</div>
</div>
</div>
<div v-for="frame of frames" :key="frame.host" @click="selectedSite = frame.host" class="flex flex-col container pointer hoverable" style="margin-top: 4px; padding: 0.5rem 1rem;">
<SiteListItem
:frame="frame"
:settings="settings"
></SiteListItem>
</div>
</template>
<template v-if="selectedSite">
<div class="flex flex-row container" style="align-items: center; color: #dedede; margin-top: 1rem;">
<div @click="selectedSite = null" class="pointer button-hover" style=" font-size: 2em; padding: 0.5rem; margin-right: 1em;">
</div>
<div>
Editing {{ selectedSite === '@global' ? 'default settings' : selectedSite }}
</div>
</div>
<div>
<SiteExtensionSettings
v-if="selectedSiteSettings"
:settings="settings"
:siteSettings="selectedSiteSettings"
:isDefaultConfiguration="selectedSite === '@global'"
></SiteExtensionSettings>
</div>
</template>
</div>
</template>
<script>
import SiteExtensionSettings from './SiteExtensionSettings.vue';
import SiteListItem from './SiteListItem.vue';
export default {
components: {
SiteExtensionSettings,
SiteListItem,
},
props: [
'settings',
'frames',
],
data() {
return {
selectedSite: null,
siteFilter: '',
filteredSites: []
}
},
computed: {
sites() {
if (!this.settings?.active?.sites) {
return [];
} else {
const sites = [];
for (const siteKey in this.settings.active.sites) {
if (!siteKey.startsWith('@') && (!this.siteFilter.trim() || siteKey.includes(this.siteFilter))) {
sites.push({
key: siteKey,
...this.settings.active.sites[siteKey]
})
}
};
sites.sort((a, b) => {
const cmpa = a.key.replace('www.', '');
const cmpb = b.key.replace('www.', '');
return cmpa < cmpb ? -1 : 1;
});
return sites;
}
},
selectedSiteSettings() {
return this.settings?.getSiteSettings(this.selectedSite) ?? null;
}
},
methods: {
}
}
</script>
<style lang="scss" src="../../../../res/css/flex.scss" scoped></style>
<style lang="scss" src="@csui/src/res-common/panels.scss" scoped></style>
<style lang="scss" src="@csui/src/res-common/common.scss" scoped></style>
<style lang="scss" scoped>
.hoverable {
border: 1px solid #333;
&:hover {
border: 1px solid #fa6;
color: rgb(255, 231, 212);
background-color: rgba(#fa6, 0.125);
}
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div>
<div class="flex flex-row">
<div class="flex-grow pointer">
<b>{{ frame.host ?? frame.key }}</b>
<span :style="getSiteTypeColor(frame.type)">
(config: {{frame.type ?? 'unknown'}})
</span>
</div>
<div>Edit</div>
</div>
<div v-if="this.siteSettings?.usesSettingsFor">
<div v-if="this.siteSettings.usesSettingsFor === '@global'">Uses default settings</div>
<div v-else>Uses settings for: {{this.siteSettings.usesSettingsFor}}</div>
</div>
<div class="flex flex-row">
<small>
Enabled: <span :style="getSiteEnabledColor(frame.host, 'enable')"><small>{{ getSiteEnabledModes(frame.host, 'enable') }}</small></span>;&nbsp;
Aard <span :style="getSiteEnabledColor(frame.host, 'enableAard')"><small>{{ getSiteEnabledModes(frame.host, 'enableAard') }}</small></span>;&nbsp;
kbd: <span :style="getSiteEnabledColor(frame.host, 'enableKeyboard')"><small>{{ getSiteEnabledModes(frame.host, 'enableKeyboard') }}</small></span>
UI: <span :style="getSiteEnabledColor(frame.host, 'enableUI')"><small>{{ getSiteEnabledModes(frame.host, 'enableUI') }}</small></span>
</small>
</div>
</div>
</template>
<script>
import ExtensionMode from '../../../../../common/enums/ExtensionMode.enum';
export default {
data() {
return {
siteSettings: undefined
}
},
props: [
'settings',
'frame',
],
created() {
this.siteSettings = this.settings.getSiteSettings(this.frame.host ?? this.frame.key);
},
methods: {
getSiteTypeColor(siteType) {
switch (siteType) {
case 'official': return 'color: #fa6';
case 'community': return 'color: rgb(114, 114, 218)';
case 'officially-disabled': return 'color: #f00';
case 'testing': return 'color: #d81';
default: return 'color: rgb(138, 65, 126)'
};
},
getSiteEnabledColor(site, component) {
const status = this.getSiteEnabledModes(site, component);
return status === 'disabled' ? 'color: #f00' : 'color: #1f8';
},
getSiteEnabledModes(site, component) {
if (this.siteSettings?.normal === ExtensionMode.Enabled) {
return 'always';
}
if (this.siteSettings?.data[component]?.theater === ExtensionMode.Enabled) {
return 'T + FS';
}
if (this.siteSettings?.data[component]?.fullscreen === ExtensionMode.Enabled) {
return 'fullscreen';
}
return 'disabled';
}
}
}
</script>

View File

@ -163,7 +163,6 @@ export default class UWServer {
this.logger.log('error','debug', '[UwServer::injectCss] Error while removing css:', {error: e, css, sender});
}
}
async replaceCss(oldCss, newCss, sender) {
if (oldCss !== newCss) {
this.removeCss(oldCss, sender);
@ -215,7 +214,9 @@ export default class UWServer {
'siteSettings': undefined,
'videoSettings': undefined,
}
//TODO: change extension icon based on whether there's any videos on current page
}
registerVideo(sender) {
@ -276,14 +277,19 @@ export default class UWServer {
}
async getCurrentSite() {
this.logger.log('info', 'comms', '%c[UWServer::getCurrentSite] received get-current-site ...', 'background-color: #243; color: #4a8');
const site = await this.getVideoTab();
// Don't propagate 'INVALID SITE' to the popup.
if (site.host === 'INVALID SITE') {
this.logger.log('info', 'comms', '%c[UWServer::getCurrentSite] Host is not valid — no info for current tab.', 'background-color: #243; color: #4a8');
return;
}
const tabHostname = await this.getCurrentTabHostname();
this.logger.log('info', 'comms', '%c[UWServer::getCurrentSite] Returning data:', 'background-color: #243; color: #4a8', {site, tabHostname});
this.eventBus.send(
'set-current-site',
@ -356,12 +362,15 @@ export default class UWServer {
const activeTab = await this.activeTab;
if (!activeTab || activeTab.length < 1) {
this.logger.log('warn', 'comms', 'There is no active tab for some reason. activeTab:', activeTab);
return null;
}
const url = activeTab[0].url;
if (!url) {
console.log('no URL for active tab:', activeTab[0].url);
}
var hostname;
if (url.indexOf("://") > -1) { //find & remove protocol (http, ftp, etc.) and get hostname

View File

@ -15,10 +15,17 @@ import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
*/
export class SiteSettings {
private settings: Settings;
private site: string;
private _site: string;
private set site(x: string) {
this._site = x;
}
public get site() {
return this._site;
}
raw: SiteSettingsInterface; // actual settings
data: SiteSettingsInterface; // effective settings
usesSettingsFor: string | undefined;
temporaryData: SiteSettingsInterface;
sessionData: SiteSettingsInterface;
readonly defaultSettings: SiteSettingsInterface;
@ -47,12 +54,56 @@ export class SiteSettings {
chrome.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)})
}
/**
* Tries to match websites, even if we're on a different subdomain.
* @returns
*/
private getSettingsForSite() {
if (this.settings.active.sites[this.site]) {
return {
siteSettings: this.settings.active.sites[this.site],
usesSettingsFor: undefined
};
}
const urlSegments = this.site.split('.').reverse();
siteLoop:
for (const cs in this.settings.active.sites) {
const configUrlSegments = cs.split('.').reverse();
// Match site with wildcard site definitions
// Also, if definition starts with 'www', match also other subdomains — e.g. if we have a configuration for
// `www.example.com`, this will also match `example.com`, `subdomain.example.com`, `nested.subdomain.example.com` ...
if (configUrlSegments[configUrlSegments.length - 1] === '*' || (configUrlSegments[configUrlSegments.length - 1] === 'www')) {
console.log('ss: comparing', configUrlSegments, urlSegments);
for (let i = 0; i < configUrlSegments.length - 1 && i < urlSegments.length; i++) {
if (configUrlSegments[i] !== urlSegments[i]) {
continue siteLoop;
}
}
return {
siteSettings: this.settings.active.sites[cs],
usesSettingsFor: cs
}
}
}
return {
siteSettings: this.settings.active.sites['@global'],
usesSettingsFor: '@global'
};
}
/**
* Merges defaultSettings into site settings where appropriate.
* Alan pls ensure default settings object follows the correct structure
*/
private compileSettingsObject() {
this.data = _cp(this.settings.active.sites[this.site] ?? {})
const {siteSettings, usesSettingsFor} = this.getSettingsForSite();
this.data = _cp(siteSettings);
this.usesSettingsFor = usesSettingsFor;
if (!this.data) {
this.data = _cp(this.defaultSettings);
@ -150,8 +201,8 @@ export class SiteSettings {
// we aren't stepping on any other toes by doing this, since everyone
// gets the first change
this.settings.active._updateFlags = undefined;
this.settings.saveWithoutReload();
// this.settings.active._updateFlags = undefined;
// this.settings.saveWithoutReload();
}
}

View File

@ -695,10 +695,10 @@ class PlayerData {
this.equalish(elementStack[currentIndex].element.offsetWidth, elementStack[nextIndex].element.offsetWidth, 2)
&& this.equalish(elementStack[currentIndex].element.offsetHeight, elementStack[nextIndex].element.offsetHeight, 2)
) {
this.siteSettings.set('playerAutoConfig.initialIndex', this.siteSettings.data.playerAutoConfig.initialIndex + 1, {noSave: true});
this.siteSettings.set('playerAutoConfig.modified', true);
console.log('updated site settings:', this.siteSettings.data.playerAutoConfig);
this.videoData.settings.saveWithoutReload();
// this.siteSettings.set('playerAutoConfig.initialIndex', this.siteSettings.data.playerAutoConfig.initialIndex + 1, {noSave: true});
// this.siteSettings.set('playerAutoConfig.modified', true);
// console.log('updated site settings:', this.siteSettings.data.playerAutoConfig);
// this.videoData.settings.saveWithoutReload();
this.updatePlayer({newElement: elementStack[nextIndex].element});
}
@ -797,9 +797,9 @@ class PlayerData {
bestCandidate = null;
} else {
bestCandidate.heuristics['autoMatch'] = true;
if (this.siteSettings.data.playerAutoConfig?.initialIndex !== bestCandidate.index) {
this.siteSettings.set('playerAutoConfig.initialIndex', bestCandidate.index, {reload: false, scripted: true});
}
// if (this.siteSettings.data.playerAutoConfig?.initialIndex !== bestCandidate.index) {
// this.siteSettings.set('playerAutoConfig.initialIndex', bestCandidate.index, {reload: false, scripted: true});
// }
}
// BUT WAIT! THERE'S MORE