اذهب إلى المحتوى

Entesar Khaled

الأعضاء
  • المساهمات

    1516
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    213

كل منشورات العضو Entesar Khaled

  1. ناقش ديميان Demian التحديات التي تواجهها المواقع متعددة الأصول multiple origins عند محاولة إنشاء تطبيق ويب تقدمي واحد يشملها جميعًا، وذلك في مقال تطبيق الويب التقدمي على أصول متعددة. يعد موقع التجارة الإلكترونية الافتراضي www.example.com مثالًا على موقع متعدد الأصول، إذا كان: الصفحة الرئيسية مُستضافة على العنوان https://www.example.com. صفحات الفئات مستضافة على عنوان مختلف مثلًا: https://category.example.com. صفحات تفاصيل المنتج مستضافة على عنوان ثالث مختلف، مثلًا: https://product.example.com. تُقيِّد سياسة الأصل المشترك same-origin مشاركة عمّال الخدمة service workers وذاكرة التخزين المؤقت cache، والأذونات permessions، عبر الأصول المختلفة، لذا نوصي بشدة بتجنب هذا النوع من إعداد المواقع (سياسة الأصل المشترك)، ونوصي من لديهم مواقع مبنية بهذه الطريقة بالفعل، بالنظر في الانتقال إلى معمارية الموقع ذات الأصل الواحد single origin إذا أمكن. تجنب استخدام أصول مختلفة لأقسام نفس الموقع عند بنائك تطبيق PWA واحد يشملها جميعًا. سنعتني في هذا المقال بالحالة المعاكسة، بدلًا من توفير تطبيق ويب تقدمي واحد مجزأ على أصول مختلفة، فإننا سنهتم بحالة الشركات التي ترغب بتوفير تطبيقات ويب تقدمية PWA متعددة، تعمل على نفس اسم النطاق، وذلك لإعلام مستخدميها أن تطبيقات الويب التقدمية هذه تنتمي لنفس الشركة أو الخدمة. لكن قبل البدء سنراجع أهم المفاهيم التقنية التي ستلزمنا، وذلك بالاعتماد على رابط موقع التجارة الالكترونية الافتراضي https://www.example.com: مصطلحات تقنية النطاق Domain: هو أي تسلسل من العناوين الموجودة في نظام أسماء النطاقات DNS، مثلًا يعد com وexample.com نطاقان، لكن الفرق هنا أن com هو النطاق الرئيسي بينما تُعد example.com أو مثلًا test.com وblog.com نطاقات فرعية sub domains من النطاق الرئيسي com، أيضًا يعد كلًا من web.example.com وapp.example.com مثلًا وغيرها، نطاقات فرعية sub domains من نطاقها الرئيسي www.example.com الذي يُعد كما قلنا نطاق فرعي بالنسبة للنطاق الرئيسي العام com وهلم جرًا. اسم المضيف Host name: هو اسم مخصص لأي جهاز متصل بشبكة، يُحلّل إلى (ويقابل) عنوان IP الجهاز في نظام أسماء النطاقات DNS، مثلًا www.example.com هو اسم مضيف. الأصل Origin: هو تجميعة من كلًا من البروتوكول، واسم المضيف، والمنفذ (اختياريًا)، مثلًا يعد العنوان https://www.example.com:443 أصل. إنشاء علاقة بين تطبيقات الويب التقدمية PWA قد ترغب في بعض الحالات بإنشاء تطبيقات مستقلة، رغم أنك لا تزال مقررًا أنها متعلقة ببعضها وتنتمي لنفس المؤسسة أو العلامة التجارية brand. يُعد إعادة استخدام نفس اسم النطاق لجميع هذه المواقع طريقة جيدة لتأسيس علاقة قوية بينها، مثلًا: يريد موقع التجارة الإلكترونية https://www.example.com إنشاء تجربة موقع مستقلة تسمح للبائعين بإدارة مخزونهم، مع التأكيد لهم أن هذا الموقع بتجربته المستقلة ينتمي إلى موقع التجارة الرئيسي الذي يشتري المستخدمون منه المنتجات. تريد شركة إخبارية إنشاء تطبيق محدد لحدث رياضي عظيم، وذلك للسماح للمستخدمين بتلقي إحصائيات حول مسابقاتهم المفضلة عبر الإشعارات، وتمكينهم من تثبيت تطبيق PWA هذا على أجهزتهم بالتالي مشاهدة المحتوى الرياضي بسهولة، هذا مع التأكيد للمستخدمين أن التطبيق الرياضي يتبع لتلك الشركة الإخبارية. تريد إحدى الشركات إنشاء تطبيقات منفصلة لكلًا من الدردشة chat ورسائل البريد mail والتقويم calender وتريد أن تعمل هذه التطبيقات بشكل منفصل لكن تكون مرتبطة باسم الشركة. تريد الشركة المالكة لموقع example.com توفير ثلاثة تطبيقات PWA مستقلة، باستخدام نفس اسم المجال لتأسيس علاقة قوية بينها. استخدام أصول منفصلة الطريقة الموصى بها في مثل هذه الحالات، هي بناء كل تطبيق ذي هدف خاص على أصل origin خاص. إذا كنت تريد استخدام نفس اسم النطاق لجميع تطبيقات PWA المرتبطة ببعضها، فيمكنك القيام بذلك باستخدام النطاقات الفرعية sub domains. مثلًا يمكن لشركة تقدم خدمات إنترنت متعددة استضافة تطبيق بريد على الأصل https://mail.example.com وتطبيق تقويم على الأصل https://calendar.example.com، وتقديم الخدمة الرئيسية لأعمالها على الأصل https://www.example.com. وهناك مثال آخر لاستخدام النطاقات الفرعية للحصول على أصول مختلفة، وهو موقع رياضي يريد إنشاء تطبيق مستقل مخصص تمامًا لحدث رياضي مهم، مثل بطولة كرة القدم، وذلك على https://footballcup.example.com، يمكن للمستخدمين تثبيت موقع البطولة هذا على أجهزتهم واستخدامه بشكل مستقل عن موقع الرياضة الرئيسي المستضاف على https://www.example.com. قد تكون هذه الطريقة مفيدة أيضًا للأنظمة التي تتيح للعملاء إنشاء تطبيقاتهم المستقلة بهم تحت علامة الشركة التجارية، كأن يتيح أحد تطبيقات الويب للتجار إنشاء تطبيقاتهم الخاصة على نطاقات فرعية بأسمائهم مثل https://ahmad.example.com وhttps://ali.example.com وغيرها. يضمن استخدام أصول مختلفة العزل بين التطبيقات، أي يمكن لكل تطبيق من تطبيقات PWA المتعلقة ببعضها، إدارة ميزات المتصفح المختلفة بشكل مستقل، بما في ذلك: قابلية التثبيت Installability: حيث يوجد لكل تطبيق منها ملف بيان موارد Manifest، يوفر تجربة التطبيق الخاصة القابلة للتثبيت. التخزين Storage: يوجد لكل تطبيق منها ذاكرة تخزين مؤقتة cache، وميزات تخزين محلي local storage، وجميع أشكال التخزين المحلي للجهاز device-local بشكل أساسي. عمال الخدمة Service Workers: يوجد لكل تطبيق منها عامل خدمة مسؤول عن نطاقات التطبيق المسجلة فيه. الأذونات Permissions: يعطَى لكل تطبيق من تطبيقات PWA المرتبطة ببعضها عبر النطاقات الفرعية والمختلفة في الأصل، أذونات بشكل منفصل عن غيره، وهذا يفيد المستخدمين في معرفة الخدمة التي يمنحونها أذوناتهم بالضبط، كما تُنسب الإشعارات بدقة لكل تطبيق. يُوصى باتباع هذه الدرجة من العزلة (الأصول المختلفة) عند بناء عدة تطبيقات ويب تقدمية PWA مستقلة Independent. إذا أرادت تطبيقات النطاقات الفرعية مشاركة بياناتها المحلية مع بعضها البعض، فبإمكانها القيام بذلك عبر ملفات تعريف الارتباط cookies، أو بطريقة متقدمة أكثر، يمكنها مزامنة التخزين بواسطة الخادم. يعد بناء تطبيقات ويب تقدمية PWA مختلفة على نطاقات منفردة باستخدام النطاقات الفرعية ممارسة جيدة. استخدام نفس الأصل الطريقة الثانية هي بناء تطبيقات PWA المتعلقة ببعضها، على نفس الأصل same origin، ويتضمن ذلك السيناريوهات التالية: مسارات غير متراكبة أي تُستضاف تطبيقات PWA المتعلقة ببعضها على نفس الأصل، لكن مع مسارات paths غير متراكبة non-overlapping، مثلًا: https://example.com/app1 https://example.com/app2 المسارات المتراكبة أو المتداخلة أي تستضاف تطبيقات الويب التقديمة PWA المتعددة على نفس الأصل، لكن أحد نطاقاتها متداخل nested داخل الآخر، مثلًا: https://example.com (التطبيق الخارجي) https://example.com/app (التطبيق الداخلي) يسمح لك كلًا من الواجهة البرمجية Service Worker وتنسيق ملف بيان الموارد manifest بتطبيق الطريقتين السابقتين، وذلك بتحديد النطاقات فرعية أو رئيسية بناء على مستوى المسار path-level، لكن في كلتا الحالتين، يؤدي استخدام نفس الأصل إلى العديد من التحديات والمحدوديّات، النابعة من حقيقة أن المتصفح لن يعتبرها تطبيقات متمايزة، بالتالي لا يُنصح بهذا الأسلوب. سنحلِّل تلك التحديات بالتفصيل في الفقرة القادمة، وسنذكر ما الذي يمكن فعله إذا لم يكن من الملائم استخدام أصول منفصلة. لا يُنصح باستخدام المسارات المتداخلة وغير المتداخلة لاستضافة تطبيقين PWA مستقلين على نفس الأصل. التحديات التي تواجه تطبيقات PWA على نفس الأصل فيما يلي بعضًا من المشكلات العملية التي تواجهنا عند استخدام المسارات المتراكبة وغير المتراكبة لنفس الأصل: التخزين Storage: تُشارَك ملفات تعريف الارتباط cookies وبيانات التخزين المحلي local storage وجميع أشكال التخزين المحلي للجهاز device-local بين التطبيقات ذات نفس الأصل origin. لهذا، فإذا قرر المستخدم مسح البيانات المحلية لأحد تطبيقات PWA المتعلقة ببعضها، فستُمسح بيانات جميع التطبيقات المشتركة بنفس الأصل مع ذلك التطبيق، فلا توجد طريقة لمسح بيانات تطبيق واحد فقط منها بعينه. تحث بعض المتصفحات مثل Chrome وغيره المستخدمين دومًا على مسح البيانات المحلية عند إلغاء تثبيت أحد التطبيقات، وهذا سيؤثر على بيانات التطبيقات الأخرى المشتركة معه في نفس الأصل، والتحدي الآخر الممكن أن تواجهه بالنسبة للتخزين في التطبيقات المشتركة بنفس الأصل، هو أن هذه التطبيقات بطبعها تشارك بعضها حصة التخزين الخاصة بها، بالتالي إذا كان أيًا منها يشغَل مساحة تخزينية كبيرة جدًا، فسيتأثر الآخر سلبًا. الأذونات Permissions: إذا منح المستخدم إذنًا لأحد تطبيقات PWA المتعلقة ببعضها، فسيُمنح نفس الإذن لجميع التطبيقات المشتركة في نفس الأصل مع ذلك التطبيق، فالأذونات مرتبطة بالأصل أيضًا كما التخزين. قد يبدو هذا ميزة، بحيث لا تضطر جميع تلك التطبيقات إلى طلب نفس الإذن، أي طلب نفس الإذن عدة مرات، لكن التحدي هنا يكمن في حظر المستخدم إذنًا لأحد التطبيقات، فوقتها تُمنع التطبيقات الأخرى المشتركة في الأصل من طلب هذا الإذن، بالتالي لا تستطيع جميعها استخدام ميزة التطبيق التي تتطلب ذلك الإذن. إعدادات المستخدم User settings: تعيّن إعدادات المستخدم لتطبيقات الويب أيضًا بالإعتماد على الأصل، مثلًا، إذا كان لتطبيقان مشتركان في الأصل أحجام خطوط مختلفة، ويريد المستخدم ضبط تكبير حجم خط أحدهما فقط، فسيَكبُر حجم خط التطبيقات الأخرى المشتركة معه في نفس الأصل. تجعلنا التحديات السابقة نتجنب هذا النهج (الأصل المشترك). لكن، إذا كنت لا تستطيع استخدام أصل منفصل (مثل نطاق فرعي) لكل تطبيق من تطبيقات PWA المتعلقة ببعضها، فيمكنك ترشيح خيار المسارات غير المتداخلة بدلًا من خيار المسارات المتراكبة أو المتداخلة ضمن خيارَي استخدام نفس الأصل في الأعلى. تحديات إضافية للمسارات المتراكبة أو المتداخلة المشكلة الإضافية المتعلقة بنهج المسارات المتراكبة أو المتداخلة (حيث https://example.com هو التطبيق الخارجي والمسار https://example.com/app هو للتطبيق الداخلي)، هو أن جميع عناوين URL في التطبيق الداخلي ستُعتبر جزءًا من كلا التطبيقين الداخلي والخارجي. وهذا يسبب عمليًا المشكلات التالية: إمكانية التثبيت Installation: إذا زار المستخدم التطبيق الداخلي (مثلًا في متصفح ويب)، وكان التطبيق الخارجي مثبتًا بالفعل في جهاز المستخدم، فلن يعرض المتصفح لافتات لتثبيت التطبيق الداخلي. وذلك لأن المتصفح عندما يتحقق من إنتماء الصفحة الحالية إلى تطبيق مثبت بالفعل أم لا، فإنه سيعتبر التطبيق الداخلي مثبت على الجهاز كون التطبيق الخارجي مثبت بالفعل، ويمكن أن تُحل هذه المشكلة بتثبيت التطبيق الداخلي يدويًا (عبر خيار "إنشاء اختصار" في المتصفح)، أو تثبيت التطبيق الداخلي أولاً، قبل التطبيق الخارجي. الواجهتان البرمجيتان Notification و Padging: إذا تم تثبيت التطبيق الخارجي دون الداخلي، فستُنسب الإشعارات والشارات القادمة من التطبيق الداخلي عن طريق الخطأ إلى التطبيق الخارجي، إذ تنسب الإشعارات والشارات بدقة لكل تطبيق في حال ثبِّت كليها على جهاز المستخدم. التقاط الرابط Link Capturing: إذا تم تثبيت التطبيق الخارجي دون الداخلي، فلن يتم التقاط روابط التطبيق الداخلي المرتبطة بالتطبيق الخارجي، أي بدلًا من أن يُفتح الرابط الداخلي في نفس التبويبة ضمن نطاق تصفح التطبيق الخارجي، فإنه سيُفتح في تبويبة جديدة لأن المتصفح سعتبره تطبيقًا غريبًا (تطبيق مختلف غير مرتبط به). لكن إذا أضيفت تطبيقات PWA هذه إلى المتجر على نظامَي Chrome OS وAndroid، فسيلتقط التطبيق الخارجي تلك الروابط، وأيضًا إذا تم تثبيت التطبيق الداخلي، فسيظل نظام التشغيل يوفر للمستخدم خيار فتح تلك الروابط المتعلقة بالتطبيق الداخلي في التطبيق الخارجي. الخاتمة تطرقنا في هذا المقال إلى الطرق المختلفة الممكن للمطورين من خلالها إنشاء العديد من تطبيقات الويب التقدمية المتعلقة ببعضها على نفس النطاق (وهذا مختلف عن استخدام نطاقات وأصول مختلفة لأقسام موقع واحد كبير). باختصار، نوصي بشدة باستخدام أصل مختلف (مثلًا باستخدام النطاقات الفرعية) لاستضافة تطبيقات PWA المستقلة، إذ يؤدي استضافتها على نفس الأصل إلى عدة تحديات، كون المتصفح لن يعتبرها تطبيقات متمايزة، إذن الخلاصة أن هناك ثلاث حالات لاستضافة تطبيقات PWA المتعلقة ببعضها على نفس النطاق: على أصول منفصلة: موصى بها. على نفس الأصل بمسارات غير متداخلة: ليست مستحسنة. على نفس الأصل بمسارات متراكبة overlapping أو متداخلة nested: لا ينصح بها بشدة. إذا لم يكن بإمكانك استخدام أصول مختلفة، فاستخدم مسارات غير متداخلة، مثلًا https://example.com/app1 وhttps://example.com/app2، بدلًا من المسارات المتراكبة أو المتداخلة مثل https: //example.com (للتطبيق الخارجي) وعنوان https://example.com/app (للتطبيق الداخلي). ترجمة -وبتصرف- للمقال Building multiple Progressive Web Apps on the same domain من موقع web.dev. اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA استقبال تطبيقات الويب التقدمية البيانات المشاركة عبر الواجهة البرمجية Web Share Target جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA
  2. يقدم هذا المقال إرشادات تصميم حول كيفية إنشاء تجربة مستخدم رائعة لتطبيقات الويب التقدمية PWAs على كلًا من الشبكات البطيئة وغير المتصلة. يمكن أن تتأثر جودة اتصال الشبكة بشكل عام بعدة عوامل مثل: تغطية ضعيفة من المزود. ظروف طقس شديدة. انقطاع التيار الكهربائي. الدخول في مناطق ميتة الاتصال دائمًا dead zones مثل البنايات ذات الجدران المانعة لاتصالات الشبكة. الدخول في مناطق ميتة الاتصال مؤقتًا مثل السفر في قطار وعبور نفق. اتصالات الإنترنت المقيدة زمنيًا time-boxed مثل الاتصالات الموجودة في المطارات أو الفنادق. الممارسات الثقافية التي تتطلب وصولًا محدودًا أو معدومًا للإنترنت في أوقات أو أيام محددة. هدفك هو تقديم تجربة مستخدم جيدة في وضع عدم الاتصال تقلل من تأثير التغيُّر في حالة التطبيق عندما يستعيد المستخدم اتصاله بالشبكة. حدد ما تريد إظهاره للمستخدمين في حالة ضعف الاتصال بالشبكة اطرح على نفسك أولًا السؤال التالي: كيف سيبدو نجاح وفشل اتصال الشبكة في تطبيقك؟ الاتصال الناجح هو التجربة العادية لتطبيقك بوجود اتصال بالشبكة، بينما يُمثِّل فشل الاتصال كيفية تصرف تطبيقك في حالة عدم الاتصال بالشبكة أو الاتصال البطيئ بها. عند التفكير في نجاح أو فشل اتصال الشبكة، عليك أن تسأل نفسك أسئلة حول تجربة المستخدم UX المهمة هذه: ما هي المدة التي تنتظرها لتحديد نجاح أو فشل الاتصال؟ ماذا يمكنك أن تفعل أثناء تحديد نجاح أو فشل الاتصال؟ ماذا تفعل في حال فشل الاتصال؟ كيف تبلِّغ المستخدم بما ورد أعلاه (أنه في مرحلة انتظار تحديد حالة التطبيق، أو أن اتصاله فشل أو أن اتصالة استُعيد)؟ بلغ المستخدمين عن حالتهم الحالية وعن تغيُّر حالة التطبيق أخبر المستخدم بالإجراءات التي لا يزال بإمكانه القيام بها عندما يكون في حالة فشل الاتصال، وأيضًا أخبره بحالة التطبيق الحالية، مثلًا ممكن أن يقول الإشعار: بلّغ المستخدم بوضوح عند حدوث تغيُّر في حالة التطبيق في أسرع وقت ممكن. استخدام تطبيق Google I/O مربع إشعار لإخبار المستخدم عندما يكون في حالة عدم اتصال بالشبكة، إذ يقول الإشعار "أنت بلا اتصال، تغييراتك في My Schedule ستُحفظ لوقت لاحق". بلِّغ المستخدمين عندما يتحسن اتصال الشبكة لديهم أو يعود يعتمد كيفية إبلاغك المستخدم بتحسُّن اتصال الشبكة على تطبيقك، فالتطبيقات مثل تطبيق حالة الطقس الذي يعطي الأولوية للمعلومات الآنية، يجب أن يُحدَّث تلقائيًا عند استعادة الاتصال، وإشعار المستخدم في مثل هكذا تطبيقات يجب أن يتم في أقرب وقت ممكن. يوصَى بإِعلام المستخدم أنه قد تم تحديث تطبيق الويب في الخلفية، وذلك باستخدام تلميح مرئي عبر تنبيه أو أي وسيلة إشعارات تؤدي الغرض. أحد الأمثلة على استخدام هذه التجربة هو متصفح Chrome Status التي تُظهر ملاحظة للمستخدم عند تحديث التطبيق في الخلفية. تحتاج بعض التطبيقات مثل تطبيق الطقس إلى تحديث تلقائي، لأن البيانات القديمة ليست مفيدة مع تقلبات الطقس المستمرة. يتيح تطبيق ana (أنا) للمستخدم معرفة وقت تحديث المحتوى عبر مربع إشعار في أعلى الشاشة. يمكنك تخصيص مساحة ثابتة تُظهر من خلالها آخر مرة تم فيها تحديث التطبيق، سيكون هذا مفيدًا لتطبيق تحويل العملات مثلًا. تطبيق Material mony يُظهر آخر تحديث للتطبيق. وينبّه المستخدم عند تحديث التطبيق. يمكن أن تعرض تطبيقات مثل تطبيقات الأخبار إشعارًا بسيطًا بالنقر للتحديث، لإعلام المستخدم بالمحتوى الجديد، فقد يتسبب التحديث التلقائي في فقدان المستخدمين لمكان تصفحهم. صحيفة الانترنت Tailpiece تُنزّل آخر الأخبار تلقائيًا. وتسمح للمستخدمين بالتحديث يدويًا حتى لا يفقدوا مكان تصفحهم في المقال. تحديث واجهة المستخدم UI لتعكس الحالة السياقية الحالية لكل بِت bit من واجهة المستخدم سياقُه ووظائفه الخاصة التي تتغير بناءً على كونها تتطلب اتصالًا ناجحًا بالشبكة أو لا، أحد الأمثلة على ذلك هو مواقع التجارة الإلكترونية التي يمكن تصفحها في وضع عدم الاتصال، فوقتها يُعطَّل زر الشراء إلى حين استعادة الاتصال. يمكن أن تتضمن الأشكال الأخرى للحالات السياقية، البيانات، مثلًا يتيح التطبيق المالي Robinhood للمستخدمين شراء الأسهم، ويَستخدِم الألوان والرسومات لإعلام المستخدم عندما يكون السوق مفتوحًا، وعند إغلاق السوق تتحول الواجهة بالكامل إلى اللون الأبيض ثم تتحول إلى اللون الرمادي، أيضًا عندما تزيد قيمة السهم أو تنقص، يتحول كل عنصر واجهة مستخدم للسهم إلى اللون الأخضر أو الأحمر حسب حالته. توضيح للمستخدم نموذج العرض الخاص بوضع عدم الاتصال يعد وضع عدم الاتصال بالشبكة نموذج عرض جديد لجميع المستخدمين، كون نموذج العرض في الوضع المتصل بالشبكة هو الأساسي والمعروض دائمًا لديهم، لذا تحتاج إلى تعليم المستخدمين التغيّرات التي ستحدث عندما لا يكون لديهم اتصال بالشبكة، أبلغهم بالمكان الذي يتم فيه حفظ البيانات الكبيرة وامنحهم الإعدادات لتغيير سلوك التخزين الافتراضي، وتأكد من استخدام عدة مكونات لتصميم واجهة المستخدم مثل اللغة الغنية بالمعلومات والرموز والإشعارات واللون والصور لتوصيل نموذج العرض بدلاً من الاعتماد على خيار تصميم واحد لذلك. تقديم تجربة افتراضية لوضع عدم الاتصال إذا كان تطبيقك لا يتطلب بيانات كثيرة في عمله، فخزِّن البيانات التي يعتمد تطبيقك عليها مؤقتًا افتراضيًا، إذ إن المستخدمين يمكن أن يشعروا بالإحباط أكثر فأكثر إذا لم يكونوا قادرين على الوصول إلى بياناتهم إلا في وضع الاتصال بالشبكة. حاول أن تجعل التجربة مستقرة قدر الإمكان، فالاتصال غير المستقر سيجعل تطبيقك يبدو أنه غير جدير بالثقة، على عكس التطبيقات التي تقلل من تأثير فشل الاتصال بالشبكة التي تبدو خلَّابة بالنسبة للمستخدم. يمكن لمواقع الأخبار الاستفادة من التنزيل التلقائي والحفظ التلقائي لآخر الأخبار بحيث يُتاح للمستخدم قراءة أخبار اليوم دون الاتصال بالشبكة، ولو على الأقل تنزيل النص بدون صور الخبر، أيضًا يمكن لتلك المواقع التكيُّف مع سلوك المستخدم، مثلًا إذا كان قسم الرياضة هو ما يشاهده المستخدم عادةً، تجعل تلك المواقع تنزيل أخبار هذا القسم هو الأولوية. إذا كان الجهاز غير متصل بالشبكة، فسيُشعر Tailpiece المستخدم بذلك. ويُعرِّف المستخدم أنه لا يزال بإمكانه استخدام التطبيق جزئيًا على الأقل. عندما يتعلق الأمر بالإبلاغ عن حالة أحد التطبيقات، فإن قول "الشبكة معطلة" يرسل رسالة مفادها أن شبكة التطبيق تواجه مشكلات، بينما توضح عبارة "أنت غير متصل" للمستخدم أن المشكلة من طرفه. إبلاغ المستخدم عندما يكون التطبيق جاهزًا للاستخدام دون اتصال بالشبكة يجب أن توضِّح للمستخدمين عند تحميلهم تطبيقك لأول مرة، ما إذا كان بالإمكان استخدام التطبيق في وضع عدم الاتصال أم لا، يمكنك القيام بذلك باستخدام وِدجِت widget يوفر ملاحظات موجزة حول عملية ما، من خلال رسالة أسفل الشاشة مثلًا عند مزامنة قسم أو تنزيل ملف. فكر مرة أخرى في اللغة التي تستخدمها وتأكد من أنها مناسبة لجمهورك، إذ يُسيئ مثلًا الجمهور غير التقني عمومًا فهم مصطلح "غير متصل"، لذا استخدم لغة قائمة على الإجراء action-based تساعد جمهورك على فهمها والاندماج معها. يظهر تطبيق Google I/O إشعارًا مفاده: اكتمل التخزين المؤقت، وستعمل الزيارات المستقبلية في وضع عدم الاتصال. يتضمن موقع Chrome Platform Status معلومات حول المساحة المشغولة بالتخزين المؤقت. اجعل "التخزين في وضع عدم الاتصال" جزءًا واضحًا من واجهة التطبيقات كثيفة البيانات إذا كان أحد تطبيقاتك يستخدم بيانات كثيرة، فتأكد من وجود مفتاح تبديل switch لاضافة عنصر ما للاستخدام دون اتصال save for offline بدلاً من التنزيل التلقائي له، وتأكد أن ذلك المفتاح غير محجوب من عناصر واجهة المستخدم الأخرى وأنه واضح للمستخدم. أحد الأمثلة على ذلك هو مشغل الموسيقى الذي يحتاج ملفات ذات بيانات كبيرة، قد يرغب مستخدم هذا التطبيق في استخدام المشغل دون اتصال بالشبكة، فهنا تنزيل الموسيقى لاستخدامها لاحقًا يتطلب تخطيط مسبق من المستخدم كون الملفات ستحجز مساحة تخزين كبيرة، ووقتها يكون اطلاع المستخدم حول هذا الأمر لازمًا أثناء تهيئته للتطبيق. توضيح ما هو متاح حاليا كن واضحًا فيما يتعلق بالخيار الذي تقدمه، قد تحتاج إلى إظهار نافذة معينة أو إعداد ما يعرض مثلًا "المكتبة غير المتصلة بالشبكة" offline library أو فهرس المحتوى content index، الذي يمكن للمستخدم من خلاله رؤية ما خزّنه في هاتفه بسهولة. أيضًا تأكد أن الإعدادات موجزة وكن واضحًا بشأن مكان تخزين البيانات ومن يمكنه الوصول إليها. إظهار التكلفة الفعلية للإجراء يساوي العديد من المستخدمين قدرة التطبيق في وضع عدم الاتصال بقدرته على "التنزيل"، وغالبًا ما يستغل المستخدمون في البلدان ذات التغطية الضعيفة وقت الاتصال بالشبكة لحفظ محتوى التطبيق للاستخدام دون اتصال. قد يتجنب مستخدمو الباقات المحدودة ذات الكلفة المرتفعة للاتصال بالإنترنت تنزيل ملفات كبيرة خوفًا من زيادة التكلفة، لذا يلزمك أيضًا عرض تكلفة تحميل الملفات أو تكلفة أداء المهام بشكل عام في تطبيقك، مثلًا عندما يكتشف تطبيق الموسيقى المذكور كمثال في فقرة سابقة أن المستخدم في خطة البيانات المدفوعة data plan، يعرِض حجم الملف المُشغَّل، ويتيح للمستخدم رؤية تكلفة تحميله. ساعد في منع التجارب الساخرة غالبًا ما ينجز المستخدمون تجربة ما ساخرة في التطبيق دون أن يدركوا ذلك، مثلًا قبل انتشار تطبيقات مشاركة الملفات المعتمدة على السحابة cloud-based، كان من الشائع أن يحفظ المستخدمون الملفات الكبيرة ويرفقوها برسائل البريد الإلكتروني ليواصلوا تحريرها من جهازٍ مختلف. من المهم عدم الإنجرار إلى تجربتهم الساخرة تلك، بل النظر إلى ما يحاولون تحقيقه منها، بمعنى، بدلاً من التفكير في كيفية جعل إرفاق ملف كبير أسهل، حُل مشكلة مشاركة الملفات الكبيرة عبر أجهزة متعددة باستخدام التخزين السحابي. اجعل العمليات قابلة للنقل من جهاز لآخر عند إنشاء تطبيقات لشبكات غير مستقرة الاتصال، حاول إجراء المزامنة بمجرد أن يتحسن الاتصال حتى تكون تجربة المستخدم قابلة للنقل من جهاز لآخر، مثلًا تخيل أن تطبيق سفر ما يفقد اتصال الشبكة في منتصف مهمة الحجز، وعند استعادة الاتصال، تتيح عملية تزامُن بيانات التطبيق مع حسابات المستخدمين لهم مواصلة الحجز على حواسيبهم المكتبية. ينزعج المستخدمين للغاية من التطبيقات غير القادرة على نقل تجاربهم بين الأجهزة. أيضًا بلّغ المستخدمين بالحالة الحالية لبياناتهم، مثلًا يمكنك إظهار ما إذا تم مزامنة التطبيق أم لا، أعلمهم بكل المستجدات في حالة التطبيق لكن حاول ألا تثقل عليهم بالرسائل والإشعارات. إنشاء تجارب تصميم متكاملة تجارب التصميم المتكاملة inclusive تشمل تصميمًا ذا مغزى، ولغة بسيطة، وأيقونات معيارية، وتمثيلات هادفة، ستساعد المستخدم لإكمال مهمته في التطبيق بسلاسة وبساطة دون أي إعاقة. استخدم لغة بسيطة وموجزة تجربة المستخدم الجيدة لا تتعلق فقط بواجهة مصممة جيدًا، بل تتضمن المسار الذي يسلكه المستخدم بالإضافة إلى الألفاظ المستخدمة في التطبيق. تجنب المصطلحات التقنية عند شرح حالة التطبيق أو مكونات واجهة المستخدم، وضع في اعتبارك أن عبارة "التطبيق غير متصل" قد لا تنقل للمستخدم الحالة الحالية للتطبيق. تجنب المصطلحات غير المفهومة للمستخدمين غير التقنيين مثل: "عامل الخدمة". استخدم اللغة الواضحة التي تصف الإجراء مثل: "تنزيل". استخدم طرق تصميم متعددة لتسهيل تجربة المستخدم استخدم اللغة واللون والمكونات المرئية لإظهار تغيُّر حالة التطبيق أو وضعه الحالي، فقد لا يلاحظ المستخدم حالة التطبيق إذا عُرضت باستخدام اللون فقط. استخدام واجهة مستخدم رمادية لتمثيل وضع عدم الاتصال هو بديهة المصممين، لكن في الويب، يمكن أن يُفهم من هذه الواجهة أن العنصر محمّل، كما أن عناصر واجهة المستخدم الرمادية مثل عناصر الإدخال في النماذج تعني أيضًا أن العنصر معطل، بالتالي استخدام اللون فقط لتصوير الحالة يمكن أن يسبب لبسًا في فهمها. لمنع هذا اللبس، عبِّر عن حالات المستخدم المختلفة بعدة طرق، مثلًا باستخدام كلًا من الألوان والتسميات والأيقونات ومكونات واجهة المستخدم الواضحة وغيرها. لا تستخدم اللون كوسيلة وحيدة لوصف الحالة، كما في الصورة التالية مستخدَم اللون الأحمر فقط للدلالة على تجاوز النص المكتوب للعدد المسموح به من الحروف. بل استخدم مزيجًا من عناصر التصميم لتوصيل المعنى، مثل اللغة الواضحة بالإضافة للون، لاحظ عند استخدم التعبير اللفظي الموضح للخطأ "تجاوزْت الحد الأقصى لعدد الأحرف" مع اللون المعبّر لإزالة اللبس بشأن سبب الخطأ. استخدم أيقونات بليغة تأكد من إيصال المعلومات للمستخدم بشكل صحيح وذلك باستخدام تسميات نصية ذات معنى مع الأيقونات، إذ يمكن أن يسبب استخدام الأيقونات لوحدها لبسًا. قد يسيء المستخدمون فهم بعض الأيقونة المستخدمة في التطبيقات، مثلًا يعد استخدام أيقونة القرص المرن لحفظ البيانات أمرًا منطقيًا بالنسبة للجيل الأكبر سنًا، لكن قد تسبب هذه الأيقونة لبسًا للمستخدمين الأصغر سنًا الذين لم يروا قرصًا مرنًا في حياتهم، وبالمثل فإن أيقونة القائمة التي تشبه البرغر hamburger menu قد تشوِّش المستخدمين عند عرضها دون تسمية. أيقونة الحفظ دون اتصال التي على شكل التنزيل النموذجي -أو ربما إذا كان الإجراء يتضمن مزامنة- قد تكون على شكل أيقونة مزامنة، لذا عند تقديم أحد الأيقونات المتعلقة بوضع عدم الاتصال، حاول أن توفر معها تسمية نصية ووصف. قد تفسَّر بعض الإجراءات على أنها حفظ دون اتصال بدلاً من إظهار حالة الشبكة، لذا فكر في الإجراء الذي تحاول الإبلاغ عنه بدلاً من عرض أيقونات مجردة (ذات طابع عام) للمستخدم، كأن يكون شكل أيقونة حفظ البيانات أو تنزيلها قائمًا على الإجراء action-based أي يدل على طبيعة استخدامها بالضبط. يمكن أن يعني وضع عدم الاتصال عددًا من الأشياء اعتمادًا على السياق، مثل التنزيل، والتصدير، والتثبيت، وما إلى ذلك. لمزيد من المعرفة، راجع مجموعة أيقونات التصميم Material Design. استخدم عناصر نائبة في التخطيط ريثما يجهز المحتوى مخطط الهيكل skeleton layout للتطبيق هو عناصر نائبة توضع في هيكل التطبيق الرئيسي ريثما يُحمّل المحتوى الفعلي، يمكنك استخدامه لتوضّح للمستخدم أن محتوى الصفحة على وشك التحميل. ضع في اعتبارك أيضًا استخدام واجهة التحميل المسبق preloader UI، مع رسالة نصية تخبر المستخدم أنه يجري تحميل التطبيق الآن، يمكنك أيضًا تحريك محتوى ما أو وضع شريط حالة أو حلقات دوارة لإشعار المستخدم أن التطبيق حي وجاري تحميل محتواه، بالتالي إقناع المستخدم بعدم إعادة طلب عنوان التطبيق أو تحديثه. وضع عناصر نائبة في هيكل التطبيق الرئيسي مكان المحتوى ريثما تُحمّل مع عرض إشعار يخبر المستخدم بمعدل التنزيل. ثم وضع المحتوى مكان تلك العناصر النائبة متى ما انتهت عملية التحميل وأصبح المحتوى جاهزًا للعرض. لا تعيق المحتوى عندما يشغِّل المستخدم إجراء مثل إضافة عنصر جديد، فإن التطبيق يتصل بالخادم لمزامنة العنصر الجديد، وللدلالة على ذلك فإنه يعرض عنصر تدخُّلي للتحميل يغطي الشاشة بأكملها (غالبًا أيقونة التحميل الدائرية تتدخل وسط الصفحة وتحجب تنفيذ اي عملية ريثما ينتهي التحميل لتختفي بعدها). قد يعمل هذا بشكل جيد إذا كان لدى المستخدم اتصال شبكة ثابت، لكن إذا كان اتصال الشبكة غير مستقر، فلا يمكن للمستخدمين التخلص من عنصر التحميل الذي يغطي الشاشة أثناء تحميل بياناتهم، فكأن الواجهة تمنعهم من القيام بأي شيء في التطبيق وتعيق ظهور محتواه. هذه ليست تجربة جيدة، بل يجب أن يتجنب تطبيقك التعامل مع طلبات الشبكة التي تعيق ظهور في وضع الاتصال غير المستقر، والسماح بدلًا من ذلك للمستخدمين بالاستمرار في تصفح تطبيقك ومهام قائمة الانتظار التي ستُنفَّذ وتُزامَن بمجرد تحسُّن الاتصال. أظهر حالة الإجراءات من خلال تزويد المستخدمين برد فعل، مثلًا إذا كان أحد مستخدمي تطبيق مستنداتك يُحرِّر مستند عند انقطاع الاتصال، ففكر في تغيير تصميم رد الفعل بحيث يكون مختلفًا بشكل مُلاحَظ عما كان عليه قبل انقطاع الاتصال، لكن حافظ على إظهار أنه قد تم حفظ ملفه وستتم مزامنته عندما يُستعاد الاتصال بالشبكة، فذلك سيؤدي إلى إعلام المستخدم بمختلف الحالات المتاحة وطمأنته بأن قد تم تخزين مهمته أو إجراءه، كما لهذا فائدة إضافية تتمثل في زيادة ثقة المستخدم في استخدام تطبيقك. صمم لجميع طبقات المستخدمين تعد الأجهزة منخفضة الجودة في بعض المناطق أمرًا شائعًا، وكذلك اتصال الشبكة غير المستقر، الذي بالنسبة للعديد من المستخدمين، لا يمكنه تحمّل نقل البيانات، لذا تحتاج في مثل هكذا حالات إلى كسب ثقة المستخدم من خلال الشفافية والاقتصاد في التعامل مع البيانات، وذلك بأن تفكِّر في طرق لمساعدة مستخدمي الاتصالات الضعيفة وطرق لتبسيط الواجهة بهدف تسريع إنجاز المهام. حاول دائمًا سؤال المستخدمين قبل تنزيل محتوى ضخم من البيانات. اعرض خيارات النطاق الترددي المنخفض low bandwidth للمستخدمين ذوي اتصال الشبكة الضعيف، أي قدِّم أصولًا قليلةً للتطبيق إذا كان اتصال الشبكة بطيئ. أيضًا وفِّر ميزة للاختيار ما بين الأصول عالية الجودة و منخفضة الجودة. الخلاصة يعد إعلام المستخدم بحالة التطبيق وبياناته مفتاحًا لتجربة المستخدم غير المتصلة بالشبكة. حاول إنشاء ارتباطات بأشياء مألوفة، مثلًا اجعل تجربة مشاهدة البيانات دون اتصال نفس التجربة المألوفة التنزيل للاستخدام لاحقًا. تذكر هذه الإرشادات عند تصميم تجربة المستخدم للشبكات غير المستقرة: فكر في كيفية التصميم في حالة نجاح الاتصال بالشبكة وحالة فشل الاتصال وأيضًا عدم استقراره. إذا كانت تكلفة نقل أو تخزين البيانات مرتفعة في تطبيقك، يجب مراعاة إعلام المستخدم بذلك. بالنسبة لمعظم المستخدمين على مستوى العالم، فإن البيئة الفنية لاتصال الشبكة (مثلًا مكان المستخدم بالنسبة للموجِّه أو الرواتر) تكاد تكون متنقلة بحتًا. الأجهزة القديمة ذات الأداء المنخفض، بالإضافة لسعة تخزين وذاكرة وقدرة معالجة محدودة، وشاشات صغيرة بجودة قليلة، فتأكد أن يكون مراعاة الأداء على مثل هذه الأجهزة جزء من تصميمك. اسمح للمستخدمين بتصفح تطبيقك عندما يكونوا في وضع عدم اتصال. أعلِم المستخدمين بوضعهم الحالي وتَغيُّر حالتهم. حاول توفير وضع عدم الاتصال افتراضيًا إذا كان تطبيقك لا يحتاج بيانات كثيرة. إذا كان تطبيقك يعالج كمية كبيرة من البيانات، فأعلِم المستخدمين حول كيفية تنزيل البيانات للاستخدام دون اتصال. اجعل المستخدم قادر على التنقل بين الأجهزة مع المحافظة على نفس التجارب بينها لنفس التطبيق. استخدم كلًا من اللغة والأيقونات والتمثيل والكتابة واللون للتعبير عن الأفكار للمستخدم. قدم تشجيع وتطمين وردود أفعال لمساعدة المستخدم في فهم حالات التطبيق المختلفة. ترجمة -وبتصرف- للمقال Offline UX design guidelines من موقع web.dev. اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA مفهوم Service Worker وتأثيره في أداء وبنية مواقع وتطبيقات الويب مشاركة تطبيقات الويب التقدمية البيانات عبر الواجهة Web Share إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA
  3. يمكن لتطبيقات الويب استخدام نفس إمكانيات المشاركة التي توفرها التطبيقات المثبتة على نظام التشغيل platform-specific، وذلك باستخدام الواجهة البرمجية Web Share التي تعمل في الخلفية Web Share API، إذ تتيح هذه الواجهة لتطبيقات الويب إمكانية مشاركة الروابط والنصوص والملفات مع التطبيقات الأخرى المثبتة على الجهاز كما التطبيقات المخصوصة بالنظام (تطبيقات النظام الأصلية أو التطبيقات المثبتة في النظام). مشاركة البيانات في تطبيقات الويب ليس كل شيء، إذ يمكن أن تشاركها التطبيقات الأخرى المحتوى، بمعنى أن بإمكان تطبيقات الويب تلقي البيانات والروابط والنصوص والملفات من التطبيقات المخصوصة بالنظام أو تطبيقات الويب الأخرى. المفاهيم والاستخدامات تتيح الواجهة البرمجية Web Share مشاركة البيانات في تطبيقات الويب ضمن قدرات استخدامها ومحدودياتها، موضّح فيما يلي هذه القدرات كما مذكور أيضًا طريقة استخدام هذه الواجهة في مشاركة محتوى تطبيقات الويب. القدرات والمحدوديّات تمتلك الواجهة البرمجية Web Share القدرات والمحدوديّات التالية: يمكن استخدامها فقط في المواقع الآمنة، التي يُوصل إليها عبر بروتوكول HTTPS. يجب أن تُستدعى استجابةً لإجراء مستخدم مثل النقر click، إذ لا يمكن استدعائها من خلال معالج التحميل onload. يمكن بواسطتها مشاركة عناوين URL أو نصوص أو ملفات. أصبحت متاحة على متصفحات Safari و Android و Chrome OS و Chrome على Windows، اعتبارًا من يناير كانون الأول 2021، أما إتاحتها في Chrome على MacOS لا تزال قيد التطوير، اطلع على MDN للحصول على التفاصيل. مُنتقي وُجْهة المشاركة على مستوى النظام مع تطبيق ويب تقدمي PWA مثبت كخيار للمشاركة إليه. مشاركة الروابط والنصوص إن أردت مشاركة الروابط والنصوص عبر الواجهة البرمجية Web Share، فاستخدم الدالة ()share، وهي دالة تعتمد على الوعد promise مع كائن خصائص properties مطلوب، ولمنع المتصفح من رمي خطأ TypeError، إذ يجب أن يحتوي ذلك الكائن على واحدة على الأقل من الخاصيات التالية: title أوtext أوURL أوfiles، مثلًا يمكنك مشاركة نص بدون عنوان URL أو العكس، وإتاحة إمكانية مشاركة الخصائص الثلاثة يؤدي إلى زيادة مرونة حالات الاستخدام. تخيل أنه بعد تشغيل الشيفرة التالية، اختار المستخدم تطبيق بريد إلكتروني كوُجْهة مشاركة، فحينها يصبح المعامل title هو موضوع رسالة subject والمعامل text هو نص الرسالة والمعامل files يصبح هو مرفقات الرسالة. if (navigator.share) { navigator.share({ title: 'web.dev', text: 'Check out web.dev.', url: 'https://web.dev/', }) .then(() => console.log('Successful share')) .catch((error) => console.log('Error sharing', error)); } إذا حوى موقعك على عناوين URL متعددة لنفس المحتوى، فشارِك عنوان URL الأساسي للصفحة بدلاً من عنوان URL الحالي، أي بدلاً من مشاركة document.location.href؛ يمكنك إيجاد عنوان URL الأساسي في وسم <meta> ضمن <head> الصفحة ومشاركته، فهذا سيوفر تجربة أفضل للمستخدم، ليس فقط على صعيد تجنب عمليات إعادة التوجيه، ولكنه يضمن أيضًا أن عنوان URL المشارك يخدم تجربة المستخدم الصحيحة لعميل معين، مثلًا إذا شاركك صديقك عنوان URL من هاتفه المحمول وفتحته عبر حاسوبك المكتبي، فيجب أن ترى إصدار سطح المكتب من الموقع او التطبيق المُشارك: let url = document.location.href; const canonicalElement = document.querySelector('link[rel=canonical]'); if (canonicalElement !== null) { url = canonicalElement.href; } navigator.share({url}); مشاركة الملفات إذا أردت إتاحة ميزة مشاركة الملفات في تطبيقك، اختبر أولًا إمكانية المشاركة عبر استدعاء التابع ()navigator.canShare، ثم ضمِّن مجموعة من الملفات في استدعاء التابع ()navigator.share: if (navigator.canShare && navigator.canShare({ files: filesArray })) { navigator.share({ files: filesArray, title: 'Vacation Pictures', text: 'Photos from September 27 to October 14.', }) .then(() => console.log('Share was successful.')) .catch((error) => console.log('Sharing failed', error)); } else { console.log(`Your system doesn't support sharing files.`); } لاحظ أن المثال يعالج الكشف عن ميزة إمكانية المشاركة عبر التابع ()navigator.canShare بدلاً من التابع ()navigator.share. كائن البيانات الممرر للدالة ()canShare في هذا المثال يدعم خاصية files فقط، لكن يمكن مشاركة ملفات الصور والفيديو والصوت والنص. (راجع امتدادات الملفات المسموح بمشاركتها حاليًا في Chromium). دراسة حالة لبرنامج Santa Tracker يعد Santa Tracker مشروعًا مفتوح المصدر، وهو تطبيق ترفيهي سنوي تحت عنوان عيد الميلاد أطلقته Google، يتيح للمستخدمين اللعب ومشاهدة وتعلم الأنشطة الصغيرة التي تُضاف يوميًا من بداية ديسمبر كل عام، كما يتيح تعقب السانتا (بابا نويل) أثناء عشية عيد الميلاد. في عام 2016، استخدم فريق Santa Tracker الواجهة البرمجية Web Share على منصة Android، وكانت هذه الواجهة مناسبة تمامًا لتطبيقات الهاتف المحمول، على عكس الأعوام السابقة التي أزال الفريق فيها أزرار المشاركة من نسخة الهاتف المحمول لأن مساحة تلك الأزرار كانت كبيرة في الشاشة، ولم تكن قادرة على إثبات وجود العديد من التطبيقات لمشاركتها المحتوى. لكن باستخدام الواجهة البرمجية Web Share تمكن فريق Santa Tracker من تقديم زر مشاركة واحد ويؤدي الغرض، مما يوفر مساحة ثمينة في الشاشة. توجه إلى Santa Tracker لمشاهدة فعاليِّة مشاركة الويب. زر المشاركة في تطبيق Santa Tracker. دعم المتصفحات هناك فروقات دقيقة في دعم المتصفحات للواجهة البرمجية Web Share، ويوصَى باستخدام استراتيجية اكتشاف الميزات (كما هو موضح في نماذج الشيفرات السابقة) بدلاً من افتراض دعم دالة معينة. دعم متصفحَي Safari و Chrome استخدام الواجهة Web Share لمشاركة العناوين والنصوص وعناوين URL بداية عام 2021 بالإصدارات التالية: Safari الإصدار 12 أو أحدث على macOS و iOS. Chrome الإصدار 75 أو أحدث على Android، والإصدار 89 أو أحدث على Chrome OS و Windows. كما دعم المتصفحان استخدام الواجهة Web Share لمشاركة الملفات أيضًا، وذلك بالإصدارات التالية: Safari الإصدار أو أحدث على macOS وiOS. Chrome الإصدار 75 أو أحدث على Android، والإصدار 89 أو أحدث على Chrome OS و Windows. تتمتع معظم المتصفحات المبنية على متصفح Chromium، مثل Edge، بنفس الدعم لميزة المشاركة مثل الإصدار المقابل من Chrome. ترجمة -وبتصرف- للمقال Integrate with the OS sharing UI with the Web Share API من موقع web.dev اقرأ أيضًا استقبال تطبيقات الويب التقدمية البيانات المشاركة عبر الواجهة البرمجية Web Share Target ما هي تطبيقات الويب التقدمية PWA جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA
  4. يُشارك المحتوى مباشرةً في الأجهزة المحمولة وأجهزة سطح المكتب، بالنقر على زر "مشاركة" واختيار أحد التطبيقات المراد مشاركة البيانات معها. مثلًا قد ترغب بمشاركة مقال مثير للاهتمام، إما عبر إرساله كرسالة بريد إلكتروني إلى أصدقاءك أو نشره في مجتمعك على تويتر أو مشاركته مع أي تطبيق آخر متاح لك المشاركة إليه في جهازك. كان فقط بإمكان التطبيقات المثبتة على نظام التشغيل platform-specific التسجيل في نظام التشغيل لاستقبال البيانات المشاركة من التطبيقات المثبتة الأخرى، لكن باستخدام الواجهة البرمجية Web Share Target (وُجهة مشاركة الويب) التي تعمل في الخلفية Web share Target API، أصبح بإمكان تطبيقات الويب المثبتة على الجهاز أيضًا التسجيل مع نظام التشغيل كهدف مشاركة لاستقبال بيانات من التطبيقات الأخرى. إمكانية استقبال تطبيقات الويب البيانات المُشاركة يُعد مكمّلًا لإمكانية تطبيقات أخرى على مشاركة البيانات، يمكنك مراجعة مقال مشاركة البيانات عبر الواجهة Web Share في تطبيقات الويب التقدمية. تجريب الواجهة البرمجية Web Share Target إن أردت تجريب الواجهة البرمجة Web Share Target، اتبع الخطوات التالية: افتح العرض التوضيحي لهذه الواجهة Web Share Target Demo وذلك على متصفح Chrome، بالإصدار 76 فما فوق على نظام أندرويد، أو 89 فما فوق على سطح المكتب. انقر زر المشاركة في العرض التوضيحي. اختر أحد التطبيقات الظاهرة في مُنتقي جهات المشاركة كوجْهة للمشاركة. بعد إتمامك عملية المُشاركة، ستُشاهد النص المُشارَك في التطبيق الذي اخترته. منتقي وجهة المشاركة على مستوى النظام. تسجيل التطبيق كهدف مشاركة يجب أن يستوفي تطبيقك معايير Chrome الخاصة بقابلية تثبيت التطبيقات لتسجيله كوجهة مشاركة، أيضًا حتى يتمكن المستخدم من مشاركة محتوى ما إلى تطبيقك، فيجب أن يكون تطبيقك مثبتًا في شاشته الرئيسية، فهذا يمنع المواقع من إضافة نفسها عشوائيًا إلى منتقي وُجهات المشاركة Share Target Picker المتاح للمستخدم مشاركة المحتوى إليها. حدِّث ملف بيان موارد تطبيق الويب لتسجيل تطبيقك كهدف مشاركة، أضف الخاصية share_target إلى ملف بيانه manifest، فهي تخبر نظام التشغيل بتضمين تطبيقك كخيار في منتقي وُجْهات المشاركة، وبشكل عام ما تضيفه إلى الخاصية share_target يضبط البيانات التي سيقبل تطبيقك أن تشارُكها. هناك ثلاثة طرق شائعة لضبط الخاصية share_target: قبول محتوى المشاركة الأساسي. قبول بيانات مشاركة تُحْدِث تغييرات في التطبيق. قبول تشارُك الملفات. قبول محتوى المشاركة الأساسي إذا أردت لتطبيقك المحدد كوُجْهة مشاركة أن يقبل فقط محتوى المشاركة الأساسي مثل البيانات والروابط والنصوص، فأضف ما يلي إلى ملف بيانه manifest.json: "share_target": { "action": "/share-target/", "method": "GET", "params": { "title": "title", "text": "text", "url": "url" } } إذا كان تطبيقك يتيح المشاركة بواسطة بروتوكول عنوان URL Scheme، وكان يستخدم القيمة body للحقل text بدلاً من القيمة text، يمكنك استبدال text": "text"‎" في الشيفرة السابقة بالقيمة "text": "body". النوع الافتراضي لنوع طلبية http والمعبر عنه بالحقل method هو "GET". يشير حقل enctype غير الظاهر في الشيفرة السابقة إلى نوع تشفير البيانات، يعيَّن هذا الحقل بالقيمة "application / x-www-form-urlencoded" افتراضيًا عندما تكون الطريقة هي "GET". قبول بيانات مشاركة تُحْدِث تغييرات في التطبيق إذا كانت البيانات المشاركة تُحدِثْ تغييرات في التطبيق الوجْهة بطريقة ما، مثلًا تحفظ إشارات مرجعية فيه، وقتها عيّن الحقل method بالنوع "POST" واضبط الحقل enctype. يُنشئ المثال التالي إشارة مرجعية في التطبيق الوُجْهة، لذا فإنه يستخدم "POST" للحقل method و القيمة "multart/form-data" للحقل enctype: { "name": "Bookmark", "share_target": { "action": "/bookmark", "method": "POST", "enctype": "multipart/form-data", "params": { "url": "link" } } } قبول مشاركة الملفات كما هو الحال مع البيانات المُشاركة التي تُحْدِث تغييرات في التطبيق، يتطلب قبول مشاركة الملفات أن تكون طريقة المشاركة method هي "POST" وأن يكون نوع enctype هو "multipart/form-data"، ويجب إضافة الحقل files، التي تمثل مصفوفة أنواع الملفات التي يقبلها تطبيقك، عناصر المصفوفة عبارة عن كائنات مكونة من حقلين: name وaccept. يمثل حقل accept نوع MIME للملف أو إمتداده أو مصفوفة تحتوي كليهما. يفضّل توفير مصفوفة تتضمن كلاً من نوع MIME وامتداد الملف لاختلاف أنظمة التشغيل في ما تفضِّل منهما. { "name": "Aggregator", "share_target": { "action": "/cgi-bin/aggregate", "method": "POST", "enctype": "multipart/form-data", "params": { "title": "name", "text": "description", "url": "link", "files": [ { "name": "records", "accept": ["text/csv", ".csv"] }, { "name": "graphs", "accept": "image/svg+xml" } ] } } } معالجة المحتوى المشارك طريقة التعامل مع البيانات المُشارَكة أمر متروك لك ويعتمد على تطبيقك، مثلًا: يمكن لتطبيق البريد الإلكتروني، صياغة بريد إلكتروني جديد من المحتوى المشارَك، باستخدام العنوان title كموضوع للبريد الإلكتروني، والنص المُشارك text والرابط url متسلسلَين معًا كجسم لرسالة البريد. يمكن لتطبيق تواصل إجتماعي صياغة منشور جديد من المحتوى المشارَك، بتجاهل العنوان title، والاستفادة من text كنص للمنشور، وإضافة url كرابط للمنشور أو حتى كنص أساسي وذلك إذا كان text مفقود، وإذا كان url مفقودًا، فقد يفحص التطبيق النص text بحثًا عن أي عنوان URL ويضيفه كرابط. يمكن لتطبيق مشاركة الصور إنشاء عرض شرائح جديد من المحتوى المشارَك، باستخدام العنوان title كعنوان لعرض الشرائح و النص text كوصف للعرض والملفات files (ملفات الصور) كشرائح تتحرك في العرض. يمكن لتطبيق الرسائل النصية صياغة رسالة جديدة من المحتوى المشارَك وذلك باستخدام النص text وurl متسلسلَين معًا كنص للرسالة وتجاهل العنوان title. معالجة مشاركات GET إذا اختار المستخدم تطبيقك كوُجْهة مشاركة، وكانت طريقة المشاركة method في تطبيقك هي "GET" (الافتراضية)، يفتح المتصفح نافذة جديدة على عنوان URL بقيمة الحقل action ضمن الخاصية share_target، ويولِّد المتصفح بعدها استعلامًا نصيًا بسيطًا، مثلًا إذا كان التطبيق المشارِك يوفر عنوان title ونص text بقيم معينة، فإن سلسلة الاستعلام التي يولّدها المتصفح ستكون ?title=hello&text=world. ولمعالجة هذا الاستعلام، استخدم مستمع الحدث DOMContentLoaded (يحصل هذا الحدث عند الانتهاء من تحميل المستند وبناء DOM) في الواجهة الأمامية foreground وحلِّل نص الاستعلام: window.addEventListener('DOMContentLoaded', () => { const parsedUrl = new URL(window.location); // searchParams.get() will properly handle decoding the values. console.log('Title shared: ' + parsedUrl.searchParams.get('title')); console.log('Text shared: ' + parsedUrl.searchParams.get('text')); console.log('URL shared: ' + parsedUrl.searchParams.get('url')); }); تأكد من استخدام عامل الخدمة لتخزين صفحة الإجراء action مؤقتًا بحيث تُحمَّل بسرعة وتعمل بموثوقية، حتى إن لم يكن المستخدم متصلًا بالشبكة. يمكن أن تساعدك الأداة Workbox في تنفيذ تخزين مؤقت مسبق preaching بالاستفادة من عامل خدمة تطبيقك. معالجة مشاركات POST إذا كانت طريقة المشاركة method في تطبيقك هي "POST"، أي كان تطبيقك يقبل بيانات مُشاركة تُحْدِث تغييرات فيه أو يقبل تشارُك ملفات، فسيحتوي جسم طلب POST الوارد على البيانات التي مرَّرها التطبيق المُشارِك، والمشفرة باستخدام قيمة enctype المعيّنة في ملف البيان. لا يمكن للواجهة الأمامية معالجة هذه البيانات مباشرة، بل تعاملها كطلب تمرره إلى عامل خدمة تطبيقك، الذي بواسطته يمكنك اعتراضها ومعالجتها باستخدام مستمع الحدث fetch. self.addEventListener('fetch', event => { const url = new URL(event.request.url); // If this is an incoming POST request for the // registered "action" URL, respond to it. if (event.request.method === 'POST' && url.pathname === '/bookmark') { event.respondWith((async () => { const formData = await event.request.formData(); const link = formData.get('link') || ''; const responseUrl = await saveBookmark(link); return Response.redirect(responseUrl, 303); })()); } }); التحقق من المحتوى المُشارَك تحقق من معالجة البيانات المُشاركة، فللأسف ليس هناك ما يضمن أن التطبيقات الوجهة ستشارك المحتوى المخصص في المكان المناسب بناء على معاملاته (معاملات المحتوى المشارك). مثلًا يبقى حقل الرابط url فارغًا في نظام Android كما ترى في الصورة التالية، كونه غير مدعوم في نظام مشاركة Android، فغالبًا ما تظهر عناوين URL في حقل النص، أو أحيانًا في حقل العنوان. معالجة المحتوى المشارَك في التطبيق الوجهة. دعم المتصفحات دعَم متصفحَي Chrome وEdge الواجهة البرمجية Web Share Target اعتبارًا من أوائل عام 2021 على النحو التالي: الإصدار 76 فما فوق من كلا المتصفحين (Chrome وEdge) على نظام Android الإصدار 89 فما فوق من Chrome على نظام Chrome OS. تذكر بالنسبة لجميع أنظمة التشغيل الأساسية، أنه يجب تثبيت تطبيق الويب حتى يظهر كهدف محتمل لاستقبال البيانات المُشاركة. ترجمة -وبتصرف- للمقال Receiving shared data with the Web Share Target API من موقع web.dev اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA ميزات تطبيق الويب التقدمي مشاركة تطبيقات الويب التقدمية البيانات عبر الواجهة Web Share إضافة اختصارات للمهام الشائعة في تطبيقات الويب التقدمية PWA مفهوم Service Worker وتأثيره في أداء وبنية مواقع وتطبيقات الويب
  5. يدعم نظام الويب الآن ميزة اختصارات التطبيقات، وذلك لتسهيل إنجاز مهام التطبيق الرئيسية، فهذه الاختصارات تسمح لمطوري الويب بتوفير وصول سريع إلى أهم الإجراءات الشائعة التي يحتاجها مستخدمو التطبيق كثيرًا. ستتعلم من هذا المقال كيفية تعيين اختصارات للتطبيقات، مع بعضٍ من أفضل الممارسات الممكن اتباعها لرفع مستوى هذه الاختصارات. نبذة عن اختصارات التطبيق تساعد اختصارات تطبيقك المستخدمين على إجراء المهام الشائعة بسرعة في تطبيقاتك، ويؤدي الوصول السهل إلى هذه المهام من أي مكان توجد فيه أيقونة التطبيق إلى زيادة تفاعل المستخدمين مع تطبيقك. تُستدعى قائمة اختصارات التطبيق عن طريق النقر بزر الفأرة الأيمن على أيقونة التطبيق في شريط مهام نظام Windows أو شريط Dock في نظام macOS لبيئة سطح المكتب، أو الضغط لفترة طويلة على أيقونة مشغل التطبيق في نظام Android. قائمة اختصارات التطبيق في Android. قائمة اختصارات التطبيق في Windows. تظهر قائمة اختصارات التطبيقات فقط لتطبيقات الويب التقدمية PWAs المثبتة على سطح مكتب أو جهاز المستخدم المحمول. يعبر كل اختصار من اختصارات تطبيقك عن إجراء ينوي المستخدم فعله، وكل منها مرتبط بعنوان URL ضمن مجال التطبيق، يُفتح هذا العنوان عندما ينقر المستخدمون على الاختصار المعنِي. وفيما يلي أمثلة على الإجراءات المحتملة لاختصارات التطبيق: صفحات التطبيق الرئيسية (الوصول إلى صفحة التطبيق الرئيسية أو صفحة آخر الطلبات مثلًا) مهام البحث في التطبيق مهام إدخال البيانات (مثلًا كتابة رسالة بريد أو منشور) الأنشطة (مثلًا بدء محادثة مع جهات الاتصال الشائعة) تحديد اختصارات التطبيق في بيان تطبيق الويب manifest تُحدَّد اختصارات التطبيقات اختياريًا في ملف بيان موارد تطبيق الويب manifest، والذي هو عبارة عن ملف JSON يخبر المتصفح عن تطبيق الويب التقدمي PWA وكيف يجب أن يتصرف عند تثبيته على حاسوب المستخدم المكتبي أو جهازه المحمول، وتُعرَّف اختصارات التطبيقات في مصفوفة shortcuts بالتحديد ضمن هذا الملف كما في المثال التالي: { "name": "Player FM", "start_url": "https://player.fm?utm_source=homescreen", … "shortcuts": [ { "name": "Open Play Later", "short_name": "Play Later", "description": "View the list of podcasts you saved for later", "url": "/play-later?utm_source=homescreen", "icons": [{ "src": "/icons/play-later.png", "sizes": "192x192" }] }, { "name": "View Subscriptions", "short_name": "Subscriptions", "description": "View the list of podcasts you listen to", "url": "/subscriptions?utm_source=homescreen", "icons": [{ "src": "/icons/subscriptions.png", "sizes": "192x192" }] } ] } كل عضو في مصفوفة الاختصارات shortcuts هو كائن اختصار يتكون على الأقل من حقل الاسم وعنوان url، أما الحقول الأخرى فهي اختيارية. حقل الاسم name: التسمية الممكن للمستخدمين قراءتها لاختصار التطبيق. حقل الاسم المختصر short_name (اختياري): التسمية الممكن للمستخدمين قراءتها لاختصار التطبيق عندما تكون المساحة محدودة، ويوصى بتعيين هذا الحقل حتى لو كان اختياريًا. حقل الوصف description (اختياري): الغرض من اختصار التطبيق. حقل عنوان URL: عنوان URL الذي يُفتح عندما ينقر المستخدم على اختصار التطبيق، ويجب أن يكون عنوان URL هذا موجودًا في نطاق بيان تطبيق الويب. حقل الأيقونات icons (اختياري): مصفوفة من كائنات الصور، يتكون كل كائن منها خاصية src وsize بشكل أساسي، بالإضافة للخاصية الإختيارية type. إذا كنت تريد تعيين أيقونات مثالية الحجم، فاجعلها بالحجم الموصى به 48dp، والذي يتوافق مع كثافات القياسات التالية بوحدة البكسل بناء على معادلة التحويل التالية: px = dp * (dpi / 160) في الشاشات منخفضة الكثافة ldpi والتي تكافئ 120 dpi، الحجم 48dp يساوي 48*0.75=36px في الشاشات متوسطة الكثافة mdpi والتي تكافئ 160 dpi، الحجم 48dp يساوي 48px (نفسه)، أي يكون حجم الأيقونة 48x48 بكسل في الشاشات عالية الكثافة hdpi والتي تكافئ 240 dpi، الحجم 48dp يساوي 48*1.5=72px، أي يكون حجم الأيقونة 72x72 بكسل في الشاشات عالية الكثافة جدًا xhdpi والتي تكافئ 320 dpi، الحجم 48dp يساوي 48*2=96px، أي يكون حجم الأيقونة 96x96 بكسل في الشاشات عالية الكثافة جدًا جدًا xxhdpi والتي تكافئ 480 dpi، الحجم 48dp يساوي 48*3=144px، أي يكون حجم الأيقونة 144x144 بكسل في الشاشات عالية الكثافة جدًا جدًا جدًا xxxhdpi والتي تكافئ 640 dpi، الحجم 48dp يساوي 48*4=192px، أي يكون حجم الأيقونة 192x192 بكسل اختبر اختصارات تطبيقك للتحقق من تعيين اختصارات التطبيق بشكل صحيح، استخدم قائمة Manifest في لوحة Application في أدوات المطور DevTools. تظهر الصورة اختصارات التطبيقات في DevTools توفر هذه القائمة إمكانية معاينة عدة خصائص محفوظة في ملف البيان، بما في ذلك اختصارات التطبيقات، ما يسهل التحقق من تحميل أيقونات الاختصارات إذا تم تعيينها (كونها اختيارية)، بالشكل الصحيح. أفضل الممارسات فيما يلي نصائح بمارسات يوصى اتباعها لزيادة كفاءة اختصارات تطبيقك. طلب اختصارات التطبيق حسب الأولوية يفضَّل تعيين اختصارات التطبيقات حسب الأولوية، وذلك بوضع اختصارات التطبيقات الأكثر أهمية في بداية مصفوفة الاختصارات shortcuts ضمن ملف mainfest، نظرًا لأن الحد الأقصى لعدد الاختصارات التي تظهر في قائمة اختصارات التطبيق يختلف باختلاف نظام التشغيل. مثلًا يتيح Chrome وEdge حتى 10 اختصارات للتطبيق في نظام Windows، بينما يأخذ Chrome في الاعتبار أول 4 اختصارات للتطبيق فقط في نظام Android، ويسمح متصفح Chrome 92 الآن بثلاث اختصارات للتطبيقات فقط على نظام Android 7 استخدم أسماء اختصارات فريدة للتطبيق يجب استخدام اسم مميز لكل اختصار تطبيق. لا تعتمد على الأيقونات للتمييز بين اختصارات التطبيقات لأنها قد لا تكون ظاهرة على جميع الأجهزة، فنظام macOS مثلًا لا يدعم إظهار أيقونات اختصارات التطبيقات المثبّته في شريط Dock. قياس استخدام اختصارات التطبيق يمكنك تعقب عناوين (الخاصية url) اختصارات تطبيقك لأغراض إحصائية، مثلًا لقياس مدى استخدام مستخدمي تطبيقك تلك الاختصارات. دعم المتصفحات يدعم متصفحَي Chrome وEdge اختصارات تطبيقات الويب التقدمية على النحو التالي: الإصدار 84 فما فوق من Chrome على نظام Android. الإصدار 85 فما فوق من كلا المتصفحين (Chrome وEdge) على نظام Windows. الإصدار 92 فما فوق من Chrome على نظام Chrome OS. الإصدار 96 فما فوق من كلا المتصفحين (Chrome وEdge) على نظامَي MacOS ولينكس Linux. دعم فاعليِّة الويب الموثوق فاعليِّة الويب الموثوق Trusted Web Activity هو طريقة لتغليف تطبيق الويب التقدمي بحاوية container تجعله يتصرف كتطبيقات الهواتف المعيارية. تقرأ الأداة Bubblewrap المستخدمة لإنشاء تطبيقات Android باستخدام فاعليِّة الويب الموثوق، اختصارات تطبيق الويب من ملف بيانه manifest، وتولِّد تلقائيًا الإعدادات المقابلة لها لتطبيق Android. لاحظ عند استخدام Bubblewrap أنه يجب تحديد أيقونات لاختصارات التطبيقات وبحجم لا يقل عن 96×96 بكسل. أداة باني تطبيق الويب التقدمي PWABuilder تحوِّل تطبيق الويب التقدمي بسهولة إلى فاعليِّة ويب موثوق، وتدعم اختصارات التطبيقات. نموذج راجع نموذج اختصارات التطبيق ومصدره. ثبت تطبيق PWA هذا وجرب اختصاراته. ترجمة -وبتصرف- للمقال Get things done quickly with app shortcuts من موقع web.dev اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA ميزات تطبيق الويب التقدمي جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل التحسين التدريجي لتطبيقات الويب التقدمية PWA شرح ملف البيان manifest لتطبيق الويب التقدمي PWA
  6. فاجأ نيك فينك Nick Finck وستيف شامبيون Steve Champeon عالم تصميم الويب بمفهوم "التحسين التدريجي" progressive enhancement في شهر مارس 2003، وهو مفهوم يعبر عن استراتيجية لتصميم الويب تركز على تنزيل المحتوى الأساسي لصفحة الويب أولًا، ثم تُضاف تدريجيًا الطبقات ذات التفاصيل والملامح الدقيقة على المحتوى الأساسي. كان التحسين التدريجي عام 2003 يتعلق باستخدام ميزات CSS الحديثة وجافاسكربت الواضحة unobtrusive JavaScript ورسومات فكتور القابلة لتغيير الحجم Scalable Vector Graphics، لكن في 2020 وما بعده أصبح التحسين التدريجي يتعلق باستخدام إمكانيات المتصفح الحديثة. شرائح عرض: تصميم ويب متكامل مع التحسين التدريجي، (المصدر). جافاسكربت الحديثة عند الحديث عن JavaScript الحديثة، فإننا نستحضر دعم المتصفح لأحدث مميزات JavaScript ES 2015 الأساسية، إذتتضمن (ECMAScript 2015 (ES6 الوعود promises والوحدات modules والفئات classes وقالب النص template literals والدوال السهمية arrow functions والكلمتان المفتاحيتان let و const والمولِّدات generators والاسناد بالتفكيك destructuring assignment ومعاملَي البقية rest والنشر spread والنوعين map الخرائط وset الأطقم والنوعين الخرائط والأطقم ضعيفة الإشارة WeackMap وWeackSet الأطقم ومميزات أخرى كثيرة. جدول دعم المتصفحات لمعايير ES6. (مصدر) يمكن استخدام الدوال غير المتزامنة async والتي هي أحد مميزات ES 2017، في جميع المتصفحات الرئيسية. تتيح الكلمتان المحجوزتان async و await كتابة السلوك غير المتزامن المعتمِد على الوعود بأسلوب نظيف، مع تجنب الحاجة إلى تكوين سلاسل وعود Promises chaining بشكل صريح. جدول دعم المتصفحات للدوال غير المتزامنة (المصدر). كما وصل سريعًا دعم المتصفحات لإضافات ES 2020 الحديثة مثل التسلسل الإختياري optional chaining وعامل الاستبدال اللاغي nullish coalescing، لاحظ الشيفرة التالية. const adventurer = { name: 'Alice', cat: { name: 'Dinah', }, }; console.log(adventurer.dog?.name); // Expected output: undefined console.log(0 ?? 42); // Expected output: 0 التطبيق المختار كنموذج: Fugu Greetings سنعمل في هذا المقال مع تطبيق ويب تقدمي PWA بسيط اسمه Fugu Greetings (مستودع التطبيق على github). إنشاء هذا التطبيق كان محاولة لمنح الويب كامل قوة تطبيقات Android أو iOS أو سطح المكتب. يُعد Fugu Greetings تطبيق رسم يتيح إنشاء بطاقات ترحيب وهمية، وإرسالها إلى الأصدقاء، ويجسد هذا التطبيق مفاهيم تطبيقات الويب التقدمية الأساسية، فهو موثوق ومتاح بالكامل دون اتصال، كما أنه قابل للتثبيت على الشاشة الرئيسية للجهاز ويتكامل بسلاسة مع نظام التشغيل. التطبيق المختار كنموذج Fugu Greetings التحسين التدريجي Progressive Enhancement حان الوقت للتحدث عن التحسين التدريجي، يصف مسرد مصطلحات الويب web glossary لدى موقع MDN هذا المفهوم على النحو التالي: قد يكون إنشاء كل بطاقة تهنئة من البداية أمرًا مرهقًا بالفعل، لذا فكر مطورو Fugu Greetings بميزة تتيح للمستخدمين استيراد صورة وبدء إنشاء البطاقة انطلاقًا منها. الطريقة التقليدية التي يمكن أن تفكر فيها لاستيراد ملفات في أحد تطبيقاتك هي استخدام عنصر <input type=file>، حيث تُنشئ بداية العنصر، وتعين نوعه أو الخاصية type إلى'file' وتُضيف أنواع المعيار MIME إلى الخاصية accept، ثم تنقر عليه برمجيًا وتستمتع بالنتيجة. يمكنك تجريب هذه الميزة في تطبيق Fugu Greetings باختيار صورة معينة إلى التطبيق وملاحظة كيف أنها تُرسم مباشرة في حاوية الرسم canvas. const importImage = async () => { return new Promise((resolve) => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.addEventListener('change', () => { resolve(input.files[0]); }); input.click(); }); } وبوجود ميزة استيراد، فكر مطورو التطبيق بميزة تصدير أيضًا تُمكِّن المستخدمين من حفظ بطاقاتهم الترحيبية محليًا. الطريقة التقليدية التي يمكنك فعلها لحفظ الملفات في تطبيقاتك، هي إنشاء رابط تنزيل بالخاصية download وبعنوان كائن البيانات الثنائية Blob URL كقيمة للخاصية href، ويمكنك أيضًا النقر برمجيًا لتشغيل التنزيل، ولا تنسى حذف عنوان URL لكائن Blob بعد توليده لمنع تسرب الذاكرة. const exportImage = async (blob) => { const a = document.createElement('a'); a.download = 'fugu-greeting.png'; a.href = URL.createObjectURL(blob); a.addEventListener('click', (e) => { setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000); }); a.click(); }; لكن لحظة! أنت بهذا لم تنزِّل البطاقة بل حفظتها، حيث لم يظهر مربع الحوار "حفظ" الذي يتيح لك اختيار مكان حفظ الملف، وانما نزّل المتصفح البطاقة مباشرةً دون تدخلك، ووضعها مباشرة في مجلد التنزيلات. ألا يوجد طريقة تنزيل أفضل؟ ماذا لو كان بإمكانك فتح ملف محلي، وتحريره، ثم حفظ التعديلات، إما على نفس الملف أو في ملف جديد، أليس هذا أفضل؟ تتيح الواجهة البرمجية File System Access التي تعمل في الخلفية File System Access API لتطبيقك بفتح وإنشاء ملفات ومجلدات، وكذلك تعديل الملفات وحفظها، وذلك بالاستفادة من التابع ()window.chooseFileSystemEntries، لكن عند استخدام هذا التابع تحتاج إلى استيراد مشروط لشيفرات الاستيراد والتصدير (ملفات mjs في المثال التالي) بناء على كون التابع متاحًا أم لا، أو بعبارة أخرى بناء على كون المتصفح يدعم الواجهة File System Access أم لا. موضّح طريقة القيام بذلك في الشيفرة التالية. const loadImportAndExport = () => { if ('chooseFileSystemEntries' in window) { Promise.all([ import('./import_image.mjs'), import('./export_image.mjs'), ]); } else { Promise.all([ import('./import_image_legacy.mjs'), import('./export_image_legacy.mjs'), ]); } }; ولكن قبل التعمق في تفاصيل الواجهة File System Access، والبدء باستخدام شيفراتها الخاصة باستيراد وتصدير الملفات،، لنرى سريعًا نمط التحسين التدريجي المتبع في الشيفرة السابقة. عند طلب صفحة من موقع ويب في المتصفحات التي لا تدعم الواجهة البرمجية File System Access، يتم فقط تحميل السكريبتات القديمة import_image_legacy.mjs و export_image_legacy.mjs التي تحتوي شيفرات التصدير والاستيراد التقليدية. يمكنك رؤية نافذة الشبكة في Firefox و Safari أدناه. نافذة مراقبة الشبكة لمتصفح Safari. نافذة شبكة أدوات المطورين لمتصفح Firefox. لكن لأن Chrome متصفح يدعم الواجهة File System Access، فيتم فقط تحميل السكربتات الجديدة import_image.mjs وexport_image.mjs التي تحتوي شيفرات متقدمة لميزات استيراد وتصدير الملفات في تطبيقات PWAs، بالاعتماد على الواجهة File System Access. نافذة شبكة أدوات المطورين لمتصفح Chrome. واجهة الوصول إلى ملفات النظام حان الآن الوقت لإلقاء نظرة على التنفيذ الفعلي للتحسين السابق، إذا فكرت باستيراد صورة باستخدام واجهة الوصول إلى ملفات النظام، فإنك ستستدعي التابع ()window.chooseFileSystemEntries وتمرر له الخاصية accept التي تعبر من خلالها أنك تريد ملفات صور عبر امتدادات الملفات أو أنواع MIME، وينتج عن هذا مقبض ملف file handle يمكنك من خلاله الحصول على الملف الفعلي عن طريق استدعاء الدالة ()getFile. const importImage = async () => { try { const handle = await window.chooseFileSystemEntries({ accepts: [ { description: 'Image files', mimeTypes: ['image/*'], extensions: ['jpg', 'jpeg', 'png', 'webp', 'svg'], }, ], }); return handle.getFile(); } catch (err) { console.error(err.name, err.message); } }; تُطبَّق الفكرة نفسها عند تنفيذ تصدير الصورة، لكن هذه المرة تحتاج تمرير المعامل type بالقيمة 'save-file' إلى التابع ()ChooseFileSystemEntries، بهذا تحصل على مربع حوار لحفظ الملف، لم يكن هذا ضروريًا عند فتح الملف (استيراد صورة) لأن القيمة 'open-file' هي الافتراضية للمعامل type. ستعين هنا المعامل accept بشكل مشابه لاستيراد ملف، لكن في التصدير العملية تقتصر على صور PNG فقط، وبدلاً من جلب الملف من المقبض، فإنك هنا تنشئ مجرى قابل للكتابة writable stream عن طريق التابع ()createWritable الذي تستخدمه لكتابة كائن البيانات الثنائية Blob أو يعني صورة بطاقتك الترحيبية إلى الملف، وأخيرًا تغلق المجرى. يمكن أن يفشل التصدير إذا نفذت مساحة القرص أو حدث خطأ في الكتابة إلى الملف أو القراءة منه، أو ربما ببساطة إذا ألغى المستخدم مربع حوار حفظ الملف، لهذا نقوم دائمًا بتغليف الاستدعاءات في عبارة try ... catch. const exportImage = async (blob) => { try { const handle = await window.chooseFileSystemEntries({ type: 'save-file', accepts: [ { description: 'Image file', extensions: ['png'], mimeTypes: ['image/png'], }, ], }); const writable = await handle.createWritable(); await writable.write(blob); await writable.close(); } catch (err) { console.error(err.name, err.message); } }; إذن باستخدام التحسين التدريجي مع الواجهة البرمجية File System Access، فإنه بإمكانك استيراد ملف صورة إلى التطبيق، وتُرسم تلك الصورة مباشرة في حاوية الرسم canvas، كما يمكنك إجراء تعديلاتك على الصورة وحفظها باستخدام مربع حوار حفظ يتيح لك تحديد اسم للملف الذي تريد حفظه وموقع لتخزينه، وذلك كله فقط للمتصفحات التي تدعم الواجهة File System Access. مربع حوار فتح ملف. الصورة المستوردة. حفظ الصورة المعدلة إلى ملف جديد. واجهتَي مشاركة الويب قد ترغب ذات مرة بمشاركة بطاقتك الترحيبية، هذا ما أتاحه مطورو Fugu Greetings بالاستفادة من الواجهتين البرمجيتين Web Share (مشاركة الويب) و Web Share Target (مشاركة وُجْهة الويب). تمتاز أجهزة الجوّال وأنظمة تشغيل سطح المكتب الآن بآليات مشاركة مدمجة، مثلًا في الصورة التالية هناك زر لمشاركة أحد مقالات مدونة ما في متصفح Safari على macOS، عند النقر على زر مشاركة المقال يمكنك مشاركة رابط المقال مع أصدقائك عبر تطبيقات جهازك مثلًا عبر تطبيق رسائل macOS. واجهة مشاركة الويب في متصفح Safari على macOS. إذا أردت تحقيق ميزة المشاركة هذه لأحد تطبيقاتك، فالشيفرة لفعل ذلك واضحة ومباشِرة، بداية تستدعي التابع ()navigator.share وتعطيه معاملات اختيارية title وtext وurl في كائن كمعامل. هذا إذا أردت مشاركة رابط المصدر، لكن ماذا لو أردت مشاركة صورة؟ المستوى الأول من واجهة مشاركة الويب لا يدعم هذا حتى الآن، لكن لحسن الحظ فإن المستوى الثاني من واجهة مشاركة الويب أضاف إمكانيات مشاركة الملفات. try { await navigator.share({ title: 'Check out this article:', text: `"${document.title}" by @tomayac:`, url: document.querySelector('link[rel=canonical]').href, }); } catch (err) { console.warn(err.name, err.message); } الطريقة التي اتبعها مطورو Fugu Greetings لمشاركة الصور هي بداية إعداد كائن data بمصفوفة ملفات files تتكون من كائن بيانات ثنائي واحد، وعنوان title ونص text، بعدها تم استخدام التابع ()navigator.canShare يخبرنا ما إذا كان كائن data الذي نحاول مشاركته يمكن مشاركته تقنيًا عبر المتصفح أم لا (هل المتصفح حديث ويدعم المشاركة أم لا)، إذا أفاد التابع بإمكانية مشاركة الملفات، يمكن استدعاء التابع ()navigator.share كما فعلنا في مشاركة الروابط. const share = async (title, text, blob) => { const data = { files: [ new File([blob], 'fugu-greeting.png', { type: blob.type, }), ], title: title, text: text, }; try { if (!(navigator.canShare(data))) { throw new Error("Can't share data.", data); } await navigator.share(data); } catch (err) { console.error(err.name, err.message); } }; وكعادة أي تحسين تدريجي، فقط في حال دعم المتصفح لواجهة برمجة مشاركة الويب أي فقط في حال وجود كلًا من 'share' و 'canShare' في كائن التنقل navigator، فإن تطبيق Fugu Greetings يستدعي الشيفرة السابقة (على فرض أنها محفوظة في ملف بالاسم share.mjs) عبر دالة الاستيراد الديناميكي ()import، وفي المتصفحات التي تحقق شرط واحد فقط من الشرطين مثل نسخ الهاتف المحمول من متصفح Safari، فيُستَخدم الشرط المتاح فقط. const loadShare = () => { if ('share' in navigator && 'canShare' in navigator) { import('./share.mjs'); } }; بوجود ميزة المشاركة هذه في تطبيق Fugu Greetings، فإنك إذا نقرت زر مشاركة في متصفح يدعم واجهة مشاركة الويب مثل متصفح Chrome على نظام Android، يتم فتح لوحة أو حوار للمشاركة، من خلاله يمكنك اختيار التطبيق الذي تريد المشاركة إليه مثلًا Gmail، بذلك تظهر أداة إنشاء البريد الإلكتروني مع الصورة التي شاركتها كمرفق في الرسالة. اختيار تطبيق لمشاركة الملف إليه. أُرفِق الملف المشارك برسالة بريد الكتروني جديدة في منشئ الـ Gmail. واجهة منتقي جهات الاتصال عندما تكتب بطاقة ترحيب، قد لا يكون من السهل دائمًا كتابة اسم جهة اتصال أحد أصدقائك بشكل صحيح عندما تريد مشاركته البطاقة عبر رقم هاتفه، يمكن للواجهة البرمجية Contact Picker التي تعمل في الخلفية Contact Picker API حل هذه المشكلة، فهي تتيح لمستخدمي التطبيق الحصول على أسماء أصدقائهم، المحفوظة في جهات اتصال هواتفهم من مجرد نقرها. إذا أردت إضافة هذه الميزة لتطبيقاتك فإنك ستحدد قائمة الخاصيات التي تريد الوصول إليها من بيانات جهات الاتصال، فاستخدِم خاصية الاسم فقط في Fugu Greetings كما ترى في الشيفرة التالية. قد تهتم أنت في تطبيقاتك بالوصول إلى أرقام هواتف جهات الاتصال أو عناوين بريدهم الإلكتروني أو صورهم الشخصية أو عناوين سكنهم، وبعدها تكوِّن كائن خيارات options وتضبط الحقل multiple إلى true لتتمكن من اختيار أكثر من مدخل (أكثر من جهة اتصال)، وأخيرًا تستدعي التابع ()navigator.contacts.select الذي يعرض جهات الاتصال التي يختارها المستخدمين بالخاصيات المحددة في تطبيقك. const getContacts = async () => { const properties = ['name']; const options = { multiple: true }; try { return await navigator.contacts.select(properties, options); } catch (err) { console.error(err.name, err.message); } }; وكعادة أي تحسين تدريجي، لا تستورد شيفرة انتقاء جهات الاتصال إلا في حال كان المتصفح يدعم بالفعل الواجهة البرمجية Contact Picker. if ('contacts' in navigator) { import('./contacts.mjs'); } يمكنك تجريب الميزة التي توفرها الواجهة البرمجية Contact Picker في تطبيق Fugu Greetings بالضغط على زر جهات الاتصال contacts في التطبيق ثم اختيار أسماء جهات اتصالك التي تريد أن تُرسم في بطاقتك الترحيبية. لاحظ كيف يقتصر منتقي جهات الاتصال في تطبيق Fugu Greetings على جلب أسماء جهات الاتصال المختارة فقط دون عناوين بريدها أو معلومات أخرى مثل أرقام هواتفها. اختيار اسمين من قائمة جهات الاتصال عبر مُنتقي جهات الاتصال. رُسمَ الاسمين في بطاقة الترحيب. واجهة الحافظة غير المتزامنة حان الآن وقت النسخ واللصق، إحدى عملياتنا المفضلة كمطوري برمجيات هي النسخ واللصق، وكمُنشِئ بطاقات ترحيب عبر تطبيق Fugu Greetings، فإنك قد ترغب في بعض الأحيان بفعل نفس الشيء، لصق صورة في بطاقة ترحيب تعمل عليها أو نسخ بطاقة جاهزة إلى تطبيق آخر لتحريرها فيه. تدعم الواجهة البرمجية Clipboard غير المتزامنة التي تعمل في الخلفية Async Clipboard API نسخ ولصق كلًا من النصوص والصور، لهذا استخدمها مطورو Fugu Greetings لإعداد ميزة النسخ من واللصق إلى التطبيق، وإذا أردت فعل ذلك في تطبيقاتك، فإنك لنسخ شيء ما إلى حافظة النظام، تحتاج أن تكتبه إليها عبر التابع ()navigator.clipboard.write الذي يأخذ مصفوفة من عناصر الحافظة كمعامل، كل عنصر منها عبارة عن كائن بحقل قيمته الـ blob (يمثل المحتوى المنسوخ)، ومفتاحه نوع blob (كائن بيانات ثنائي). const copy = async (blob) => { try { await navigator.clipboard.write([ new ClipboardItem({ [blob.type]: blob, }), ]); } catch (err) { console.error(err.name, err.message); } }; وللصق شيء ما من الحافظة، فإنك تحتاج إلى المرور على عناصرها بعد الحصول عليها من خلال التابع ()navigator.clipboard.read. يمكن لكل عنصر من عناصر الحافظة ClipboardItem الاحتفاظ بمحتوياته في أنواع MIME مختلفة، لذلك فإنك للحصول على كائن blob لعنصر الحافظة فإنك بحاجة للمرور على قائمة الأنواع المتاحة افتراضيًا، ولكل نوع تستدعي الدالة ()getType ممررًا إليها النوع الحالي في حلقة التكرار كوسيط. const paste = async () => { try { const clipboardItems = await navigator.clipboard.read(); for (const clipboardItem of clipboardItems) { try { for (const type of clipboardItem.types) { const blob = await clipboardItem.getType(type); return blob; } } catch (err) { console.error(err.name, err.message); } } } catch (err) { console.error(err.name, err.message); } }; وبالطبع كأي ميزة تحسين تدريجي، تفعل هذا فقط للمتصفحات الداعمة للواجهة البرمجية Clipboard: if ('clipboard' in navigator && 'write' in navigator.clipboard) { import('./clipboard.mjs'); } يمكنك تجريب هذه الميزة عمليًا في تطبيق Fugu Greetings، مثلًا جرب نسخ صورة مفتوحة لديك في تطبيق عارض الصور إلى الحافظة، والآن عندما تنقر زر لصق في التطبيق، فإنه يستأذنك ما إذا كنت ترغب بالسماح له بمشاهدة النصوص والصور في الحافظة، بعدما تسمح له بذلك فإنه يستطيع لصق الصورة من الحافظة. طلب الإذن بالوصول إلى محتوى الحافظة. واجهة الشارات كون تطبيق Fugu Greetings قابلًا للتثبيت installable، له أيقونة خاصة يمكن للمستخدمين تثبيتها في شريط التطبيقات app dock أو الشاشة الرئيسية، استغل مطورو Fugu Greetings هذه الأيقونة لإظهار شارة انطلاقًا منها، تحتوي عدّاد نقرات قلم الرسم pen strokes counter، وذلك بالاستفادة من الواجهة البرمجية Padging التي تعمل في الخلفية Badging API. إذا أردت إضافة شارات في تطبيقاتك، ففيما يلي طريقة استخدام الواجهة البرمجية Padging لفعل ذلك، إذ يمكنك ببساطة إضافة مستمع حدث event listener يعيِّن شارة الأيقونة بقيمة عدّاد نقرات القلم التي تزيد عند تحقق الحدث pointerdown (ينطلق هذا الحدث عندما يصبح مؤشر الرسم فعال)، وعند مسح محتوى حاوية الرسم يمكنك إعادة تعيين العداد إلى صفر لتُزال الشارة. let strokes = 0; canvas.addEventListener('pointerdown', () => { navigator.setAppBadge(++strokes); }); clearButton.addEventListener('click', () => { strokes = 0; navigator.setAppBadge(strokes); }); وكعادة أي تحسين تدريجي، حمّل هذا المنطق (الشيفرة) فقط للمتصفحات التي تدعم الواجهة البرمجية Badging: if ('setAppBadge' in navigator) { import('./badge.mjs'); } ولتجريب ذلك قمت في هذا المثال برسم الأرقام من واحد إلى سبعة، باستخدام ضغطة قلم واحدة لكل رقم، لذا فإن عداد الشارة على الأيقونة الآن هو 7. رسم الأرقام من 1 إلى 7 بضغطة قلم واحدة لكل رقم. عدّاد ضغطات القلم ممثّل شارة لأيقونة التطبيق. واجهة المزامنة الدورية للخلفية هل تريد أن تبدأ كل يومٍ جديدٍ بمحتوى جديد؟ من الميزات الرائعة لتطبيق Fugu Greetings أنه يمكن أن يلهمك كل يوم بصورة خلفية جديدة لإنشاء بطاقتك، يستخدم التطبيق لهذه الميزة الواجهة البرمجية Periodic Background Sync التي تعمل في الخلفية Periodic Background Sync API، وإذا أردت فعل هذا لتطبيقاتك فإن عليك إضافة حدث مزامنة دوري periodic sync event في تسجيل عامل الخدمة service worker registration عبر التابع ()register، عملية تسجيل الحدث تتطلب وسم tag يستمع عامل الخدمة إليه، وحد أدنى للفاصل الزمني للمزامنة interval. ممرر هنا حدث المزامنة بوسم اسمه 'image-of-the-day' وبفاصل زمني لا يقل عن يوم واحد، ليحصل المستخدم على صورة خلفية جديدة كل 24 ساعة. const registerPeriodicBackgroundSync = async () => { const registration = await navigator.serviceWorker.ready; try { registration.periodicSync.register('image-of-the-day-sync', { // An interval of one day. minInterval: 24 * 60 * 60 * 1000, }); } catch (err) { console.error(err.name, err.message); } }; الخطوة الثانية هي الاستماع إلى حدث المزامنة الدورية 'periodicsync' في عامل الخدمة، فإذا كان وسم الحدث هو 'image-of-the-day'، أي بنفس اسم الحدث المُسجل في عامل الخدمة، يتم جلب صورة اليوم عبر الدالة ()getImageOfTheDay، وتُنشر النتيجة لجميع العملاء، لتُحدَّث حاويات رسمهم canvases وذاكراتهم المؤقته caches بها. self.addEventListener('periodicsync', (syncEvent) => { if (syncEvent.tag === 'image-of-the-day-sync') { syncEvent.waitUntil( (async () => { const blob = await getImageOfTheDay(); const clients = await self.clients.matchAll(); clients.forEach((client) => { client.postMessage({ image: blob, }); }); })() ); } }); وكالعادة لأن هذه الميزة تعتبر تحسينًا تقدميًا، فيجب تحميل الشيفرة السابقة فقط للمتصفحات التي تدعم الواجهة البرمجية Periodic Background Sync لكلًا من شيفرة العميل و شفرة الخادم. لاحظ بدلاً من استخدام الاستيراد الديناميكي ()import غير المدعوم في عامل الخدمة حتى اللحظة، استخدمنا الاستيراد التقليدي ()importScripts. // في طرف العميل const registration = await navigator.serviceWorker.ready; if (registration && 'periodicSync' in registration) { import('./periodic_background_sync.mjs'); } // في عامل الخدمة if ('periodicSync' in self.registration) { importScripts('./image_of_the_day.mjs'); } وبتجريب هذه الميزة في تطبيق Fugu Greetings، يؤدي الضغط على زر Wallpaper إلى إظهار صورة بطاقة الترحيب لهذا اليوم التي تُحدَّث يوميًا عبر الواجهة البرمجية Periodic Background Sync. نقر زر Wallpaper يؤدي لعرض صورة اليوم. واجهة مُطْلِق الإشعارات قد تحتاج في بعض الأحيان إلى دَفعة بسيطة لإنهاء بطاقة الترحيب التي بدأت بإنشائها في تطبيق Fugu Greetings، يمكن الحصول على هذه الدفعة من خلال ميزة تحديد الوقت الذي تريد فيه كمستخدم للتطبيق إظهار تنبيه لإنهاء بطاقتك الترحيبية، فعندما يحين ذلك الوقت ستتلقى إشعارًا بأن بطاقتك في انتظار إنهائك لها. تمكن مطورو Fugu Greeting من إضافة هذه الميزة من خلال الواجهة البرمجية Notification Triggers التي تعمل في الخلفية Notification Triggers API، فبعد تعيينك الوقت الذي تريد إطلاق الإشعار فيه، يُجدوِل التطبيق الإشعار باستخدام الخاصية showTrigger، يمكن أن تكون الجدولة عبر الكائن TimestampTrigger بالتاريخ الذي حددته، وسيُطلق التنبيه محليًا دون الحاجة إلى جانب الشبكة أو الخادم. const targetDate = promptTargetDate(); if (targetDate) { const registration = await navigator.serviceWorker.ready; registration.showNotification('Reminder', { tag: 'reminder', body: "It's time to finish your greeting card!", showTrigger: new TimestampTrigger(targetDate), }); } وكما هو الحال مع كل ما ذكرناه، تعد هذه الميزة تحسينًا تدريجيًا، لذا يتم تحميل الشيفرة بشرط دعم المتصفح لها. if ('Notification' in window && 'showTrigger' in Notification.prototype) { import('./notification_triggers.mjs'); } يمكنك تجريب هذه الميزة بنفسك في تطبيق Fugu Greetings، وذلك بتفعيل خيار Reminder (تذكير) الذي يَطلب منك إدخال متى تريد أن تُنبّه بشأن إنهاء بطاقتك، عندما يحين ذلك الموعد ستحصل بالفعل على تنبيه لإنهائها. جدولة إشعار محلي للتذكير بإنهاء بطاقة التهنئة. عندما يُطلَق إشعار مجدول في Fugu Greetings، يُعرَض تمامًا مثل أي إشعار آخر، لكن الفرق أنه لا يتطلب اتصالاً بالشبكة كما قلنا. الإشعار المطلَق يظهر في مركز إشعارات macOS. واجهة قفل الإيقاظ تحتاج في بعض الأحيان إلى التحديق طويلًا في الشاشة حتى يأتيك الإلهام، أسوأ ما يمكن أن يحدث حينها هو إيقاف تشغيل الشاشة، وهذا ما يحدث غالبًا تلقائيًا عند توقُف تفاعلنا مع التطبيقات لفترة طويلة. يمكن منع حدوث ذلك من خلال الواجهة البرمجية Wake Lock التي تعمل في الخلفية Wake Lock API. إذا أردت إضافة هذه الميزة لتطبيقاتك، فإن أول ما عليك فعله هو الحصول على قفل إيقاظ باستخدام التابع ()navigator.wakelock.request، وبالتحديد للحصول على قفل إيقاظ للشاشة، يمكنك تمرير نص 'screen' إلى التابع، وتضيف مستمع الحدث ليُعلِمُك متى يُحرَّر قفل الإيقاظ. يمكن أن يحدث هذا مثلًا عندما تتغير قابلية ظهور النافذة. فيمكنك مثلًا عندما تصبح علامة التبويب ظاهرة مجددًا، أن تعاود طلب قفل الإيقاظ. let wakeLock = null; const requestWakeLock = async () => { wakeLock = await navigator.wakeLock.request('screen'); wakeLock.addEventListener('release', () => { console.log('Wake Lock was released'); }); console.log('Wake Lock is active'); }; const handleVisibilityChange = () => { if (wakeLock !== null && document.visibilityState === 'visible') { requestWakeLock(); } }; document.addEventListener('visibilitychange', handleVisibilityChange); document.addEventListener('fullscreenchange', handleVisibilityChange); وكعادة التحسين التدريجي، تُحمِّل شيفرة هذه الميزة فقط إذا كان المتصفح يدعم الواجهة البرمجية Wake Lock. if ('wakeLock' in navigator && 'request' in navigator.wakeLock) { import('./wake_lock.mjs'); } هذه الواجهة مستخدمة في Fugu Greetings لتطبيق ميزة إبقاء الشاشة يقظة إذا طلب المستخدم ذلك، يمكنك تجريب هذه الميزة عبر تفعيل مربع الاختيار Insomnia الذي يُبقي الشاشة يقظة عند ابتعادك عن التطبيق بدلًا من إيقاف تشغيلها. مربع اختيار إبقاء الشاشة يقظة. واجهة كشف التوقف في بعض الأحيان حتى إن كنت تحدق في الشاشة لساعات، يكون لا فائدة من هذا التحديق ولا تتوصل لأدنى فكرة حول ما يمكنك فعله في بطاقتك الترحيبية. تسمح الواجهة البرمجية Idle Detection التي تعمل في الخلفية Idle Detection API بالكشف عن توقف استخدام تطبيقك. على صعيد Fugu Greetings إذا كان المستخدم في وضع عدم استخدام التطبيق لفترة طويلة جدًا، فإنه يُعاد تعيين التطبيق إلى الحالة الابتدائية وتُمسح حاوية الرسم canvas. إذا أردت استخدام هذه الميزة لتطبيقاتك فعليك كخطوة أولى لاستخدام الواجهة Idle Detection أن تتأكد من منح المتصفح الإذن للكشف عن توقف استخدام التطبيق 'idle-detection'، وإلا فأنت بحاجة إلى طلب ذلك الإذن عبر التابع ()IdleDetector.requestPermission، لاحظ أن استدعاء هذه التابع يتطلب قبول من المستخدم. // 'idle-detection' تأكد من الحصول على الإذن const state = await IdleDetector.requestPermission(); if (state !== 'granted') { // Need to request permission first. return console.log('Idle detection permission not granted.'); } الخطوة التالية هي إنشاء كائن للكشف عن توقف المستخدم، تُسجِّل مستمع حدث يستمع إلى تغييرات استخدام المستخدم للتطبيق. حالات المستخدم هي أن يكون نشطًا أو متوقفًا، وحالات الشاشة هي أن تكون نشطة أو مقفلة، إذا كان المستخدم متوقف عن استخدام التطبيق، فستُمسح حاوية الرسم، ويمكن أن تعطي كائن الكشف فاصلًا زمنيًا للكشف قدره 60 ثانية مثلًا. const idleDetector = new IdleDetector(); idleDetector.addEventListener('change', () => { const userState = idleDetector.userState; const screenState = idleDetector.screenState; console.log(`Idle change: ${userState}, ${screenState}.`); if (userState === 'idle') { clearCanvas(); } }); await idleDetector.start({ threshold: 60000, signal, }); وكالعادة، لا تُحمِّل الشيفرة إلا بعدما تتأكد من دعم المتصفح لها. if ('IdleDetector' in window) { import('./idle_detection.mjs'); } في تطبيق Fugu Greetings، تُمسح حاوية الرسم عند تفعيل مربع الاختيار Ephemeral زائل ويكون المستخدم متوقف عن استخدام التطبيق لفترة طويلة جدًا. حاوية الرسم ممسوحة كون خيار Ephemeral مفعّل والمستخدم متوقف عن استخدام التطبيق لفترة طويلة. الخاتمة لقد كانت مغامرة رائعة شاهدنا خلالها العديد من واجهات برمجة التطبيقات في تطبيقٍ واحد. باستخدام التحسين التدريجي تذكر أن لا تجبر المستخدم أبدًا على تنزيل شيفرات ميزة لا يدعمها متصفحه. قد يبدو تطبيق Fugu Greetings مختلفًا قليلاً في كل متصفح نظرًا لعدم دعم جميع المنصات لجميع الميزات السابقة، ولكن الوظائف الأساسية للتطبيق موجودة في جميع المتصفحات ويتم تحسينها تدريجيًا وفقًا لإمكانيات كل متصفح. لاحظ أن هذه الإمكانيات قد تتغير حتى في نفس المتصفح، اعتمادًا على ما إذا كان التطبيق يعمل كتطبيق مثبت في الجهاز أو كعلامة تبويب في متصفح. نسخة Android من تطبيق Fugu Greetings في متصفح Chrome. نسخة سطح المكتب من تطبيق Fugu Greetings في متصفح Safari. نسخة سطح المكتب من تطبيق Fugu Greetings في متصفح Chrome. إذا كنت مهتمًا بتطبيق Fugu Greetings، يمكنك الاطلاع على شيفرته كاملةً على منصة GitHub وتشكيل بطاقتك الترحيبية باستخدامه. بتطبيق استراتيجية التحسين التدريجي في تطوير تطبيقاتك، تأكد أن جميع المستخدمين سيحصلوا على تجربة أساسية جيدة وقوية، لكن المستخدمين الذين يستعملون متصفحات تدعم واجهات برمجية لنظام الويب Web APIs أكثر، سيحصلون على تجربة أفضل لتطبيقاتك. ترجمة -وبتصرف- للمقال Progressively enhance your Progressive Web App من موقع web.dev. اقرأ أيضًا جعل تطبيق الويب التقدمي PWA يبدو كتطبيق أساسي في نظام التشغيل ما هي تطبيقات الويب التقدمية PWA؟ ميزات تطبيق الويب التقدمي PWA نماذج اقتراحات تثبيت تطبيقات الويب التقدمية PWA توفير تجربة تثبيت مخصصة داخل تطبيق الويب التقدمي PWA
  7. لفظ مصطلح تطبيق الويب التقدمي progressive web application -ويختصر إلى PWA- يُعَد إشاعة مدوِّية، فمن المراهن أن تطبيقات الويب التقدمية مجرد مواقع ويب، إذ يُقِر توثيق PWA في Microsoft بهذا، وموقع web.dev يتفق مع هذا الإعتبار، وحتى مرشّحَي PWA فرانسيس بيريمان Frances Berriman وأليكس راسل Alex Russell يقولا ذلك أيضًا. أجل، تطبيقات الويب التقدمية PWAs هي مجرد مواقع ويب، لكنها في الوقت نفسه أكثر من ذلك بكثير، إذا طوِّر تطبيق PWA على أكمل وجه، فلن يبدو وكأنه موقع ويب، بل سيبدو كتطبيق حقيقي real app، لكن ماذا يعني أنْ يبدو موقع الويب كتطبيق حقيقي؟ للإجابة على ذلك، دعنا نأخذ تطبيق Apple Podcasts كمثال، يتوفر هذا التطبيق على نظام macOS لبيئة سطح المكتب وعلى نظام iOS (ونظام iPadOS) لبيئة الهاتف المحمول، فيُعد تطبيق Podcasts أحد تطبيقات الوسائط، لكن زبدة الأفكار التي سنوضحها من خلاله في هذا المقال تنطبق أيضًا على أنواع أخرى من التطبيقات. تطبيق Apple Podcasts على iPhone وmacOS (المصدر) تحذير: يوجد لكل ميزة من ميزات التطبيق المدرجة في الأسفل (على أنظمة iOS وAndroid وسطح المكتب) فقرة كيف تفعل هذا في الويب، يمكنك قرائتها لمزيد من التفاصيل حول تطبيق الميزة في مواقعك. يرجى ملاحظة أن المتصفحات على أنظمة التشغيل المختلفة لا تدعم كلها جميع المميزات المدرجة هنا؛ لذا تأكد من مراجعة ملاحظات التوافقية بدقة ضمن المقالات المرتبطة. إمكانية التشغيل دون اتصال بالشبكة إذا راجعت النظر في بعض التطبيقات الخاصة بأنظمة التشغيل الأساسية سواء على هاتفك المحمول أو حاسوبك المكتبي، ستلاحظ أنك لا تحصل على أي محتوى أبدًا دون اتصال بالشبكة، لكن في تطبيق Podcasts، حتى لو كنت غير متصل بالشبكة، فهناك دائمًا محتوى يمكنك مشاهدته في التطبيق. لا يعرض قسم أفضل المصوَّرات top charts أي محتوى، ويعرض بدلاً من ذلك رسالة "لا يمكن الاتصال الآن" مقترنة بزر إعادة المحاولة. قد لا تكون هذه أفضل تجربة استخدام، ولكن يمكنك على الأقل قراءة شيء على الشاشة. تطبيق Podcasts دون اتصال بالشبكة. يتبع تطبيق Podcasts نموذج معماري يسمى هيكل التطبيق app shell model، كل المحتوى الثابت اللازم لعرض التطبيق الأساسي يخزّن مؤقتًا محليًا، بما في ذلك الصور المزخرفة مثل أيقونات القائمة اليسرى وأيقونات واجهة المستخدم الأساسية للمشغِّل. لا يُحمَّل المحتوى الديناميكي (مثل بيانات أفضل المصوَّرات) إلا عند الطلب، مع توفُّر محتوى احتياطي مخزن مؤقتًا محليًا في حالة فشل التحميل. يمكنك قراءة المقال The App Shell Model لمعرفة كيفية تطبيق هذا النموذج المعماري على تطبيق الويب الخاص بك. المحتوى متاح والوسائط قابلة للتشغيل دون اتصال لا يزال بإمكانك الانتقال إلى قسم التنزيلات والاستمتاع بحلقات البث الصوتي podcasts المحملة الجاهزة للتشغيل والمعروضة مع جميع بياناتها الوصفية مثل الصور والأوصاف وذلك أثناء عدم الاتصال بالشبكة، عبر الواجهة المسحوبة على اليسار. يمكن تشغيل حلقات البث الصوتي المحملة حتى دون اتصال بالشبكة. يمكن تقديم محتوى الوسائط المحملة مسبقًا من ذاكرة التخزين المؤقت cache، مثلًا باستخدام استراتيجية تخديم مقاطع الصوت والفيديو المخزنة مؤقتًا serve cached audio and video والتي تقدمها مكتبة Workbox. يمكن دائمًا تخزين المحتويات الأخرى في ذاكرة التخزين المؤقت أو في قاعدة البيانات المفهرسة IndexedDB. إذا كان لديك بيانات يجب تخزينها تخزينًا دائمًا دون التعرض لاحتمال زوالها عند انخفاض حجم الذاكرة المتاح؛ فيمكنك استخدام الواجهة البرمجية Persistent Storage API (التخزين الثابت) التي تعمل في الخلفية. خلفية تنزيل استباقية عندما تعاود الاتصال بالشبكة، يمكنك بالطبع البحث عن محتوى معين في التطبيق، مثلا تستعلم عن "HTTP 203" في خانة البحث، وبعد أن تظهر النتيجة، إذا اشتركت في أحد سلاسل حلقات "HTTP 203"، فإن أحدث حلقة من تلك السلسلة ستُنزّل مباشرة دون إذنك. بعد الاشتراك في سلسلة حلقات بث ما، تُنزّل أحدث حلقة منها مباشرة. تنزيل أحد حلقات بث ما قد يستغرق وقتًا طويلًا، لذا تتيح لك الواجهة البرمجية fetch التي تعمل في الخلفية Background Fetch API تفويض عمليات التنزيل إلى المتصفح الذي يعتني بها في الخلفية، وفي نظام Android، يمكن للمتصفح بدوره تفويض مهمة التنزيل هذه إلى نظام التشغيل، بالتالي لا يلزم إبقاء المتصفح مُشغّلًا، وبمجرد اكتمال التنزيل، يتم إيقاظ عامل خدمة تطبيقك service worker وبذلك يمكنك تحديد ماذا تفعل في الاستجابة. المشاركة والتفاعل مع التطبيقات الأخرى يتكامل تطبيق Podcasts بشكل طبيعي مع التطبيقات الأخرى، مثلًا عندما تنقر بزر الفأرة الأيمن فوق حلقة تعجبك؛ يمكنك مشاركتها مع تطبيقات أخرى على جهازك، مثل تطبيق الرسائل، كما إن تطبيق Podcasts يتكامل بشكل طبيعي مع حافظة النظام clipboard، إذ يمكنك النقر بزر الفأرة الأيمن فوق أي حلقة ونسخ رابطها إلى الحافظة. مشاركة حلقة بث على تطبيق الرسائل. كيف تفعل هذا في الويب؟ تتيح الواجهتان البرمجيتان Web Share وWeb Share Target (مشاركة وُجْهِة الويب) لتطبيقك مشاركة واستقبال النصوص والملفات والروابط من وإلى التطبيقات الأخرى على جهاز المستخدم. صحيح أنه لا يمكن لتطبيق ويب حتى اللحظة إضافة عناصر قائمة جديد إلى قائمة زر الفأرة الأيمن المضمنة في نظام التشغيل، إلا أن هناك عدة طرق أخرى للمشاركة من وإلى التطبيقات الأخرى على الجهاز، فباستخدام الواجهة البرمجية Clipboard غير المتزامنة يمكنك برمجيًا قراءة وكتابة النصوص والصور (صور PNG) إلى حافظة النظام. ويمكنك في نظام Android استخدام الواجهة البرمجية Contact Picker API التي تعمل في الخلفية لاختيار جهات اتصال معينة كمدخلات لأحد التطبيقات. إذا كانت أحد وظائف برنامجك تحتاج تشغيل أحد الخيارين: تطبيق خاص بنظام التشغيل الأساسي أو تطبيق PWA، وقتها يمكنك استخدام الواجهة البرمجية get installed related apps (جلب التطبيقات المثبتة المشابهة) للتحقق ما إذا كان تطبيق النظام الأساسي مثبتًا على حاسوب المستخدم أم لا، إذا كان مثبتًا بالفعل؛ فإنك لا تحتاج أن تعرض للمستخدم خيار تشغيل PWA من الأساس. تحديث التطبيق في الخلفية يمكنك إعداد التطبيق لتنزيل حلقات بث جديدة تلقائيًا عبر إعدادات تطبيق Podcasts، بهذا أنت لست مضطرًا حتى للتفكير في المستجدات، فالمحتوى المحدث سيكون دائمًا موجودًا لديك. إعداد Podcasts لمراقبة حلقات البث الجديدة كل ساعة. كيف تفعل هذا في الويب؟ تتيح واجهة المزامنة الدورية التي تعمل في الخلفية Periodic Background Sync API لتطبيقك بتحديث محتواه بانتظام في الخلفية دون الحاجة إلى أن يكون مشغَّلًا. هذا يعني أن المحتوى الجديد متاح استباقيًا، بالتالي يمكن لمستخدمي تطبيقك الغوص فيه مباشرة متى شاؤوا. الحالة متزامنة عبر السحابة يتم مزامنة اشتراكاتك عبر جميع أجهزتك في ذات الوقت وفي عالم سلس، فلا داعي للقلق بشأن مزامنة اشتراكاتك في حلقات البث يدويًا. وبالمثل، لا داعي للخوف من أن تُستهلك ذاكرة هاتفك المحمول بسبب حلقات البث التي استمعت إليها على سطح المكتب والعكس، إذ تُزامن حالة تشغيل كل حلقة وتُحذف حلقات البث المستَمع إليها تلقائيًا. الحالة متزامنة عبر التخزين السحابي. يمكنك تفويض مهمة مزامنة حالة بيانات التطبيق إلى الواجهة البرمجية Background Sync API التي تعمل في الخلفية. لا يجب أن تتم مزامنة المستخدم مباشرة عندما يفتح التطبيق للمرة الأولى، بل تمامًا في نهاية زيارته، وحتى ربما يُستحسن أن تتم مزامنته عندما يغلق التطبيق أو ينتهي من استخدامه للمرة الثانية. التحكم في التطبيق عبر أزرار الجهاز عندما تكون مشغولاً بتطبيق آخر، مثلًا قراءة صفحة أخبار في متصفح Chrome، لا يزال بإمكانك التحكم في تطبيق Podcasts باستخدام مفاتيح الوسائط في حاسوبك المحمول؛ فلست مضطرًا للانتقال إلى التطبيق فقط من أجل تمرير حلقة البث للأمام أو للخلف. التحكم بتطبيق Podcasts عبر مفاتيح الوسائط (المصدر). تدعم الواجهة البرمجية media session (جلسة الوسائط) التي تعمل في الخلفية Media Session API أزرار الوسائط في الجهاز، بهذا يمكن للمستخدمين الاستفادة من أزرار وسائط الأجهزة ضمن لوحات المفاتيح أو سماعات الرأس أو حتى أزرار وسائط البرامج في ساعاتهم الذكية، للتحكم في تطبيق الويب. تعدد المهام واختصارات التطبيق يمكنك دائمًا القيام بمهام متعددة والعودة إلى Podcasts من أي تطبيق كنت فيه بدون مشاكل، ويوجد للتطبيق اختصار مميز وواضح يمكنك وضعه على سطح المكتب أو شريط تطبيقاتك dock لتشغله مباشرة عندما ترغب باستخدامه. تنفيذ مهام متعددة والرجوع لتطبيق Podcasts. يمكن تثبيت تطبيقات الويب التقدمية على كلًا من سطح المكتب وشاشة الهاتف المحمول الرئيسية أو قائمة البدء start menu أو شريط dock، ويمكن أن يحدث التثبيت بناءً على طلب مسبق من المستخدم، أو يتحكم مطور التطبيق فيه بالكامل. يغطي مقال ما الذي يلزم التطبيق ليكون قابلًا للتثبيت؟ كل ما تحتاج إلى معرفته بهذا الخصوص. عند القيام بمهام متعددة، تبدو تطبيقات الويب التقدمية مستقلة عن المتصفح. إدراج إجراءات التطبيق الشائعة ضمن قائمة مختصرة يمكن تنفيذ إجراءات التطبيق الأكثر شيوعًا (اختصارات التطبيق) مثل البحث عن محتوى جديد ومراقبة حلقات البث الجديدة؛ مباشرة من قائمة اختصار التطبيق في شريط dock، كما يمكنك فتح التطبيق تلقائيًا عند تسجيل الدخول للنظام وذلك عبر قائمة خيارات. الإجراءات السريعة متاحة مباشرة من أيقونة التطبيق. قد تتساءل، كيف يمكن فعل ذلك مع تطبيق ويب؟ يمكنك من خلال تحديد اختصارات التطبيق في ملف بيان موارد تطبيق الويب التقدمي manifest تسجيل مسارات سريعة للمهام الشائعة الممكن للمستخدمين الوصول إليها مباشرة من أيقونة التطبيق. في أنظمة التشغيل مثل macOS، يمكن للمستخدمين أيضًا النقر بزر الفأرة الأيمن فوق أيقونة التطبيق وتعيين التطبيق ليعمل فور تسجيل الدخول للنظام run on login. التصرف كتطبيق افتراضي يمكن أن تتكامل تطبيقات iOS المختلفة مع تطبيق Podcasts وحتى مواقع الويب و تطبيقات البريد الإلكتروني، وذلك من خلال البروتوكول podcasts://‎، فإذا كنت في المتصفح وتوجهت لرابط مثل podcasts://podcasts.apple.com/podcast/the-css-podcast/id1042283903 فستُنقل مباشرة إلى تطبيق Podcasts ويمكننك المتابعة معه بشكل طبيعي، تشترك بحلقات بث جديدة أو تستمع لحلقات سابقة. فتح تطبيق Podcasts مباشرة من خلال المتصفح. كيف يعمل هذا الويب؟ لا يمكن معالجة بروتوكولات مخصصة من الروابط حتى اللحظة، لكن هناك عمل مستمر لاقتراح معالجة بروتوكول URL لتطبيقات الويب التقدمية. حاليًا تُعد الدالة registerProtocolHandler مع البادئة web+‎ الخيار الأمثل لفعل ذلك. تكامل نظام الملفات المحلي قد لا تلمَح مباشرة أن تطبيق Podcasts يتكامل بشكل طبيعي مع نظام الملفات المحلي. يَحفظ تطبيق Podcasts بياناته في المسار التالي على حاسوبك: ~/Library/Group Containers/243LU875E5.groups.com.apple.podcasts وتمثِّل ملفات التخزين المؤقت cache قسم التنزيلات أو المحتوى المتاح بلا اتصال، وذلك في المسار التالي: ~/Library/Group Containers/243LU875E5.groups.com.apple.podcasts/Library/Cache حلقات البث مخزنة في مجلد خاص بالتطبيق في نظام. في الويب، تمكِّن الواجهة البرمجية File System Access (الوصول إلى ملفات النظام) المطورين من الوصول إلى نظام الملفات المحلي للجهاز. يمكنك استخدام هذه الواجهة مباشرة أو عبر مكتبة browser-fs-access التي توفر للمتصفحات وصولًا احتياطيًا لملفات النظام. لأسباب تتعلق بالأمان لا يمكن الوصول لمجلدات النظام عبر الويب. شكل المنصة ومظهرها هناك شيء جلي وواضح بالنسبة لتطبيقات iOS الشبيهة بتطبيق Podcasts، وهو أنه لا يمكن تحديد العناوين labels ونوع الخط فيها هو نفس نوع خط نظام الجهاز، كما يؤخَذ اختيارك لتعيين مظهر التطبيق (الوضع الليلي والنهاري) بعين الاعتبار. تطبيق Podcasts يدعم الوضعين المضيء والمظلم. يستخدم التطبيق نوع الخط الافتراضي للنظام. في الويب، يمكنك حماية عناصر واجهة المستخدم من التحديد عن طريق الخطأ بالاستفادة من خاصية user-select في css بالقيمة none، لكن مع ذلك، تأكد من عدم إساءة استخدام هذه الخاصية وجعل محتوى التطبيق غير قابلة للتحديد، إذ يجب استخدام هذه الخاصية فقط لعناصر واجهة المستخدم مثل نصوص الأزرار وما شابه. تمكنك القيمة system-ui للخاصية font-family من استخدام خط واجهة المستخدم الافتراضي للنظام في تطبيقك. أخيرًا يمكن أن يُعرض تطبيقك بمظهر المستخدم المفضل من خلال الاعتناء بخيار prefers-color-scheme (نظام الألوان المفضل) للمستخدم مع مفتاح اختياري لتبديل المظهر من مضيء إلى مظلم. الشيء الآخر الذي يجب اتخاذ قرار بشأنه هو ما يجب على المتصفح فعله عند اقتراب الوصول إلى نهاية شريط التمرير، مثلًا تنفيذ سحب للتحديث، وذلك ممكن عبر خاصية overscroll-behavior. شريط عنوان مخصص ستلاحظ بالنظر إلى نافذة تطبيق Podcasts أن تجربة تصميم التطبيق شبيهة بتجربة نافذة متصفح Safari، إذ لا يحتوي Podcasts على شريط عنوان كلاسيكي متكامل ولا شريط أدوات، وإنما لديه تجربة مخصصة تبدو كشريط جانبي مثبت في نافذة المشغل الرئيسية. أشرطة العنوان المخصصة لكلًا من Safari و Podcasts. في الويب، يمكنك ضبط خاصيتَي display و theme-color في ملف بيان تطبيق الويب لتحديد شكل وأسلوب عرض نافذة تطبيقك وتحديد أيٍ من عناصر تحكم المتصفح الافتراضية يجب أن يظهر. حركات سلِسة الحركات داخل Podcasts سريعة وسلسة، مثلًا عندما تفتح واجهة ملاحظات حلقة البث المسحوبة على اليمين؛ فإنها تنزلق بانسيابية، وعندما تحذف حلقة من تنزيلاتك، فإن الحلقات المتبقية تطفو لأعلى وتستغل مساحة الشاشة المتحررة من الحلقة المحذوفة. الحركات داخل Podcasts مثل الفتح السلس للواجهة المسحوبة. يمكنك بالتأكيد إدخال الحركات في تطبيقات الويب إذا كنت تأخذ بعين الاعتبار عددًا من النصائح الموضحة في مقال الحركات في تصميم الويب: لماذا نستخدمها ومتى؟، كما ستجد بعضًا من المصادر الملهمة لإنشاء الحركات في مقال مدخل إلى الحركات. يمكن تحسين حركات التمرير الشائعة في المحتوى المقسم إلى صفحات مرقمة paginated أو في الوسائط متكررة العرض carousels؛ باستخدام ميزة تمرير css السلس CSS Scroll Snap، أو يمكنك استخدام الواجهة البرمجية Web Animations لتحكم كامل فيها. ظهور المحتوى خارج التطبيق يمكن لتطبيق Podcasts على iOS عرض المحتوى في أماكن أخرى غير التطبيق نفسه، مثلًا في عرض ودجات النظام Widgets، أو في اقتراحات مُساعد Apple الشخصي Siri، فوجود عبارات استباقية تحث المستخدم على اتخاذ إجراء ما وتتطلب فقط نقرة للتفاعل معها يمكن أن يؤدي إلى زيادة معدل الاندماج بالتطبيق بشكل كبير. ظهور محتوى التطبيق خارج تطبيق Podcasts الرئيسي. تسمح الواجهة البرمجية Content Indexing (فهرسة المحتوى) التي تعمل في الخلفية Content Indexing API بإخبار المتصفح بمحتوى تطبيق الويب التقدمي المتاح دون اتصال، ما يتيح للمتصفح إظهار ذلك المحتوى خارج التطبيق الرئيسي. يمكنك مساعدة محركات البحث والمساعدين الافتراضيين مثل Google Assistant في تقديم ما يعرضه موقعك بشكل مثالي من خلال تحديد المحتوى المثير للاهتمام في تطبيقك باعتباره مناسب أن يُعاد كصوت يمكن التحدث به speakable، وأيضًا باستخدام البيانات المُهيكلية structured-data في موقعك. وِدجت شاشة القفل للتحكم في وسائط التطبيق يعرض Podcasts عند تشغيل حلقة بث، أداة تحكم رائعة في شاشة القفل تحتوي على بيانات وصفية مثل صورة الحلقة وعنوانها واسمها. التحكم في الوسائط المشغلة في التطبيق من شاشة القفل. في الويب، تتيح لك الواجهة البرمجية Media Session API التي تعمل في الخلفية تحديد البيانات الوصفية مثل صورة العنصر وعنوانه، وما إلى ذلك، لتُعرض بعد ذلك في شاشة القفل، أو شاشة الساعات الذكية، أو أدوات الوسائط في المتصفح. دفع الإشعارات أصبح طلب الحصول على الإشعارات في الويب أقل هذه الأيام، فالإشعارات الفورية إلى حد ما تعتبر مصدر إزعاج في الويب، لكن إذا استُخدمت بشكل صحيح، فيمكن أن تضيف قيمة كبيرة. مثلًا يمكن لتطبيق Podcasts إشعارك اختياريًا بحلقات البث الجديدة من السلاسل التي اشتركت فيها أو ينصحك بالمشاركة بسلاسل بث جديدة، أيضًا إشعارك بميزات التطبيق الجديدة. إرسال التطبيقات إشعارات فورية لإبلاغ المستخدم بالمحتوى الجديد. يمكنك فعل ذلك في الويب عبر الواجهة البرمجية Push API التي تعمل في الخلفية إذ تتيح لتطبيقك بتلقي الإشعارات حتى تتمكن من إشعار المستخدمين بالأحداث الملاحظة حول تطبيق الويب التقدمي، وبالنسبة للإشعارات التي يجب إطلاقها في وقت محدد مستقبلًا والتي لا تتطلب اتصالاً بالشبكة فيمكنك استخدام الواجهة البرمجية Notification Triggers API (مُطْلِق الإشعارات) التي تعمل في الخلفية. شارات أيقونة التطبيق عندما تكون هناك حلقات بث جديدة متاحة في إحدى سلاسل البث التي اشتركت فيها، تظهر شارة على أيقونة Podcasts في الشاشة الرئيسية، ما يشجعك على فتح التطبيق بطريقة غير تطفلية لمشاهدة المستجدات. تُعد الشارات طريقة ماكرة لشد المستخدم على فتح التطبيق. في الويب، يمكنك تعيين شارات أيقونة التطبيق باستخدام الواجهة البرمجية Badging API، فهذا مفيد بشكل خاص عندما يكون لدى تطبيق الويب التقدمي مفهوم "غير مقروء" unread للعناصر الجديدة التي لم يفتحها المستخدم، أو بشكل عام عندما تحتاج وسيلة لاستدراج المستخدم إلى التطبيق بشكل غير ملحوظ. أسبقية تشغيل الوسائط على إعدادات توفير الطاقة قد تُطفَأ الشاشة في نُسخ سطح المكتب من Podcasts عند تشغيل الوسائط وترك التطبيق مشغل فترة طويلة دون تفاعل لكن لا يدخل النظام في وضع السكون sleep، كما يمكن لنُسخ الهواتف الذكية من Podcasts إبقاء الشاشة فعّالة اختياريًا، مثلًا لعرض كلمات المقطع المشغل أو تسميته التوضيحية. تبقي التطبيقات الشاشة فعالة. في الويب، تتيح لك الواجهة البرمجية Screen Wake Lock API (قفل إيقاظ الشاشة) التي تعمل في الخلفية إمكانية منع إيقاف تشغيل الشاشة لسبب ما، وبالنسبة للويب فإن تشغيل الوسائط يمنع نظام التشغيل تلقائيًا من الدخول في وضع السكون sleep. استكشاف التطبيق في متجر التطبيقات يعد تطبيق Podcasts جزءًا من تجربة سطح مكتب نظام macOS، لكن على نظام iOS فيجب تثبيته من متجر App Store. بالبحث السريع عن "podcast" أو "podcasts" أو"apple podcasts" فإن Podcasts يبرز فورًا في متجر التطبيقات. تعلَّم المستخدمون استكشاف التطبيقات في متاجر التطبيقات. لا تسمح Apple بنشر تطبيقات ويب تقدمية في متجر App Store، لكن في نظام Android يمكنك تغليف تطبيق الويب التفاعلي الخاص بك في فاعليِّة ويب موثوق Trusted Web Activity (حاوية container تجعله يتصرف كتطبيقات الهواتف المعيارية). يجعل سكربت bubblewrap عملية رفع تطبيق PWA على المتجر أمرًا يسيرًا عبر واجهة سطر الأوامر، وهو أيضًا من يُشغِّل داخليًا ميزة تصدير تطبيقات Android عبر الأداة PWABuilder أو باني تطبيق PWA، الممكن استخدامها لرفع التطبيقات على المتجر دون الاستعانة بسطر الأوامر. الخلاصة تطوّرت تطبيقات الويب التقدمية PWAs كثيرًا منذ ظهورها عام 2015، ويعمل فريق Chromium (المتصفح الحر الذي بني عليه متصفح كروم) الآن في سياق مشروع Fugu على سد النقص فيها. باتباعك على الأقل بعضًا من نصائح هذا المقال، يمكنك الاقتراب شيئًا فشيئًا من هيئة ومظهر يشبه التطبيق الأساسي المثبت في أي نظام تشغيل مما يُنْسِي مستخدميك أنهم يتعاملون مع موقع ويب، لأن معظمهم لا يهتم بصراحة بكيفية إنشاء تطبيقك (ولماذا يجب عليهم ذلك؟)، طالما هو يبدو كتطبيق حقيقي. ترجمة -وبتصرف- للمقال Make your PWA feel more like an app من موقع web.dev اقرأ أيضًا ما هي تطبيقات الويب التقدمية PWA؟ ميزات تطبيق الويب التقدمي PWA نماذج اقتراحات تثبيت تطبيقات الويب التقدمية PWA توفير تجربة تثبيت مخصصة داخل تطبيق الويب التقدمي PWA النسخة العربية الكاملة من كتاب أنظمة التشغيل للمبرمجين
  8. اهلا بك، في البداية سيكون الامر صعب قليلا.. خاصة في مجال مثل التصميم؛ التنافس على مشاريعه عالي لكن مع مرور الوقت ستاخذ مشاريع اكثر.. لا اقولك انه تعتمد عليه كمصدر دخل اساسي ولا حتى مستقبلا... لكن مصدر دخل مساعد ممكن المهم.. معرض الاعمال.. ضع فيه تصاميم تراها شائعة الطلب.. ضع فيه تصاميم احترافية تميزك عن باقي المصممين.. ثم عندما تقدم عرض اشر الى معرض اعمالك وتقييماتك.. في البداية ستمل من كثرة تقديم العروض.. لكن اذا اثبت جدارتك في المشاريع الاولى التي تاخذها فمع الوقت ستكون زبائن مستمرين بالتوفيق
  9. أهلًا بك، يمكن ذلك من خلال اعدادات الفيس او من خلال برامج مخصصة للرد التلقائي في فيسبوك.. سواء الرد التلقائي على التعليقات أو على رسائل الماسينجر .. يمكنك تعلمها فقط بعد قراءة مقال يشرح ذلك أو فيديو ساترك لك مصادر تفيدك وقد تجد مصادر أخرى تفيدك من خلال البحث في غوغل https://www.youtube.com/watch?v=P5igzc3WC70
  10. أهلًا بك @ريان مدشل أي طالب يسجل في أحد الدورات يُرسل له رابط دروس الدورة على الإيميل الذي سجل به.. بهذا يمكنك تذكر عنوان الايميل الذي سجلت به.. أما بالنسبة لكلمة المرور التي ربما يكون نسيانك لها هو سبب المشكلة؛ فيمكنك انشاء كلمة مرور جديدة عند فشل تسجيل الدخول.. في صفحة تسجيل الدخول تضغط على "فقدت كلمة المرور" ضمن خيارات المساعدة أسفل زر "دخول" إذا لا تزال المشكلة موجودة فأرجو أن تتواصل مع مركز المساعدة من خلال فتح تذكرة من هنا بالتوفيق
  11. أهلًا بك، ال index الخاص بال anchor الثاني هو 1 وبناء على ذلك.. سنجلب أولًا جميع ال anchors في الصفحة ثم نغير نص ال anchor الثاني <script> var anchors=document.getElementsByTagName('a'); anchors[1].innerHTML="anchor two"; </script>
  12. أهلًا بك، بالطبع.. الأقسام الباقية ستكون تطبيق عملي بحت.. فهمت انه متاح لك فقط المسار الاول (مسار الأأساسيات) .. على العموم يمكنك الاندماج مع الجزئية العملية الموجودة في آخر قسم من مسار الأساسيات (جزئية بناء الموقع الشخصي)؛ قس على ذلك باقي الدروس في الأقسام القادمة لكن على مستوى تقني أعلى نظرًا لاننا قد انتهينا من الاساسيات بالتوفيق
  13. وعليكم السلام @عبد الحميد مل العود أرجو أن تتأكد أن بعض الأعمدة أسفل الفيديو لونها أبيض.. هذا يعني أن الصوت أعلى من صفر.. إذا كانت جميعها رمادية فهذا يعني أن الصوت مكتوم من الفيديو نفسه.. وفي هذه الحالة يمكنك تشغيله من خلال خلال الضغط على أكثر من عمود منهم فيرتفع الصوت .. او اضغط عليهم جميعًا فيكون الصوت أعلى ما يمكن ثم اضبط التخفيض من حاسوبك نفسه إذا لا تزال المشكلة موجودة أو واجهت مشكلة أخرى فأرجو أن تعلق للمدرسين أسفل الفيديو نفسه فيردوا عليك بشكل أسرع من هنا
  14. أهلًا بك، للتو جربت فتح صفحة إنشاء تذكرة من خلال هذا الرابط ولم اواجه أي مشكلة ربما المشكلة المتصفح لديك.. جرب تحديث الصفحة و حذف الكاش (ctrl+sgift+delete) و إيقاف إضافات المتصفح (من قائمة الإعدادات ثم more tools ثم extentions ثم أوقف جميع الإضافات) أو جرب متصفح آخر (موزيلا أوبيرا edge) أو حتى جهاز آخر (الهاتف الذكي الخاص بك)
  15. أهلًا بك، يمثِّل العنصر <meta> البيانات الوصفية التي لا يمكن تمثيلها باستخدام عناصر HTML المتعلقة بالبيانات الوصفية مثل <base> أو <link> أو <script> أو <style> أو <title>. للمزيد أنصحك بقراءة توثيق هذا العنصر من موسوعة حسوب لا يوجد شيئ اسمه افضل لغة برمجة.. وإنما هناك مجالات برمجة مختلفة لكل مجال لغات برمجة محددة وعادة تتميز احدى اللغات على اخواتها ضمن نفس المجال أرجو أن تقرأ عن مجالات البرمجة جميعًا ثم تقرأ عن لغات برمجة كل مجال ثم تنظر أيها مناسب أكثر لك من حيث انتشارها او سهولة تعلمها او طلبها في السوق او اي معايير اخرى تهمك لاحظ الإجابة على اسئلة مشابهة وردتنا
  16. أهلًا بك، في الواقع ليس لدي خبرة ب Unity لكن لا يوجد مسافة فارغة بين كلمة Log والقوس يعني جرب غيرها للتالي ممكن أن يزول الخطأ Debug.Log("Hi");
  17. اهلا بك، في الواقع لا يوجد وقت محدد متوقع من طالب ان ينهيها فيه ولا حتى وقت تقديري.. ممكن اقول لك 4 اشهر .. ربما تنهيها انت باقل من ذلك بكثير.. او ربما تكون ممكن يحبون المراجعة كل فترة فتنهيها باكثر من ذلك بكثير.. الامر يعتمد على عدة عوامل اولها مستواك التقني والخلفية المسبقة.. اذا لم يكن لديك اي خلفية ربما تحتاح الى توقيف الفيديو اكثر من مرة بالتالي وقت اكثر.. وايضا التفرغ.. بعض الطلاب لديهم مشاغل اخرى تكون بالنسبة لهم اولويات اعلى من متابعة الدورة.. طبعا عزيمة الطالب شيى مهم جدا ايضا اخيرا .. العبرة ليس في الكم وانما في الكيف.. يعني طالب يتخرج بعد 6 شهور متقن لكل ما تعلمة عمليا افضل من من يتخرج خلال شهر وهو لا يزال ضعيف ولم يكن يطبق مع الفيديو اذن الاجابة على سؤالك؛ ان لا تكترث بشان هذا السؤال.. ضع عزيمة .. ولا تخرج من اي درس الا وتكون طبقت كلشي عمليا ونجح معك.. كل التوفيق
  18. أهلًا بك، أجل يمكنك تحميل لعبه PES لنظام macOSX من هنا يمكنك تحميل لعبة Fifa20 لنظام macOSX من هنا و Fifa19 من هنا
  19. أهلًا بك، لا تقلق أغلب الطلاب الذين يسجلون في دورات الأكاديمية يكونون مثلك تمامًا ليس لديهم أي خلفية عن البرمجة.. لكن مع البدء يلاحظون أن الدورة تبدأ معهم من الصفر الآن ابدأ بالثلاث فيديوهات ضمن قسم مقدمة الدورة.. فهذا القسم يعرفك عن الدورة ويدمجك بالتدريج مع لغة javascript .. وهذا القسم ليس طويل وأنما كلة على بعضه حوالي 10 دقائق.. أرجو أن تشاهد الفيديوهات الثلاثة هناك بما أنك أصبحت من طلاب الدورة فيمكنك نشر أي شيئ تراه معقد في خانة التعليقات المخصصة للطلاب أسفل كل فيديو.. ونحن المدرسون متواجدون على مدار الساعة تقريبًا للإجابة على أسئلة الطلاب هناك يمكنك البدء الآن .. الشئ الذي تراه معقد بعض الشيئ.. أوقف الفيديو.. ان كان موجود تعليقات لطلاب سابقين فهم زملاءك في الدورة يمكنك قراءه تعليقاتهم وردود المدرسين عليهم.. ربما تكون أسئلتهم قد خطرت في بالك.. ثم أ{جع الفيديو للدقيقة التي لم تفهمها وكرر مشاهدتك لها عدة مرات وتابع.. إذا انتهى الدرس ولا يزال لديك سؤال مهم أو شيء لم تفهمه.. يمكنك نشر تعليق ونحن سنجيبك .. مرة على المرة ستعتاد على لغة البرمجة وطريقة الشرح كل التوفيق
  20. أهلًا بك@محمد عبد الصمد ابدأ من بداية الدورة.. سيرسَل لك رابط الدورة على الايميل أو يمكنك البدء بقسم المقدمة من أول مسار "أساسيات لغة JavaScript" من هنا بالتوفيق
  21. أهلًا بك، قبل التفكير بالدورة عليك التفكير بأي مجال تريد أن تبدع.. لذلك أنصحك بالقراءة عن مجالات البرمجة ثم اخيار أحدها ثم التعرف على لغات برمجة هذا المجال وبعد ذلك البدء بتلك اللغة أنصحك بقراءة الإجابة عن سؤالك الذي تكرر كثيرًا لدينا
  22. تواصل مع الدعم الفني واوصف لهم مشكلتك بالتحديد وهم سيخبروك ما المشكلة اذا كانت منك او من البنك
  23. إعداد واجهات قالب ذلك الموقع وتنسيق صورها وحركاتها والانتقالات بينها والأوان.. تتعلمه في دورة تطوير واجهات المستخدم تفعيل عمليات الشراء وإنشاء حسابات للمستخدمين وتخزين البيانات وإجراء المعاملات وضبط نظام الخصوصية والتحكم في منتجات الموقع؛ يتم من خلال البرمجة الخلفية بأي لغة برمجة ويب تتعامل مع قواعد البيانات مثل ruby أو PHP أو JavaScript (بالتحديد js تستخدم في برمجة الواجهات والخلفية أيضًا).. نظام الموقع الذي عرضته مبني بلغة php ونظام ووردبريس .. لكن يمكن بناء مثله تمامًا باستخدام البرمجة الخاصة يعني بالتخلي عن ووردبريس.. فقط بلغة البرمجة الخلفية وإطار عملها.. يعني مثلًا باستخدام php المتمثلة في إطار الويب الأكثر شهرةً laravel .. أو لغة js المتمثلة في بيئة العمل nodejs أولغة ruby المتمثلة في إطار عمل الويب الأكثر شهرة rails.. أو لغات أخرى بدون أنظمة مساعدة (بدون ووردبريس) يعني واجهات المواقع هي عالم آخر منفصل عن البرمجة الخلفية للمواقع.. وغالبًا يفضل المطورون تخصيص أنفسهم.. اما مطوري فرونتإند يكون لديهم حس التصميم غالب على حس البرمجة فيمشون في مجال تطوير الواجهات باستخدام html css js وتقنيات فرونتإند اخرى ويستعينون بمطور خلفية ليكمل البرمجة على هذه الواجهات التي يبنونها.. أو التخصص كمطوري خلفية يحبون التعامل مع البرمجة الخلفية وقواعد البيانات أكثر .. فيمشون في مجال الباكإند دورات الباكإند في الأكاديمية هي التالي دورة تطوير تطبيقات الويب باستخدام لغة php . دورة تطوير تطبيقات الويب باستخدام لغة js .. دورة تطوير التطبيقات باستخدام لغة ruby
  24. أهلًا بك، متسلسلة مقلوب المضروب يمكن حلها كالتالي // A simple C++ program to compute sum of series 1/1! + 1/2! + .. + 1/n! #include <iostream> using namespace std; // Utility function to find int factorial(int n) { int res = 1; for (int i=2; i<=n; i++) res *= i; return res; } // A Simple Function to return value of 1/1! + 1/2! + .. + 1/n! double sum(int n) { double sum = 0; for (int i = 1; i <= n; i++) sum += 1.0/factorial(i); return sum; } // Driver program to test above functions int main() { int n = 5; cout << sum(n); return 0; } ال figure يمكن حلها كالتالي #include <stdio.h> int main() { int i, j, N; printf("Enter N: "); scanf("%d", &N); for(i=1; i<=N; i++) { // Logic to print numbers for(j=i; j>=1; j--) { printf("%d", j); } printf("\n"); } return 0; }
×
×
  • أضف...