update applovin demo upload
This commit is contained in:
@@ -34,6 +34,16 @@ export function openApplovinUpload(_context: vscode.ExtensionContext) {
|
||||
await handleUpload(panel, msg.paths);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type === 'saveQr') {
|
||||
await handleSaveQr(panel, msg.url, msg.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type === 'saveAllQrs') {
|
||||
await handleSaveAllQrs(panel, msg.items || []);
|
||||
return;
|
||||
}
|
||||
} catch (err: any) {
|
||||
panel.webview.postMessage({ type: 'error', message: err?.message || String(err) });
|
||||
}
|
||||
@@ -124,6 +134,68 @@ async function handleUpload(panel: vscode.WebviewPanel, filePaths: string[]) {
|
||||
panel.webview.postMessage({ type: 'done' });
|
||||
}
|
||||
|
||||
function qrBaseName(name: string): string {
|
||||
return (name || 'qr').replace(/\.html?$/i, '').replace(/[\\/:*?"<>|]/g, '_') || 'qr';
|
||||
}
|
||||
|
||||
async function downloadQr(url: string, destPath: string): Promise<void> {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const buf = Buffer.from(await res.arrayBuffer());
|
||||
await fs.promises.writeFile(destPath, buf);
|
||||
}
|
||||
|
||||
async function handleSaveQr(panel: vscode.WebviewPanel, url: string, name: string) {
|
||||
if (!url) return;
|
||||
const base = qrBaseName(name);
|
||||
const defaultUri = vscode.Uri.file(path.join(require('os').homedir(), 'Downloads', `${base}.png`));
|
||||
const target = await vscode.window.showSaveDialog({
|
||||
defaultUri,
|
||||
filters: { 'PNG Image': ['png'] },
|
||||
saveLabel: 'Save QR',
|
||||
});
|
||||
if (!target) return;
|
||||
try {
|
||||
await downloadQr(url, target.fsPath);
|
||||
panel.webview.postMessage({ type: 'status', message: `Saved QR: ${target.fsPath}` });
|
||||
} catch (err: any) {
|
||||
panel.webview.postMessage({ type: 'error', message: 'Save QR failed: ' + (err?.message || err) });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveAllQrs(panel: vscode.WebviewPanel, items: Array<{ name: string; url: string }>) {
|
||||
if (!items.length) return;
|
||||
const picked = await vscode.window.showOpenDialog({
|
||||
canSelectFiles: false,
|
||||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
openLabel: 'Save QRs Here',
|
||||
});
|
||||
if (!picked || !picked.length) return;
|
||||
const dir = picked[0].fsPath;
|
||||
let saved = 0;
|
||||
const errors: string[] = [];
|
||||
for (const it of items) {
|
||||
const base = qrBaseName(it.name);
|
||||
let dest = path.join(dir, `${base}.png`);
|
||||
let n = 1;
|
||||
while (fs.existsSync(dest)) {
|
||||
dest = path.join(dir, `${base} (${n++}).png`);
|
||||
}
|
||||
try {
|
||||
await downloadQr(it.url, dest);
|
||||
saved++;
|
||||
} catch (err: any) {
|
||||
errors.push(`${it.name}: ${err?.message || err}`);
|
||||
}
|
||||
}
|
||||
if (errors.length) {
|
||||
panel.webview.postMessage({ type: 'error', message: `Saved ${saved}/${items.length}. Errors:\n` + errors.join('\n') });
|
||||
} else {
|
||||
panel.webview.postMessage({ type: 'status', message: `Saved ${saved} QR${saved === 1 ? '' : 's'} to ${dir}` });
|
||||
}
|
||||
}
|
||||
|
||||
function getHtml(): string {
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -163,11 +235,15 @@ function getHtml(): string {
|
||||
<p style="opacity:0.8;font-size:12px;margin-top:0;">Upload HTML files to p.applov.in and get QR codes to scan with the AppLovin Playable Preview app (iOS/Android).</p>
|
||||
|
||||
<div class="file-cell" style="margin-bottom:8px;">
|
||||
<button id="pick" class="secondary">Choose files...</button>
|
||||
<button id="pick" class="secondary">Add files...</button>
|
||||
<button id="clear" class="secondary" style="display:none;">Clear</button>
|
||||
<span class="file-name" id="fileName">(no files)</span>
|
||||
</div>
|
||||
<div id="fileList" style="margin-bottom:8px;font-size:12px;opacity:0.85;"></div>
|
||||
<div class="actions">
|
||||
<button id="upload">Upload to AppLovin</button>
|
||||
<button id="saveAll" class="secondary" style="display:none;">Save All QRs...</button>
|
||||
<button id="regenAll" class="secondary" style="display:none;">Regenerate QRs</button>
|
||||
</div>
|
||||
<div id="status"></div>
|
||||
<div id="results"></div>
|
||||
@@ -175,13 +251,51 @@ function getHtml(): string {
|
||||
<script>
|
||||
const vscode = acquireVsCodeApi();
|
||||
const pickBtn = document.getElementById('pick');
|
||||
const clearBtn = document.getElementById('clear');
|
||||
const uploadBtn = document.getElementById('upload');
|
||||
const fileNameEl = document.getElementById('fileName');
|
||||
const fileListEl = document.getElementById('fileList');
|
||||
const statusEl = document.getElementById('status');
|
||||
const resultsEl = document.getElementById('results');
|
||||
const saveAllBtn = document.getElementById('saveAll');
|
||||
const regenAllBtn = document.getElementById('regenAll');
|
||||
let selectedPaths = [];
|
||||
let resultItems = [];
|
||||
|
||||
function basename(p) {
|
||||
const i = Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\\\'));
|
||||
return i >= 0 ? p.slice(i + 1) : p;
|
||||
}
|
||||
function renderSelection() {
|
||||
if (!selectedPaths.length) {
|
||||
fileNameEl.textContent = '(no files)';
|
||||
fileListEl.innerHTML = '';
|
||||
clearBtn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
fileNameEl.textContent = selectedPaths.length + ' file' + (selectedPaths.length === 1 ? '' : 's');
|
||||
fileListEl.innerHTML = selectedPaths.map(p => '<div>' + escapeHtml(basename(p)) + '</div>').join('');
|
||||
clearBtn.style.display = '';
|
||||
}
|
||||
|
||||
saveAllBtn.addEventListener('click', () => {
|
||||
if (!resultItems.length) return;
|
||||
vscode.postMessage({ type: 'saveAllQrs', items: resultItems });
|
||||
});
|
||||
|
||||
regenAllBtn.addEventListener('click', () => {
|
||||
const imgs = resultsEl.querySelectorAll('img[data-qr]');
|
||||
imgs.forEach(img => {
|
||||
const base = img.getAttribute('data-qr');
|
||||
img.src = base + '&_=' + Date.now();
|
||||
});
|
||||
});
|
||||
|
||||
pickBtn.addEventListener('click', () => vscode.postMessage({ type: 'pickFiles' }));
|
||||
clearBtn.addEventListener('click', () => {
|
||||
selectedPaths = [];
|
||||
renderSelection();
|
||||
});
|
||||
|
||||
uploadBtn.addEventListener('click', () => {
|
||||
statusEl.textContent = '';
|
||||
@@ -191,6 +305,9 @@ function getHtml(): string {
|
||||
return;
|
||||
}
|
||||
uploadBtn.disabled = true;
|
||||
resultItems = [];
|
||||
saveAllBtn.style.display = 'none';
|
||||
regenAllBtn.style.display = 'none';
|
||||
vscode.postMessage({ type: 'upload', paths: selectedPaths });
|
||||
});
|
||||
|
||||
@@ -201,13 +318,14 @@ function getHtml(): string {
|
||||
window.addEventListener('message', (e) => {
|
||||
const m = e.data;
|
||||
if (m.type === 'filesSelected') {
|
||||
selectedPaths = m.files.map(f => f.path);
|
||||
const names = m.files.map(f => f.name);
|
||||
fileNameEl.textContent = names.length === 1
|
||||
? names[0]
|
||||
: names.length + ' files: ' + names.slice(0, 3).join(', ') + (names.length > 3 ? '...' : '');
|
||||
const added = m.files.map(f => f.path).filter(p => !selectedPaths.includes(p));
|
||||
selectedPaths = selectedPaths.concat(added);
|
||||
renderSelection();
|
||||
} else if (m.type === 'start') {
|
||||
resultsEl.innerHTML = '';
|
||||
resultItems = [];
|
||||
saveAllBtn.style.display = 'none';
|
||||
regenAllBtn.style.display = 'none';
|
||||
} else if (m.type === 'status') {
|
||||
statusEl.textContent = m.message;
|
||||
statusEl.className = '';
|
||||
@@ -230,27 +348,28 @@ function getHtml(): string {
|
||||
wrap.innerHTML =
|
||||
'<div style="font-weight:600;margin-bottom:8px;word-break:break-all;">' + escapeHtml(m.name) + '</div>' +
|
||||
'<div style="text-align:center;margin-bottom:8px;">' +
|
||||
'<img src="' + m.qrImg + '" alt="QR" style="background:#fff;padding:8px;border-radius:4px;max-width:100%;" />' +
|
||||
'<img src="' + m.qrImg + '" data-qr="' + m.qrImg + '" alt="QR" style="background:#fff;padding:8px;border-radius:4px;max-width:100%;" />' +
|
||||
'</div>' +
|
||||
'<div style="font-size:11px;opacity:0.8;word-break:break-all;">Hash: ' + escapeHtml(m.hash) + '</div>';
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'result-actions';
|
||||
const copyHash = document.createElement('button');
|
||||
copyHash.textContent = 'Copy Hash';
|
||||
copyHash.onclick = () => vscode.postMessage({ type: 'copy', text: m.hash });
|
||||
const copyPreview = document.createElement('button');
|
||||
copyPreview.textContent = 'Copy URL';
|
||||
copyPreview.className = 'secondary';
|
||||
copyPreview.onclick = () => vscode.postMessage({ type: 'copy', text: m.previewUrl });
|
||||
const openPreview = document.createElement('button');
|
||||
openPreview.textContent = 'Open';
|
||||
openPreview.className = 'secondary';
|
||||
openPreview.onclick = () => vscode.postMessage({ type: 'open', text: m.previewUrl });
|
||||
actions.appendChild(copyHash);
|
||||
actions.appendChild(copyPreview);
|
||||
const saveQr = document.createElement('button');
|
||||
saveQr.textContent = 'Save QR';
|
||||
saveQr.className = 'secondary';
|
||||
saveQr.onclick = () => vscode.postMessage({ type: 'saveQr', url: m.qrImg, name: m.name });
|
||||
actions.appendChild(openPreview);
|
||||
actions.appendChild(saveQr);
|
||||
wrap.appendChild(actions);
|
||||
resultsEl.appendChild(wrap);
|
||||
resultItems.push({ name: m.name, url: m.qrImg });
|
||||
if (resultItems.length >= 2) {
|
||||
saveAllBtn.style.display = '';
|
||||
regenAllBtn.style.display = '';
|
||||
}
|
||||
} else if (m.type === 'done') {
|
||||
statusEl.innerHTML = '<span class="ok">Done.</span>';
|
||||
uploadBtn.disabled = false;
|
||||
|
||||
Reference in New Issue
Block a user