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;
|
||||
};
|
||||
|
||||
// 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(){
|
||||
var r=await api("GET","/api/collections/addon_config/records?perPage=200");
|
||||
CONFIGS = r.ok ? (r.data.items||[]) : [];
|
||||
cfgRec = CONFIGS[0] || null;
|
||||
cfg=null;
|
||||
if (cfgRec){
|
||||
try { cfg=JSON.parse(await decryptBlob(cfgRec.blob, PASSWORD, cfgRec.salt)); }
|
||||
catch(_){ cfg=null; }
|
||||
var opened=[];
|
||||
for (var i=0;i<CONFIGS.length;i++){
|
||||
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);
|
||||
$("adSetup").className = configured ? "hidden" : "";
|
||||
$("adEdit").className = configured ? "" : "hidden";
|
||||
|
|
@ -1222,24 +1251,36 @@ $("byGo").onclick=async function(){
|
|||
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){
|
||||
var merged=Object.assign({}, cfg||{}, next);
|
||||
var salt=(cfgRec && cfgRec.salt) || newSaltB64();
|
||||
var blob=await encryptWithSalt(JSON.stringify(merged), PASSWORD, salt);
|
||||
var body={ blob:blob, salt:salt, kdf:KDF_ID, updatedAt:new Date().toISOString() };
|
||||
var r;
|
||||
if (cfgRec){
|
||||
r=await api("PATCH","/api/collections/addon_config/records/"+cfgRec.id, body);
|
||||
} else {
|
||||
if (!PROFILES.length){ setMsg($("adMsg"),"Účet nemá profil.","err");
|
||||
if (!PROFILES.length && !CONFIGS.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);
|
||||
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();
|
||||
for (var j=0;j<PROFILES.length;j++){
|
||||
if (covered[PROFILES[j].id]) continue;
|
||||
var cr=await api("POST","/api/collections/addon_config/records",
|
||||
{ profile:PROFILES[j].id, blob:await encryptWithSalt(plain, PASSWORD, salt),
|
||||
salt:salt, kdf:KDF_ID, updatedAt:now });
|
||||
if (cr.ok) wrote++; else failed++;
|
||||
}
|
||||
if(btn) busy(btn,false);
|
||||
setMsg($("adMsg"), r.ok ? "Zdroje uloženy. Aplikace si je stáhne sama."
|
||||
: "Uložení selhalo.", r.ok?"ok":"err");
|
||||
if (r.ok) await loadAddons();
|
||||
var ok = wrote>0 && failed===0;
|
||||
setMsg($("adMsg"), ok ? "Zdroje uloženy. Aplikace si je stáhne sama."
|
||||
: "Uložení selhalo.", ok?"ok":"err");
|
||||
if (wrote>0) await loadAddons();
|
||||
}
|
||||
|
||||
$("adSave").onclick=async function(){
|
||||
|
|
|
|||
Loading…
Reference in a new issue