fix(e2e): update preset name in scanner E2E test from "ir" to "default" (for IOS version)

This commit is contained in:
Sarto
2026-05-07 15:07:47 +03:30
parent 6c799d9e7f
commit bf8d066d4d
11 changed files with 334 additions and 348 deletions
+91
View File
@@ -0,0 +1,91 @@
name: iOS Release (preview)
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag for the pre-release (e.g. v0.15.0-ios.1)'
required: true
default: 'v0.0.0-ios.0'
push:
tags: ['v*-ios.*']
permissions:
contents: write
jobs:
build:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.26'
cache: true
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode.app
- name: Install gomobile + gobind
run: |
go install golang.org/x/mobile/cmd/gomobile@latest
go install golang.org/x/mobile/cmd/gobind@latest
gomobile init
go get golang.org/x/mobile/bind golang.org/x/mobile/bind/objc
go mod tidy
- name: Build Mobile.xcframework
run: gomobile bind -iosversion=14.0 -target=ios,iossimulator -o ios/Mobile.xcframework ./mobile
- name: Archive (unsigned)
run: |
xcodebuild \
-project ios/Thefeed.xcodeproj \
-scheme Thefeed \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath build/Thefeed.xcarchive \
archive \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
DEVELOPMENT_TEAM=""
- name: Pack unsigned IPA
run: |
mkdir -p build/Payload
cp -r build/Thefeed.xcarchive/Products/Applications/Thefeed.app build/Payload/
(cd build && zip -qry "Thefeed-${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}-unsigned.ipa" Payload)
ls -lh build/*.ipa
- name: Resolve tag
id: tag
run: |
TAG="${GITHUB_REF_NAME}"
if [ -z "$TAG" ] || [ "$TAG" = "main" ]; then TAG="${{ github.event.inputs.tag }}"; fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Create pre-release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: ${{ steps.tag.outputs.tag }} (iOS preview)
prerelease: true
generate_release_notes: false
body: |
⚠️ **iOS-only preview release.**
This release ships only the iOS build. Other platforms
(server, Linux/macOS/Windows clients, Android APKs) are not
included — pull the latest stable tag for those.
The IPA is **unsigned**. To install:
- Re-sign with your own provisioning profile and distribute via TestFlight, or
- Use a sideloading tool such as AltStore / Sideloadly with your own Apple ID.
iOS 14.0 or newer required. Universal binary (iPhone + iPad).
files: |
build/Thefeed-*.ipa
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+5
View File
@@ -38,3 +38,8 @@ ios/DerivedData
ios/*.xcuserdata
ios/Thefeed.xcodeproj/xcuserdata
ios/Thefeed.xcodeproj/project.xcworkspace/xcuserdata
# Stale backups (sed/in-place edits)
*.bak
internal/web/static/iran-lion-sun.svg
internal/web/static/iran-lion-sun-dark.svg
+2 -2
View File
@@ -82,7 +82,7 @@ thefeed یک سیستم تونل DNS است که به شما اجازه می‌
رابط وب شامل یک اسکنر ریزالور داخلی است (آیکون 🔍 در نوار کناری) که بازه‌های IP را بررسی می‌کند تا سرورهای DNS قابل دسترسی به سرور thefeed شما را پیدا کند:
- **اهداف متنوع**: آی‌پی‌های تکی، CIDR (مثل `5.1.0.0/16`)، یا نام دامنه — هر خط یکی
- **بارگذاری CIDR ایران**: دکمه یک‌کلیکی برای بارگذاری لیست بازه‌های ISP ایران
- **بارگذاری CIDR پیش‌فرض**: دکمه یک‌کلیکی برای بارگذاری لیست پیش‌فرض بازه‌های ISP
- **پاک کردن اهداف**: دکمه برای پاک کردن سریع لیست CIDR/IP اسکنر
- **انتخاب پروفایل**: انتخاب کنید کدام پروفایل برای تست استفاده شود
- **قابل تنظیم**: همزمانی (پیش‌فرض ۵۰)، تایم‌اوت (پیش‌فرض ۱۵ ثانیه)، حداکثر آی‌پی
@@ -498,7 +498,7 @@ MIT
<div align="center">
**برای ایران آزاد** <img src="internal/web/static/iran-lion-sun.svg" alt="شیر و خورشید" height="20">
**برای ایران آزاد** <img src="internal/web/static/lion-sun.svg" alt="شیر و خورشید" height="20">
*هر ایرانی حق دسترسی آزاد به اطلاعات را دارد*
+2 -2
View File
@@ -504,7 +504,7 @@ The browser-based UI has:
The web UI includes a built-in resolver scanner (🔍 icon in sidebar) that probes IP ranges to discover DNS servers capable of reaching your thefeed server. Features:
- **Flexible targets**: enter individual IPs, CIDRs (e.g. `5.1.0.0/16`), or domain names — one per line
- **Iran CIDRs preset**: one-click button to load a curated list of Iranian ISP ranges
- **Default CIDR preset**: one-click button to load the bundled curated CIDR range list
- **Clear targets**: button to quickly clear the scanner CIDR/IP list
- **Profile-aware**: select which profile's domain and passphrase to use for probing
- **Configurable**: set concurrency (default 50), timeout (default 15s), and max IPs to scan
@@ -662,7 +662,7 @@ MIT
<div align="center">
**For FREE IRAN** <img src="internal/web/static/iran-lion-sun.svg" alt="Lion-and-Sun" height="20">
**For FREE IRAN** <img src="internal/web/static/lion-sun.svg" alt="Lion-and-Sun" height="20">
*Everyone deserves free access to information*
+11
View File
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>TheFeed</title>
<meta http-equiv="refresh" content="0; url=privacy-policy.html">
</head>
<body>
<p>See <a href="privacy-policy.html">Privacy Policy</a>.</p>
</body>
</html>
+186
View File
@@ -0,0 +1,186 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>TheFeed — Privacy Policy</title>
<style>
:root { color-scheme: light dark; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
max-width: 720px;
margin: 40px auto;
padding: 0 20px;
line-height: 1.6;
color: #222;
}
@media (prefers-color-scheme: dark) {
body { background: #111; color: #ddd; }
a { color: #6cb6ff; }
}
h1 { font-size: 1.8em; margin-bottom: 0; }
h2 { font-size: 1.2em; margin-top: 1.8em; }
.updated { color: #888; font-size: .9em; margin-top: 4px; }
ul { padding-left: 1.2em; }
li { margin: .4em 0; }
code { background: rgba(127,127,127,.15); padding: 1px 5px; border-radius: 4px; }
.lang-switch { margin-bottom: 24px; }
.lang-switch a { margin-right: 12px; }
section[hidden] { display: none; }
[dir="rtl"] { direction: rtl; text-align: right; }
[dir="rtl"] ul { padding-right: 1.2em; padding-left: 0; }
</style>
</head>
<body>
<div class="lang-switch">
<a href="#" onclick="show('en');return false">English</a>
<a href="#" onclick="show('fa');return false">فارسی</a>
</div>
<section id="en">
<h1>TheFeed — Privacy Policy</h1>
<p class="updated">Last updated: 2026-05-07</p>
<h2>Summary</h2>
<p>
TheFeed does not collect, store, sell, or transmit any personal information
to its developers or to any third party. Everything stays on your device or
on a server you choose to connect to.
</p>
<h2>What we collect</h2>
<p>
<strong>Nothing.</strong> The app has no analytics, no crash reporters, no
advertising SDKs, and no telemetry. The developers cannot see your usage,
your messages, your channels, your saved files, or any other data.
</p>
<h2>Data on your device</h2>
<p>
The app stores the following on your device only, inside its own sandboxed
storage area:
</p>
<ul>
<li>Server profiles you create (domain, passphrase, settings).</li>
<li>Cached messages and media downloaded from servers you connect to.</li>
<li>Resolver lists and the per-resolver health scoreboard.</li>
<li>Your interface language preference.</li>
</ul>
<p>
All of this is removed when you delete the app from your device.
</p>
<h2>Network connections</h2>
<p>
The app makes outbound network connections to:
</p>
<ul>
<li>DNS resolvers you or the developer of your selected server have configured.</li>
<li>The HTTPS endpoint of any optional relay (for example, the GitHub HTTPS API for the optional fast-relay download path).</li>
<li>Public Telegram channel preview pages, fetched through Google's Translate web proxy, when the optional channel-browse feature is enabled by the user.</li>
</ul>
<p>
These connections are made directly between your device and the chosen
third party. The app developers operate no central server and have no
access to the contents of those connections.
</p>
<h2>Permissions</h2>
<ul>
<li><strong>Photos (write only)</strong> — requested only when you choose to save a downloaded image or video to your camera roll.</li>
<li><strong>Local network</strong> — the app talks only to its own embedded server on <code>127.0.0.1</code>, which does not require system permission. The app does not scan your local network.</li>
</ul>
<h2>Children's privacy</h2>
<p>
The app is not directed at children under 13 and does not knowingly collect
information from them.
</p>
<h2>Changes</h2>
<p>
If this policy changes, the updated version will be posted at this URL.
The "Last updated" date above will reflect the change.
</p>
<h2>Contact</h2>
<p>
For questions, open an issue at
<a href="https://github.com/sartoopjj/thefeed/issues">github.com/sartoopjj/thefeed</a>.
</p>
</section>
<section id="fa" hidden dir="rtl">
<h1>TheFeed — حریم خصوصی</h1>
<p class="updated">آخرین به‌روزرسانی: ۲۰۲۶/۰۵/۰۷</p>
<h2>خلاصه</h2>
<p>
این برنامه هیچ اطلاعات شخصی‌ای را جمع‌آوری، ذخیره، فروش یا به سرور
سازنده ارسال نمی‌کند. تمام داده‌ها روی دستگاه شما یا روی سروری که خودتان
انتخاب می‌کنید باقی می‌ماند.
</p>
<h2>چه چیزی جمع‌آوری می‌کنیم</h2>
<p>
<strong>هیچ چیز.</strong> این برنامه هیچ سرویس آنالیتیکس، گزارش کرش،
تبلیغات یا تله‌متری ندارد. سازنده‌ی برنامه به استفاده، پیام‌ها، کانال‌ها،
فایل‌های ذخیره‌شده یا هیچ داده‌ی دیگری از شما دسترسی ندارد.
</p>
<h2>داده‌های روی دستگاه</h2>
<p>
برنامه فقط روی فضای ذخیره‌سازی محلی خود این داده‌ها را نگه می‌دارد:
</p>
<ul>
<li>پروفایل سرورهایی که می‌سازید (دامنه، رمز، تنظیمات).</li>
<li>پیام‌ها و رسانه‌های دانلود‌شده از سرور انتخابی.</li>
<li>لیست ریزالورها و امتیازهای سلامتی هرکدام.</li>
<li>زبان رابط کاربری انتخابی شما.</li>
</ul>
<p>با حذف برنامه از دستگاه، همه‌ی این داده‌ها هم پاک می‌شود.</p>
<h2>اتصال به شبکه</h2>
<p>برنامه به این موارد متصل می‌شود:</p>
<ul>
<li>ریزالورهای DNS که شما یا سازنده‌ی سرور انتخابی پیکربندی کرده‌اید.</li>
<li>HTTPS رله‌های اختیاری (مثلاً GitHub برای مسیر دانلود سریع اختیاری).</li>
<li>صفحات پیش‌نمایش کانال‌های عمومی تلگرام از طریق پروکسی وب گوگل ترنسلیت — فقط وقتی کاربر این قابلیت اختیاری را فعال کند.</li>
</ul>
<p>
این اتصال‌ها مستقیماً بین دستگاه شما و طرف سوم برقرار می‌شوند. سازنده
سرور مرکزی ندارد و به محتوای این اتصال‌ها دسترسی ندارد.
</p>
<h2>مجوزها</h2>
<ul>
<li><strong>تصاویر (فقط نوشتن)</strong> — فقط زمانی که خودتان تصمیم به ذخیره‌ی عکس یا ویدیو در گالری بگیرید.</li>
<li><strong>شبکه‌ی محلی</strong> — برنامه فقط با سرور داخلی روی <code>127.0.0.1</code> کار می‌کند که به مجوز سیستمی نیاز ندارد.</li>
</ul>
<h2>کودکان</h2>
<p>این برنامه برای کودکان زیر ۱۳ سال طراحی نشده.</p>
<h2>تغییرات</h2>
<p>هر تغییری در این متن، در همین آدرس منتشر می‌شود.</p>
<h2>تماس</h2>
<p>
برای سؤال یا گزارش مشکل:
<a href="https://github.com/sartoopjj/thefeed/issues">github.com/sartoopjj/thefeed</a>
</p>
</section>
<script>
function show(lang) {
document.getElementById('en').hidden = (lang !== 'en');
document.getElementById('fa').hidden = (lang !== 'fa');
document.documentElement.lang = lang;
}
// Auto-pick on load.
if ((navigator.language || '').toLowerCase().startsWith('fa')) show('fa');
</script>
</body>
</html>
+3 -3
View File
@@ -22,7 +22,7 @@ func (s *Server) handleScannerPresets(w http.ResponseWriter, r *http.Request) {
}
writeJSON(w, map[string]any{
"presets": []preset{
{Name: "ir", Label: "Iran", Count: parseScannerPresetCount()},
{Name: "default", Label: "Default", Count: parseScannerPresetCount()},
},
})
}
@@ -51,7 +51,7 @@ func (s *Server) handleScannerStart(w http.ResponseWriter, r *http.Request) {
var req struct {
Targets []string `json:"targets"`
Preset string `json:"preset"` // e.g. "ir" — server-side preset, avoids sending 50K IPs
Preset string `json:"preset"` // e.g. "default" — server-side preset, avoids sending 50K IPs
MaxIPs int `json:"maxIPs"`
RateLimit int `json:"rateLimit"`
Timeout float64 `json:"timeout"`
@@ -65,7 +65,7 @@ func (s *Server) handleScannerStart(w http.ResponseWriter, r *http.Request) {
}
// Resolve preset into targets server-side.
if req.Preset == "ir" && len(req.Targets) == 0 {
if req.Preset == "default" && len(req.Targets) == 0 {
req.Targets = parseScannerPresetLines()
}
+23 -23
View File
@@ -473,7 +473,7 @@
text-decoration: underline
}
.free-iran {
.positive-tag {
color: var(--success);
font-weight: 700
}
@@ -3440,7 +3440,7 @@
<label data-i18n="scanner_targets">IPs or CIDRs (one per line)</label>
<div style="display:flex;gap:4px">
<button class="btn btn-flat" onclick="document.getElementById('scanTargets').value='';updateScanIpCount()" data-i18n="scanner_clear_targets" style="font-size:12px;padding:4px 10px">&#128465; Clear</button>
<button class="btn btn-flat" onclick="loadScannerPresets()" style="font-size:12px;padding:4px 10px"><img class="iran-flag-icon" src="/static/iran-lion-sun.svg" alt="IR" style="height:14px;vertical-align:middle;margin-right:2px"> <span data-i18n="scanner_load_presets">Load IR Presets</span></button>
<button class="btn btn-flat" onclick="loadScannerPresets()" style="font-size:12px;padding:4px 10px"><img class="lion-sun-icon" src="/static/lion-sun.svg" alt="" style="height:14px;vertical-align:middle;margin-right:2px"> <span data-i18n="scanner_load_presets">Load Presets</span></button>
</div>
</div>
<textarea id="scanTargets" rows="3" placeholder="5.1.0.0/16&#10;8.8.8.8&#10;1.1.1.1" style="width:100%;font-family:monospace;font-size:13px" oninput="updateScanIpCount()"></textarea>
@@ -3713,8 +3713,8 @@
scanner_about_short: 'بازه‌های IP را اسکن کنید تا ریزالورهای DNS سازگار با سرور شما پیدا شوند.',
scanner_read_more: 'بیشتر بخوانید...',
scanner_read_less: 'بستن',
scanner_load_presets: 'بارگذاری لیست ایران',
scanner_preset_active: 'ریزالورهای ایران بارگذاری شد',
scanner_load_presets: 'بارگذاری لیست پیش‌فرض',
scanner_preset_active: 'ریزالورهای پیش‌فرض بارگذاری شد',
scanner_from_input: 'از ورودی',
scanner_from_preset: 'از پیش‌فرض',
scanner_new_scan: 'اسکن جدید',
@@ -3958,8 +3958,8 @@
scanner_about_short: 'Scan IP ranges to find DNS resolvers that work with your server.',
scanner_read_more: 'Read more...',
scanner_read_less: 'Show less',
scanner_load_presets: 'Load IR Presets',
scanner_preset_active: 'Iran resolvers loaded',
scanner_load_presets: 'Load Presets',
scanner_preset_active: 'Default resolvers loaded',
scanner_from_input: 'from input',
scanner_from_preset: 'from preset',
scanner_new_scan: 'New Scan',
@@ -5962,7 +5962,7 @@
}
h += '<div class="ch-avatar">' + avatarImg + '<span class="ch-avatar-letter">' + esc(avatarText) + '</span></div>';
var chSubText = !isX ? (handle.charAt(0) === '@' ? handle : '@' + handle) : '';
h += '<div class="ch-info"><div class="ch-name">' + formatIranTitleHtml(label) + (isPriv ? '<span class="ch-type-tag">' + t('private') + '</span>' : (isX ? '<span class="ch-type-tag x-tag">' + t('x_label') + '</span>' : '')) + '</div>';
h += '<div class="ch-info"><div class="ch-name">' + formatTitleWithFlag(label) + (isPriv ? '<span class="ch-type-tag">' + t('private') + '</span>' : (isX ? '<span class="ch-type-tag x-tag">' + t('x_label') + '</span>' : '')) + '</div>';
if (chSubText) h += '<div class="ch-sub">' + esc(chSubText) + '</div>';
h += '<div class="ch-preview">' + badge + '</div></div>';
var autoKey = handle.replace(/^@/, '').trim();
@@ -6021,7 +6021,7 @@
newMsgScrollDone = false;
openChat();
var ch = channels[num - 1]; var name = (ch && (ch.DisplayName || ch.displayName || ch.Name || ch.name)) || 'Channel ' + num;
document.getElementById('chatName').innerHTML = formatIranTitleHtml(name);
document.getElementById('chatName').innerHTML = formatTitleWithFlag(name);
var chHandle = (ch && (ch.Name || ch.name)) || '';
var isXCh = ch && (ch.ChatType || ch.chatType) === 2;
var subHandle = (!isXCh && chHandle) ? (chHandle.charAt(0) === '@' ? chHandle : '@' + chHandle) : '';
@@ -7329,8 +7329,8 @@
// and the body of a [REPLY] (so a reply that carries an image
// still renders a downloadable media card instead of dropping it).
function parseTextMedia(text, idBase) {
var iranify = function (s) {
return linkify(s).replace(/🇮🇷/g, '<img src="/static/iran-lion-sun.svg" alt="\u{1F981}☀️" style="height:1.1em;vertical-align:middle">');
var swapNationalFlag = function (s) {
return linkify(s).replace(/🇮🇷/g, '<img src="/static/lion-sun.svg" alt="\u{1F981}☀️" style="height:1.1em;vertical-align:middle">');
};
var mediaTypes = ['[IMAGE]', '[VIDEO]', '[FILE]', '[AUDIO]', '[STICKER]', '[GIF]', '[CONTACT]', '[LOCATION]'];
var rest = text;
@@ -7354,15 +7354,15 @@
var mediaHtml = '';
var textHtml = '';
if (cards.length === 0) {
textHtml = iranify(text);
textHtml = swapNationalFlag(text);
} else if (cards.length === 1) {
var c0 = cards[0];
if (c0.parsed.downloadable || c0.parsed.size > 0) {
mediaHtml = renderDownloadableMedia(c0.tag, c0.parsed, idBase);
textHtml = c0.parsed.caption ? iranify(c0.parsed.caption) : '';
textHtml = c0.parsed.caption ? swapNationalFlag(c0.parsed.caption) : '';
} else {
mediaHtml = '<div class="media-tag">' + c0.tag + '</div>';
textHtml = iranify(text.substring(c0.tag.length).replace(/^\n/, ''));
textHtml = swapNationalFlag(text.substring(c0.tag.length).replace(/^\n/, ''));
}
} else {
mediaHtml = '<div class="media-album">';
@@ -7370,7 +7370,7 @@
mediaHtml += renderDownloadableMedia(cards[k].tag, cards[k].parsed, idBase + '-' + k);
}
mediaHtml += '</div>';
textHtml = rest ? iranify(rest) : '';
textHtml = rest ? swapNationalFlag(rest) : '';
}
return { mediaHtml: mediaHtml, textHtml: textHtml };
}
@@ -7429,7 +7429,7 @@
text = '';
}
currentMsgTexts.push(text);
var mediaHtml = '', textHtml = linkify(text).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/iran-lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">');
var mediaHtml = '', textHtml = linkify(text).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">');
// Check for [REPLY]:ID or [REPLY] format (backward compat: also [REPLY:ID])
var replyMatch = text.match(/^\[REPLY\](?::(\d+))?/) || text.match(/^\[REPLY:(\d+)\]/);
if (replyMatch) {
@@ -7502,11 +7502,11 @@
if (c0.parsed.downloadable || c0.parsed.size > 0) {
mediaHtml = renderDownloadableMedia(c0.tag, c0.parsed, id);
textHtml = c0.parsed.caption
? linkify(c0.parsed.caption).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/iran-lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">')
? linkify(c0.parsed.caption).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">')
: '';
} else {
mediaHtml = '<div class="media-tag">' + c0.tag + '</div>';
textHtml = linkify(text.substring(c0.tag.length).replace(/^\n/, '')).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/iran-lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">');
textHtml = linkify(text.substring(c0.tag.length).replace(/^\n/, '')).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">');
}
} else {
mediaHtml = '<div class="media-album">';
@@ -7516,7 +7516,7 @@
}
mediaHtml += '</div>';
textHtml = rest
? linkify(rest).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/iran-lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">')
? linkify(rest).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1.1em;vertical-align:middle">')
: '';
}
}
@@ -7888,8 +7888,8 @@
}
// ===== UTILITIES =====
function formatIranTitleHtml(s) {
return esc(s).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/iran-lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1em;vertical-align:middle">');
function formatTitleWithFlag(s) {
return esc(s).replace(/\uD83C\uDDEE\uD83C\uDDF7/g, '<img src="/static/lion-sun.svg" alt="\u{1F981}\u2600\uFE0F" style="height:1em;vertical-align:middle">');
}
function esc(s) { var d = document.createElement('div'); d.appendChild(document.createTextNode(s)); return d.innerHTML }
function escAttr(s) { return esc(s).replace(/"/g, '&quot;').replace(/'/g, '&#39;') }
@@ -7936,7 +7936,7 @@
// ===== SCANNER =====
var scanPollTimer = null;
var scanLastResults = []; // cache for selection
var scannerActivePreset = ''; // server-side preset name (e.g. 'ir')
var scannerActivePreset = ''; // server-side preset name (e.g. 'default')
var scannerPresetIpCount = 0; // IP count from preset
function countCIDRIPs(text) {
@@ -7998,7 +7998,7 @@
}
async function loadScannerPresets() {
if (scannerActivePreset === 'ir') {
if (scannerActivePreset === 'default') {
// Toggle off
scannerActivePreset = '';
scannerPresetIpCount = 0;
@@ -8023,7 +8023,7 @@
var tag = document.getElementById('scanPresetTag');
if (scannerActivePreset) {
tag.style.display = '';
tag.innerHTML = '<img src="/static/iran-lion-sun.svg" alt="IR" style="height:14px;vertical-align:middle;margin-right:4px"> ' + t('scanner_preset_active');
tag.innerHTML = '<img src="/static/lion-sun.svg" alt="" style="height:14px;vertical-align:middle;margin-right:4px"> ' + t('scanner_preset_active');
} else {
tag.style.display = 'none';
tag.textContent = '';
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 248 B

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="Flag_of_Iran_1964"
data-name="Flag of Iran 1964"
id="lion_sun_flag"
data-name="Lion and Sun"
viewBox="0 0 1050 599.99999"
version="1.1"
xml:space="preserve"
sodipodi:docname="Lion and Sun flag.svg"
sodipodi:docname="lion-sun.svg"
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
width="1050"
height="600"
@@ -29,7 +29,7 @@
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Flag_of_Iran_1964" /><defs
inkscape:current-layer="lion_sun_flag" /><defs
id="defs1"><style
id="style1">
.cls-1 {

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+3 -3
View File
@@ -178,10 +178,10 @@ func TestE2E_Scanner_Presets(t *testing.T) {
if len(data.Presets) == 0 {
t.Error("expected non-empty presets list")
}
if data.Presets[0].Name != "ir" {
t.Errorf("first preset name = %q, want ir", data.Presets[0].Name)
if data.Presets[0].Name != "default" {
t.Errorf("first preset name = %q, want default", data.Presets[0].Name)
}
if data.Presets[0].Count == 0 {
t.Error("expected non-zero count for ir preset")
t.Error("expected non-zero count for default preset")
}
}