213 lines
16 KiB
HTML
213 lines
16 KiB
HTML
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
.wrap { padding: 1.25rem 1rem; font-size: 14px; color: var(--color-text-primary); direction: rtl; }
|
||
h1 { font-size: 18px; font-weight: 500; margin: 0 0 3px; }
|
||
.sub { font-size: 13px; color: var(--color-text-secondary); margin: 0 0 1.25rem; }
|
||
.badge { display: inline-flex; align-items: center; font-size: 11px; font-weight: 500; padding: 2px 8px; border-radius: 20px; white-space: nowrap; }
|
||
.b-ok { background: var(--color-background-success); color: var(--color-text-success); }
|
||
.b-new { background: var(--color-background-danger); color: var(--color-text-danger); }
|
||
.b-med { background: var(--color-background-warning); color: var(--color-text-warning); }
|
||
.b-min { background: var(--color-background-info); color: var(--color-text-info); }
|
||
.progress-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 1.25rem; }
|
||
.pcard { background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 12px 14px; }
|
||
.pcard .val { font-size: 28px; font-weight: 500; }
|
||
.pcard .lbl { font-size: 12px; color: var(--color-text-secondary); margin-top: 2px; }
|
||
.ok-val { color: var(--color-text-success); }
|
||
.bad-val { color: var(--color-text-danger); }
|
||
.new-val { color: var(--color-text-warning); }
|
||
.section { margin-bottom: 1.4rem; }
|
||
.section-hdr { font-size: 14px; font-weight: 500; margin: 0 0 8px; display: flex; align-items: center; gap: 8px; }
|
||
.card { background: var(--color-background-primary); border: 0.5px solid var(--color-border-tertiary); border-radius: var(--border-radius-lg); margin-bottom: 8px; overflow: hidden; }
|
||
.card.fixed { border-right: 3px solid var(--color-border-success); }
|
||
.card.broken{ border-right: 3px solid var(--color-border-danger); }
|
||
.card.new { border-right: 3px solid var(--color-border-warning); }
|
||
.card.minor { border-right: 3px solid var(--color-border-info); }
|
||
.ch { display: flex; align-items: flex-start; gap: 8px; padding: 10px 14px; cursor: pointer; }
|
||
.ch:hover { background: var(--color-background-secondary); }
|
||
.ch-icon { font-size: 15px; flex-shrink: 0; margin-top: 1px; }
|
||
.ch-title { font-size: 13.5px; font-weight: 500; flex: 1; line-height: 1.4; }
|
||
.ch-badge { flex-shrink: 0; }
|
||
.chev { font-size: 11px; color: var(--color-text-tertiary); transition: transform .2s; margin-right: auto; margin-left: 4px; }
|
||
.chev.open { transform: rotate(90deg); }
|
||
.cb { display: none; padding: 0 14px 14px; border-top: 0.5px solid var(--color-border-tertiary); }
|
||
.cb.open { display: block; }
|
||
.cb p { font-size: 13px; color: var(--color-text-secondary); line-height: 1.7; margin: 8px 0 6px; }
|
||
pre { font-family: var(--font-mono); font-size: 11.5px; background: var(--color-background-tertiary); border: 0.5px solid var(--color-border-tertiary); border-radius: var(--border-radius-md); padding: 9px 11px; overflow-x: auto; margin: 6px 0; line-height: 1.6; white-space: pre; }
|
||
.fix { background: var(--color-background-success); border-radius: var(--border-radius-md); padding: 8px 11px; margin-top: 8px; font-size: 13px; line-height: 1.6; }
|
||
.fix strong { color: var(--color-text-success); font-size: 11px; display: block; margin-bottom: 2px; }
|
||
.warn { background: var(--color-background-warning); border-radius: var(--border-radius-md); padding: 8px 11px; margin-top: 8px; font-size: 13px; line-height: 1.6; }
|
||
.warn strong { color: var(--color-text-warning); font-size: 11px; display: block; margin-bottom: 2px; }
|
||
code { font-family: var(--font-mono); font-size: 12px; background: var(--color-background-secondary); padding: 0 4px; border-radius: 3px; }
|
||
.score-row { display: flex; align-items: center; gap: 10px; font-size: 13px; margin-bottom: 7px; }
|
||
.score-lbl { min-width: 160px; color: var(--color-text-secondary); }
|
||
.strack { flex: 1; height: 6px; background: var(--color-border-tertiary); border-radius: 3px; position: relative; }
|
||
.sfill { height: 100%; border-radius: 3px; }
|
||
.sval { min-width: 36px; font-size: 12px; color: var(--color-text-secondary); text-align: left; }
|
||
</style>
|
||
|
||
<div class="wrap">
|
||
<h1>مراجعة النسخة المحدّثة — V2</h1>
|
||
<p class="sub">مقارنة مع المراجعة السابقة · 16 مشكلة فُحصت</p>
|
||
|
||
<div class="progress-row">
|
||
<div class="pcard"><div class="val ok-val">11</div><div class="lbl">مشكلة مُصلحة ✅</div></div>
|
||
<div class="pcard"><div class="val bad-val">2</div><div class="lbl">مشكلة جديدة أدخلتها الإصلاحات ⚠️</div></div>
|
||
<div class="pcard"><div class="val new-val">3</div><div class="lbl">مشكلة لم تُعالج بعد</div></div>
|
||
<div class="pcard"><div class="val ok-val">69%</div><div class="lbl">تحسن من المراجعة الأولى</div></div>
|
||
</div>
|
||
|
||
<div class="score-row"><span class="score-lbl">صحة المنطق البرمجي</span><div class="strack"><div class="sfill" style="width:72%;background:#3B8BD4"></div></div><span class="sval">72% ↑</span></div>
|
||
<div class="score-row"><span class="score-lbl">نظافة الكود</span><div class="strack"><div class="sfill" style="width:63%;background:#1D9E75"></div></div><span class="sval">63% ↑</span></div>
|
||
<div class="score-row" style="margin-bottom:1.4rem"><span class="score-lbl">قابلية الصيانة</span><div class="strack"><div class="sfill" style="width:58%;background:#1D9E75"></div></div><span class="sval">58% ↑</span></div>
|
||
|
||
<!-- FIXED -->
|
||
<div class="section">
|
||
<div class="section-hdr"><span class="badge b-ok">✅ مُصلح</span> ما تم إصلاحه بشكل صحيح</div>
|
||
|
||
<div class="card fixed">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">✅</span><span class="ch-title">C-1 — استبدال الحلقة التكرارية والاستدعاء الذاتي بـ <code>Timer.periodic</code></span><span class="ch-badge badge b-ok">ممتاز</span><span class="chev">▶</span></div>
|
||
<div class="cb"><p>تم حذف <code>updateLocation()</code> كاملاً واستبدالها بـ <code>startUpdateLocationTimer()</code> و <code>stopUpdateLocationTimer()</code>. التايمر مسجّل في <code>onClose()</code> و <code>_stopAllServices()</code>. إصلاح ممتاز.</p>
|
||
<div class="warn"><strong>ملاحظة مهمة</strong>لا يظهر في الكود استدعاء لـ <code>startUpdateLocationTimer()</code> من أي مكان. يجب التأكد أنها تُستدعى من الـ View أو من <code>startRideFromDriver()</code>.</div></div>
|
||
</div>
|
||
|
||
<div class="card fixed">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">✅</span><span class="ch-title">C-4 — تحديث <code>myLocation</code> في <code>_handleLocationUpdate()</code></span><span class="chev">▶</span></div>
|
||
<div class="cb"><pre>void _handleLocationUpdate(geo.Position pos) {
|
||
final newLoc = LatLng(pos.latitude, pos.longitude);
|
||
myLocation = newLoc; // ← [Fix C-4] ✅ صحيح
|
||
// ...</pre></div>
|
||
</div>
|
||
|
||
<div class="card fixed">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">✅</span><span class="ch-title">M-4 — دمج <code>checkForNextStep()</code> مع <code>_checkNavigationStep()</code></span><span class="chev">▶</span></div>
|
||
<div class="cb"><p><code>checkForNextStep</code> أصبحت wrapper بسيط يستدعي <code>_checkNavigationStep</code>. منطق واحد، لا تعارض.</p></div>
|
||
</div>
|
||
|
||
<div class="card fixed">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">✅</span><span class="ch-title">M-5 — <code>disposeEverything()</code> لا تستدعي <code>onClose()</code> يدوياً</span><span class="chev">▶</span></div>
|
||
<div class="cb"><pre>void disposeEverything() {
|
||
_stopAllServices(); // ✅ بدون onClose()
|
||
}</pre></div>
|
||
</div>
|
||
|
||
<div class="card fixed">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">✅</span><span class="ch-title">C-3 جزئي — دالة مساعدة <code>_parseDistanceToMeters()</code> مشتركة</span><span class="chev">▶</span></div>
|
||
<div class="cb"><p>تم استخراج منطق تحليل المسافة إلى دالة واحدة تستخدمها كلا <code>finishRideFromDriver()</code> و <code>_validateTripDistance()</code>. يحل مشكلة التضارب في الوحدات.</p>
|
||
<div class="warn"><strong>لم يُحل كاملاً</strong>التحقق من المسافة لا يزال يحدث مرتين (انظر مشكلة C-3 أدناه).</div></div>
|
||
</div>
|
||
|
||
<div class="card fixed">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">✅</span><span class="ch-title">M-1 + M-2 + M-6 + N-1 + N-5 — إصلاحات طفيفة متعددة</span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<p><strong>M-1:</strong> <code>jitterMeters</code> → <code>jitterKm = 0.01</code> ✅</p>
|
||
<p><strong>M-2:</strong> <code>distance</code> المحلية → <code>distToPassenger</code> ✅</p>
|
||
<p><strong>M-6:</strong> تعليق يوضح أن الوحدة كيلومتر ✅</p>
|
||
<p><strong>N-1:</strong> <code>&directionsmode</code> → <code>?directionsmode</code> ✅</p>
|
||
<p><strong>N-5:</strong> إضافة <code>update()</code> في <code>getLocationArea()</code> ✅</p>
|
||
<p><strong>M-3:</strong> حذف <code>_performanceReadings</code> والمتغيرات الميتة ✅</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- NEW BUGS -->
|
||
<div class="section">
|
||
<div class="section-hdr"><span class="badge b-new">🚨 جديد</span> مشاكل أدخلتها الإصلاحات</div>
|
||
|
||
<div class="card new">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">🚨</span><span class="ch-title">BUG جديد — <code>Completer</code> في C-2 يُسبب Deadlock عند إغلاق الديالوج بـ Back</span><span class="ch-badge badge b-new">حرج</span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<p>الإصلاح استخدم <code>Completer</code> بشكل صحيح لحل مشكلة الـ callback الآني، لكنه أدخل مشكلة أخرى: لو أغلق المستخدم الديالوج بزر الرجوع (Back) في Android بدون ضغط OK، فإن <code>completer.future</code> لن تكتمل أبداً، والدالة ستبقى معلّقة (deadlock) لأن <code>_validateTripDistance()</code> هي <code>async</code> وتنتظر نتيجة لن تأتي:</p>
|
||
<pre>final completer = Completer<bool>();
|
||
MyDialog().getDialog('Exit Ride?'.tr, '', () {
|
||
if (!completer.isCompleted) completer.complete(true);
|
||
Get.back();
|
||
});
|
||
return await completer.future; // ← ينتظر للأبد إذا أُغلق بـ Back</pre>
|
||
<div class="fix"><strong>الحل</strong>أضف <code>barrierDismissible: false</code> للديالوج، أو استخدم <code>completer.complete(false)</code> عند إغلاق الديالوج بدون تأكيد (عبر <code>WillPopScope</code> أو <code>onDismissed</code> callback في <code>MyDialog</code>).</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card new">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">🚨</span><span class="ch-title">C-3 لا يزال — المستخدم يرى ديالوجَي تأكيد متتاليَين عند إنهاء الرحلة بالزر</span><span class="ch-badge badge b-new">حرج</span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<p>رغم إضافة <code>_parseDistanceToMeters()</code>، تدفق الكود لا يزال يُقدّم ديالوجَين:</p>
|
||
<pre>// finishRideFromDriver(isFromSlider: false):
|
||
MyDialog().getDialog('Are you sure to exit ride?', '', () {
|
||
Get.back();
|
||
finishRideFromDriver1(); // ← isFromSlider = false افتراضياً
|
||
});
|
||
|
||
// finishRideFromDriver1():
|
||
if (!await _validateTripDistance(false)) return; // ← يُقدّم ديالوجاً ثانياً!</pre>
|
||
<p>المستخدم يرى "هل أنت متأكد؟" → يضغط OK → يرى "Exit Ride?" مرة ثانية → ينتظر مجدداً.</p>
|
||
<div class="fix"><strong>الحل</strong>احذف الديالوج من <code>finishRideFromDriver()</code> وأبقه في <code>_validateTripDistance()</code> فقط. أو مرّر <code>isFromSlider: true</code> لما يأتي من موافقة مسبقة.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- REMAINING -->
|
||
<div class="section">
|
||
<div class="section-hdr"><span class="badge b-med">⚠️ لم تُعالج</span> مشاكل لا تزال قائمة</div>
|
||
|
||
<div class="card broken">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">⚠️</span><span class="ch-title">M-7 — Null checks على <code>String</code> غير قابلة للـ null</span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<pre>if (isSocialPressed == true && passengerId != null && rideId != null) {
|
||
// ^^^^^^^^^^^ دائماً non-null</pre>
|
||
<p>لو <code>passengerId == ''</code> يمر الشرط ويُرسل بيانات فارغة للسيرفر. الفحص الصحيح: <code>passengerId.isNotEmpty && rideId.isNotEmpty</code>.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card broken">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">⚠️</span><span class="ch-title">N-2 — تأخير 1 ثانية Hardcoded في <code>argumentLoading()</code></span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<pre>await Future.delayed(const Duration(seconds: 1));
|
||
await getRoute(...);</pre>
|
||
<p>لا يزال موجوداً. Race condition يجب معالجته بـ <code>Completer</code> بدلاً من تخمين الوقت.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card broken">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">⚠️</span><span class="ch-title">N-4 — <code>step0</code> إلى <code>step4</code> بدلاً من <code>List<String></code></span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<pre>String step0 = ''; String step1 = ''; // ...
|
||
step0 = Get.arguments['step0']?.toString() ?? '';
|
||
step1 = Get.arguments['step1']?.toString() ?? '';</pre>
|
||
<p>لا تزال 5 متغيرات منفصلة. <code>List<String> steps = List.filled(5, '')</code> أوضح وأسهل في المعالجة.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STILL MINOR -->
|
||
<div class="section">
|
||
<div class="section-hdr"><span class="badge b-min">ℹ️ بسيطة</span> ملاحظات إضافية على هذه النسخة</div>
|
||
|
||
<div class="card minor">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">ℹ️</span><span class="ch-title"><code>_suggestOptimization()</code> لا تزال موجودة لكن لا يستدعيها أحد</span><span class="chev">▶</span></div>
|
||
<div class="cb"><p>بعد حذف <code>_performanceReadings</code> و <code>_analyzePerformance()</code>، بقيت <code>_suggestOptimization()</code> معزولة. إما أن تُستدعى من مكان ما أو تُحذف.</p></div>
|
||
</div>
|
||
|
||
<div class="card minor">
|
||
<div class="ch" onclick="t(this)"><span class="ch-icon">ℹ️</span><span class="ch-title">الاستيرادات المكررة لـ <code>dart:math</code> و <code>geolocator</code> لا تزال</span><span class="chev">▶</span></div>
|
||
<div class="cb">
|
||
<pre>import 'dart:math';
|
||
import 'dart:math' as math; // مكرر
|
||
import 'package:geolocator/geolocator.dart' as geo;
|
||
import 'package:geolocator/geolocator.dart'; // مكرر</pre>
|
||
<p>يُسبب تحذيرات من المحلل ويُشوّش قراءة الكود. احذف النسخة غير المعرّفة.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<script>
|
||
function t(header) {
|
||
const b = header.nextElementSibling;
|
||
const ch = header.querySelector('.chev');
|
||
const o = b.classList.contains('open');
|
||
b.classList.toggle('open', !o);
|
||
if (ch) ch.classList.toggle('open', !o);
|
||
}
|
||
</script>
|