قطعی اینترنت بعد از 18 و 19 دی 1404 و قطعی سراسری اینترنت بعد از شروع جنگ سوم در اسفند 1404، باعث شد خیلی‌ها مثل من که دانش کمی توی برنامه‌نویسی داشتند، برای جبران قطع دسترسی‌ها و متناسب نیازهاشون دست به کار بشن و با گسترش دانش و آزمون و خطا، کارهایی انجام بدند که شاید شبیه اختراع چرخ از ابتدا به‌نظر می‌رسیده اما حداقل نیاز اونها رو مرتفع می‌کرده. من هم از این قافله جدا نبودم و برای این وبسایت که بر پایۀ وردپرس ساخته شده، پلاگین‌های اختصاصی نوشتم که گزارش اونها رو در زیر می‌تونید بخونید؛ شاید برای آیندگان کمکی باشه:

پلاگین شبیه‌ساز کانال تلگرام

سالیان سال فعالیت در شبکه‌های اجتماعی و اطلاع‌رسانی درخصوص کلاس‌ها، دوره‌ها، اشتراک‌گذاری منابع آموزشی و سرگرمی به‌زبان آلمانی، با قطع دسترسی از بین رفت. لاجرم من هم مجبور به کوچ به پیام‌رسان‌های بومی شدم اما پراکندگی این پیام‌رسان‌ها و عدم دسترسی به همۀ مخاطبین در یک پیام‌رسان واحد، باعث شد به این فکر بیوفتم که پلاگینی توسعه بدم تا با کمک اون، مطالب مندرج در تمام این پیام‌رسان‌ها و تلگرام، تجمیع شده و در قالب یک کانال، در صفحۀ اصلی نمایش داده بشه؛ تا با این کار بتونم پیام‌ها و اطلاعیه‌ها رو یکجا به تمام مخاطبین این وبسایت برسونم. انجام این کار، با توجه به رویکرد سنگین نکردن لود صفحه‌ای که شورت کد پلاگین درش قرار میگیره، چالش جدیدی برای من بود. موارد مهمی که در توسعۀ این پلاگین انجام/استفاده شدند رو در زیر می‌بینید.

1. Class Constants for Meta Keys – Encapsulation & DRY Principle

استفاده از class constants به جای hard-coded strings در تمام متدها:

const POST_TYPE = ‘tg_channel_post’;
const META_TYPE = ‘_tg_media_type’;
const META_URL = ‘_tg_media_url’;
const META_HASHTAGS = ‘_tg_hashtags_str’;

Benefit: Single point of truth.

تغییر یک constant در کلاس در همه‌جا propagate می‌شود. خطای تایپی در runtime به صفر می‌رسد.

Design Pattern: Constant Interface (refined).

2. Conditional Meta Box with WordPress Media API Integration

در متد render_meta_box، منطق شرطی برای نمایش فیلدهای وابسته به نوع رسانه، همراه با lazy initialization of wp.media frames:

var mediaFrame;
$(‘#tg_upload_btn’).on(‘click’, function(e) {
if (mediaFrame) { mediaFrame.open(); return; }
mediaFrame = wp.media({ title: ‘انتخاب رسانه’, … });
mediaFrame.on(‘select’, function() {
var attachment = mediaFrame.state().get(‘selection’).first().toJSON();
$(‘input[name=”tg_media_url”]’).val(attachment.url);
});
mediaFrame.open();
});

Technical note: Singleton pattern for media frames prevents multiple DOM listeners and redundant object instantiation.

UX: Dynamic toggling of poster and file options based on radio selection via jQuery toggle().

3. Optimized Hashtag Storage for Meta Query

استخراج هشتگ‌ها با regex و ذخیره به فرمت ,tag1,tag2, برای استفاده از LIKE در فیلتر:

$hashtags = $this->extract_hashtags($post->post_content);
$hashtags_str = ‘,’ . implode(‘,’, $hashtags) . ‘,’;
update_post_meta($post_id, self::META_HASHTAGS, $hashtags_str);

و در کوئری AJAX:

‘meta_query’ => [[
‘key’ => self::META_HASHTAGS,
‘value’ => ‘,’ . $hashtag . ‘,’,
‘compare’ => ‘LIKE’,
]];

Performance:

استفاده از LIKE با pattern ای که از leading/trailing comma استفاده می‌کند، ایندکس meta_value را (در حد امکان) کارآمد نگه می‌دارد.

Edge cases:

متد extract_hashtags با preg_match_all('/#([\w\-]+)/u', $text, $matches) تمامی هشتگ‌های فارسی و انگلیسی را پوشش می‌دهد.

4. Infinite Scroll with Debounced Search & URL State Management

در get_infinite_js، یک finite state machine برای مدیریت offset, hashtag filter, search query و loading flags پیاده شده:

var loading = false, hasMore = true, offset = 0, currentHashtag = ”, currentSearch = ”;

function performSearch() {
var searchVal = $.trim($(‘.tg-search-input’).val());
if (searchVal === currentSearch) return;
currentSearch = searchVal;
currentHashtag = ”;
resetAndLoad(); // offset = 0, container.empty(), fetch from start
}

$(‘.tg-search-input’).on(‘input’, function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(performSearch, 500); // debounce
});

Debouncing:

جلوگیری از ارسال درخواست‌های AJAX به ازای هر کاراکتر.

URL hash handling:

خواندن پارامتر tg_hashtag از $_GET و سپس ست کردن فیلتر مربوطه قبل از اولین load.

Smooth scroll:

پس از reset، با scrollToContainer کاربر را به بالای لیست هدایت می‌کند.

5. Persian Date Conversion – Graceful Fallback Pattern

داخل متد gregorian_to_jalali:

if (class_exists(‘IntlDateFormatter’)) {
$formatter = new IntlDateFormatter(
‘fa_IR@calendar=persian’,
IntlDateFormatter::NONE,
IntlDateFormatter::NONE,
‘Asia/Tehran’,
IntlDateFormatter::TRADITIONAL,
‘yyyy/MM/dd – HH:mm’
);
return $formatter->format($timestamp);
}
// Manual algorithm fallback

Robustness:

روی سرورهایی که intl extension ندارند، الگوریتم محاسبات دستی (با پشتیبانی از leap years) جایگزین می‌شود.

Timezone:

استفاده از Asia/Tehran برای نمایش ساعت محلی.

6. Intelligent Video Handling – Internal vs External URLs

$is_internal = (strpos($url, home_url()) !== false);
if ($is_internal) {
echo ‘<video controls preload=”metadata” poster=”…”>…’;
} else {
echo ‘<div class=”video-cover” onclick=”window.open(…)”>…’;
}

Internal:

از تگ <video> native با preload="metadata" برای کاهش بار اولیه.

External:

شبیه‌سازی thumbnail با پخش‌کننده overlay که کاربر را در یک tab جدید به منبع اصلی می‌برد.

تجربه کاربری یکپارچه بدون breaking UX.

7. Unique Hashtag Cloud – Single Optimized SQL Query

داخل get_all_unique_hashtags:

global $wpdb;
$query = $wpdb->get_col($wpdb->prepare(
“SELECT DISTINCT pm.meta_value
FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_type = %s
AND p.post_status = ‘publish'”,
$meta_key, $post_type
));