Sources page: one set of sources for every profile on the account
The app kept a copy of the addon config per profile, and this page read and edited only the first one. Sources set here therefore never reached the other profiles: the owner's Kids profile went on with a Czech addon address that had no webshare in it. The page now opens every copy and shows one merged answer (the newest value per field, and the longest Czech addon URL whatever its age, since that is the one carrying webshare's credentials too). Saving writes the same sources to every copy and creates one for a profile without. The app does the same from 1.3.0+37. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
4c8e02f48b
commit
db50af0c69
1 changed files with 58 additions and 17 deletions
|
|
@ -1111,15 +1111,44 @@ $("adUnlock").onclick=async function(){
|
||||||
$("acChange").disabled=false;
|
$("acChange").disabled=false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// One account, one set of sources. The app used to keep a copy per profile,
|
||||||
|
// so an account that ever switched profile holds several copies that drifted
|
||||||
|
// apart: sources edited here reached only the first, and a child profile went
|
||||||
|
// on without webshare. Read every copy and show one answer. The newest value
|
||||||
|
// wins per field, except the Czech addon URL, where the longer one wins: it is
|
||||||
|
// the one that carries webshare's credentials as well as prehraj.to's.
|
||||||
|
function mergeConfigs(list){
|
||||||
|
var sorted=list.slice().sort(function(a,b){
|
||||||
|
return String(b.updatedAt||"").localeCompare(String(a.updatedAt||"")); });
|
||||||
|
var out={};
|
||||||
|
sorted.forEach(function(e){
|
||||||
|
Object.keys(e.cfg||{}).forEach(function(k){
|
||||||
|
var v=e.cfg[k];
|
||||||
|
if ((out[k]==null || out[k]==="") && v!=null && v!=="") out[k]=v;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var longest="";
|
||||||
|
list.forEach(function(e){
|
||||||
|
var u=(e.cfg||{}).czechAddonUrl||"";
|
||||||
|
if (u.length>longest.length) longest=u;
|
||||||
|
});
|
||||||
|
if (longest) out.czechAddonUrl=longest;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadAddons(){
|
async function loadAddons(){
|
||||||
var r=await api("GET","/api/collections/addon_config/records?perPage=200");
|
var r=await api("GET","/api/collections/addon_config/records?perPage=200");
|
||||||
CONFIGS = r.ok ? (r.data.items||[]) : [];
|
CONFIGS = r.ok ? (r.data.items||[]) : [];
|
||||||
cfgRec = CONFIGS[0] || null;
|
cfgRec = CONFIGS[0] || null;
|
||||||
cfg=null;
|
cfg=null;
|
||||||
if (cfgRec){
|
var opened=[];
|
||||||
try { cfg=JSON.parse(await decryptBlob(cfgRec.blob, PASSWORD, cfgRec.salt)); }
|
for (var i=0;i<CONFIGS.length;i++){
|
||||||
catch(_){ cfg=null; }
|
try {
|
||||||
|
opened.push({ cfg: JSON.parse(await decryptBlob(CONFIGS[i].blob, PASSWORD, CONFIGS[i].salt)),
|
||||||
|
updatedAt: CONFIGS[i].updatedAt });
|
||||||
|
} catch(_){}
|
||||||
}
|
}
|
||||||
|
if (opened.length) cfg=mergeConfigs(opened);
|
||||||
var configured = cfg && (cfg.addonUrl || cfg.czechAddonUrl);
|
var configured = cfg && (cfg.addonUrl || cfg.czechAddonUrl);
|
||||||
$("adSetup").className = configured ? "hidden" : "";
|
$("adSetup").className = configured ? "hidden" : "";
|
||||||
$("adEdit").className = configured ? "" : "hidden";
|
$("adEdit").className = configured ? "" : "hidden";
|
||||||
|
|
@ -1222,24 +1251,36 @@ $("byGo").onclick=async function(){
|
||||||
await saveConfig(out, $("byGo"));
|
await saveConfig(out, $("byGo"));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The same sources go to every profile's copy, and a profile without one gets
|
||||||
|
// one: a device reads the copy of whichever profile it is on, and an older app
|
||||||
|
// that found none would make its own from whatever it had.
|
||||||
async function saveConfig(next, btn){
|
async function saveConfig(next, btn){
|
||||||
var merged=Object.assign({}, cfg||{}, next);
|
if (!PROFILES.length && !CONFIGS.length){ setMsg($("adMsg"),"Účet nemá profil.","err");
|
||||||
|
if(btn) busy(btn,false); return; }
|
||||||
|
var plain=JSON.stringify(Object.assign({}, cfg||{}, next));
|
||||||
|
var now=new Date().toISOString();
|
||||||
|
var wrote=0, failed=0, covered={};
|
||||||
|
for (var i=0;i<CONFIGS.length;i++){
|
||||||
|
var rec=CONFIGS[i];
|
||||||
|
covered[rec.profile]=true;
|
||||||
|
var pr=await api("PATCH","/api/collections/addon_config/records/"+rec.id,
|
||||||
|
{ blob:await encryptWithSalt(plain, PASSWORD, rec.salt), salt:rec.salt,
|
||||||
|
kdf:KDF_ID, updatedAt:now });
|
||||||
|
if (pr.ok) wrote++; else failed++;
|
||||||
|
}
|
||||||
var salt=(cfgRec && cfgRec.salt) || newSaltB64();
|
var salt=(cfgRec && cfgRec.salt) || newSaltB64();
|
||||||
var blob=await encryptWithSalt(JSON.stringify(merged), PASSWORD, salt);
|
for (var j=0;j<PROFILES.length;j++){
|
||||||
var body={ blob:blob, salt:salt, kdf:KDF_ID, updatedAt:new Date().toISOString() };
|
if (covered[PROFILES[j].id]) continue;
|
||||||
var r;
|
var cr=await api("POST","/api/collections/addon_config/records",
|
||||||
if (cfgRec){
|
{ profile:PROFILES[j].id, blob:await encryptWithSalt(plain, PASSWORD, salt),
|
||||||
r=await api("PATCH","/api/collections/addon_config/records/"+cfgRec.id, body);
|
salt:salt, kdf:KDF_ID, updatedAt:now });
|
||||||
} else {
|
if (cr.ok) wrote++; else failed++;
|
||||||
if (!PROFILES.length){ setMsg($("adMsg"),"Účet nemá profil.","err");
|
|
||||||
if(btn) busy(btn,false); return; }
|
|
||||||
body.profile=PROFILES[0].id;
|
|
||||||
r=await api("POST","/api/collections/addon_config/records", body);
|
|
||||||
}
|
}
|
||||||
if(btn) busy(btn,false);
|
if(btn) busy(btn,false);
|
||||||
setMsg($("adMsg"), r.ok ? "Zdroje uloženy. Aplikace si je stáhne sama."
|
var ok = wrote>0 && failed===0;
|
||||||
: "Uložení selhalo.", r.ok?"ok":"err");
|
setMsg($("adMsg"), ok ? "Zdroje uloženy. Aplikace si je stáhne sama."
|
||||||
if (r.ok) await loadAddons();
|
: "Uložení selhalo.", ok?"ok":"err");
|
||||||
|
if (wrote>0) await loadAddons();
|
||||||
}
|
}
|
||||||
|
|
||||||
$("adSave").onclick=async function(){
|
$("adSave").onclick=async function(){
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue