mirror of
https://github.com/sartoopjj/thefeed.git
synced 2026-05-18 02:14:43 +03:00
fix(e2e): update preset name in scanner E2E test from "ir" to "default" (for IOS version)
This commit is contained in:
@@ -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 }}
|
||||
@@ -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
@@ -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">
|
||||
|
||||
*هر ایرانی حق دسترسی آزاد به اطلاعات را دارد*
|
||||
|
||||
|
||||
@@ -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*
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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">🗑 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 8.8.8.8 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, '"').replace(/'/g, ''') }
|
||||
@@ -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 |
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user