لم نعد نعمل بنشاط على تطوير ميزات لهذا التطبيق. سيتم قبول العلاقات العامة لإصلاح الأخطاء والترجمات وتحديثات المحتوى. يتم تطوير الميزات النشطة على https://github.com/zooniverse/front-end-monorepo/
لتجنب الاضطرار إلى تثبيت Node.js أو أي تبعيات أخرى، يمكنك تشغيل كل شيء باستخدام Docker وDocker Compose.
سوف يقوم docker-compose build
ببناء صورة Docker محلية وتشغيل npm ci
. قم بتشغيل هذا كلما قمت بتغيير التبعيات في package.json
.
يقوم docker-compose up
بتشغيل خادم ويب للتطوير يستمع على المنفذ 3735.
يقوم docker-compose down
بإيقاف خادم التطوير.
docker-compose run --rm shell
يبدأ حاوية تقوم بتشغيل Shell على سبيل المثال. لتشغيل الاختبارات.
تأكد من أن لديك Node 8 و npm
5 أو أحدث. يوصى بإدارة عمليات تثبيت Node الخاصة بك باستخدام nvm .
يقوم npm ci
بتثبيت التبعيات.
npm start
ببناء الموقع وتشغيله محليًا.
npm ci --legacy-peer-deps
. راجع العدد 6155 للمزيد من التفاصيل.
تتم إعادة توجيه الجذر /
إلى www.zooniverse.org لأن تطبيق الواجهة الأمامية هذا لم يعد مستخدمًا للصفحة الرئيسية. قم بتوجيه متصفحك إلى مسار فرعي لعرض هذا التطبيق قيد التشغيل محليًا.
افتح متصفح الويب الذي تختاره وانتقل إلى https://localhost:3735/lab
إذا كنت تريد تسجيل الدخول عبر Panoptes API وعرض الصفحات المعتمدة، فستحتاج إلى إعداد https://local.zooniverse.org:3735/lab
واستخدامه بدلاً من استخدام localhost:3735. وإلا فسوف تواجه أخطاء CORS. (تحتاج إلى إضافة اسم المضيف إلى ملف المضيفين الخاص بك، مع الإشارة إلى الملف المحلي. التعليمات موجودة على Stackoverflow الخاص بنا.)
استكشاف الأخطاء وإصلاحها: يقوم متصفح الويب بحظر موقع الويب المحلي
المشكلة: عند محاولة عرض المضيف المحلي:3735 أو local.zooniverse.org:3735، يوقفني متصفح الويب ويظهر شاشة تحذير.
أمثلة على الأخطاء: "اتصالك ليس خاصًا / NET::ERR_CERT_AUTHORITY_INVALID" في Chrome 104؛ "تحذير: مخاطر أمنية محتملة في المستقبل" على Firefox 103؛ "هذا الاتصال ليس خاصًا" في Safari 15.4.
السبب: خادم الويب المحلي يقوم بتشغيل HTTPS، ويستخدم شهادة موقعة ذاتيًا. تعتبر متصفحات الويب الحديثة هذه الشهادات غير جديرة بالثقة على الإطلاق، ومؤشرًا محتملاً لهجوم رجل في الوسط.
الحل (الحلول):
thisisunsafe
) في أي مكان على النافذة لتجاوز التحذير مؤقتًا؛ أويمكن تكوين التطبيق باستخدام متغيرات البيئة التالية:
NODE_ENV
- يضبط بيئة التعليمات البرمجية، ويحدد ما إذا كان سيتم تطبيق أي تحسينات إنتاجية على التعليمات البرمجية المجمعة، وأي مجموعة من الإعدادات الافتراضية التي سيتم تطبيقها، على سبيل المثال، عنوان url لمضيف واجهة برمجة التطبيقات، وعنوان url لمضيف Talk وما إلى ذلك.PANOPTES_API_APPLICATION
- يضبط معرف التطبيق لاستخدامه عند تقديم طلبات المصادقة إلى Panoptes API. الإعدادات الافتراضية التي تم تعيينها بواسطة NODE_ENV
.PANOPTES_API_HOST
- يضبط عنوان URL لمثيل Panoptes API. الإعدادات الافتراضية التي تم تعيينها بواسطة NODE_ENV
.STAT_HOST
- يقوم بتعيين عنوان URL لمثيل Stats API. الإعدادات الافتراضية التي تم تعيينها بواسطة NODE_ENV
.SUGAR_HOST
- يضبط عنوان URL لمثيل Sugar API. الإعدادات الافتراضية التي تم تعيينها بواسطة NODE_ENV
.TALK_HOST
- يضبط عنوان URL لمثيل Talk API. الإعدادات الافتراضية التي تم تعيينها بواسطة NODE_ENV
. scripts
package.json
؛ ولتجاوزها، ستحتاج إلى تعديل package.json
.NODE_ENV
، راجع config.js
في panoptes-javascript-client.سيتم تنظيم علاقات عامة جديدة لـ GitHub من داخل منظمة Zooniverse بواسطة Jenkins كجزء من عملية CI. بمجرد انتهاء CI، يجب أن يتم تنظيم التغييرات على https://pr-{PR-Number}.pfe-preview.zooniverse.org. في بعض الأحيان تنتهي مهلة جينكينز قبل الانتهاء من البناء. إذا فشل إنشاء العلاقات العامة، استخدم الرابط إلى Jenkins (من علاقاتك العامة) لتسجيل الدخول ومحاولة إعادة تشغيل البناء.
للاختبار باستخدام بيانات الإنتاج، يمكنك إضافة env=production
إلى عنوان url للتطوير الخاص بك، على سبيل المثال localhost:3735/projects?env=production
. لاحظ أنه تتم إزالته عند كل تحديث للصفحة.
كل الأشياء الجيدة موجودة في ./app . ابدأ من ./app/main.cjsx
لقد قمنا بمقارنة كود JavaScript الخاص بنا بنسخة معدلة من دليل أسلوب AirBnB. يرجى فحص تغييراتك باستخدام eslint، باستخدام ملف .eslintrc الموجود في جذر هذا الريبو. إذا كانت لديك أي أسئلة، فلا تتردد في طرحها علينا على GitHub.
أثناء التحرير، ابذل قصارى جهدك لمتابعة أسلوب التصميم والهندسة المعمارية المستخدمة بالفعل في المشروع. قاعدة التعليمات البرمجية كبيرة، وقد تطورت الأنماط أثناء تطويرها. قم بإلقاء نظرة على Zooniverse/front-end-monorepo للحصول على فكرة عن اتفاقياتنا لتنظيم المكونات.
جرب npm ci
لتحديث تبعياتك. وقراءة التحذيرات، يجب أن تخبرك إذا كنت تستخدم الإصدار الخاطئ من Node أو npm أو إذا كنت تفتقد أي تبعيات. إذا كنت تستخدم docker-compose
لإنشاء الموقع واختباره، فلن تواجه أي مشكلات مع إصدار Node، ولكن docker-compose build
سيبني صورة جديدة باستخدام npm ci
جديد.
إذا كتبت مكونًا جديدًا، فاكتب اختبارًا. يجب أن يكون لكل مكون ملف .spec.js
الخاص به. عداء الاختبار هو Mocha والإنزيم متاح لاختبار مكونات React. يلقي Mocha خطأ ( Illegal import declaration
) عند تجميع ملفات Coffeescript التي تحتوي على بيانات استيراد ES6 مع سلاسل القالب. قم بتحويل هذه الواردات إلى بيانات require
. يمكنك إجراء الاختبارات باستخدام npm test
.
تتم معالجة النشر بواسطة Github Action.
عند فتح طلبات السحب، يتم تشغيل إجراء Github للنشر في موقع التدريج الفرعي. يعتمد موقع تخزين البيانات الثنائية الكبيرة على رقم طلب السحب، على سبيل المثال https://pr-5926.pfe-preview.zooniverse.org
.
عند الدفع للإتقان، يتم تشغيل إجراء Github للنشر لإتقان التدريج الموجود على https://master.pfe-preview.zooniverse.org
.
يتم تشغيل عمليات نشر الإنتاج من خلال تحديث يُشار إليه بعلامة production-release
. يجب تحديث هذه العلامة عبر عمليات الدردشة وبعد ذلك سيتم تشغيل إجراء Github الذي يقوم بإنشاء الملفات وتحميلها إلى موفر السحابة الخاص بنا الموجود على https://www.zooniverse.org
. يمكن تشغيل نشر الإنتاج بشكل مخصص في علامة تبويب الإجراءات حسب الحاجة إذا كان لديك الأذونات المناسبة في المستودع، ولكن لا تفعل ذلك إلا في حالة الطوارئ.
كل ما يتعلق بالمصنف.
المكونات المتعلقة بالمجموعات.
مكونات متنوعة عامة وقابلة لإعادة الاستخدام.
يتم وضع عناصر التخطيط على مستوى التطبيق هنا. إذا كان يؤثر على رأس الموقع الرئيسي، أو تذييل الموقع الرئيسي، أو تخطيط محتوى الموقع الرئيسي، فهذا هو المكان الذي يعيش فيه.
الوظائف والبيانات الفردية التي يتم إعادة استخدامها عبر المكونات.
هذا هو المكان الذي يعيش فيه الجزء الأكبر من التطبيق. من الناحية المثالية، يشير كل مسار إلى مكون الصفحة المسؤول عن جلب البيانات ومعالجة أي إجراءات يمكن للمستخدم تنفيذها على تلك البيانات. يستخدم مكون الصفحة تلك البيانات لعرض واجهة المستخدم بمكونات غبية، وتمرير الإجراءات حسب الضرورة.
كان المقصود في الأصل الاحتفاظ بمكونات معزولة لا يمكن إعادة استخدامها في أي مكان. ربما تنتمي هذه إلى مكان أقرب إلى المكان الذي يتم استخدامها فيه بالفعل.
طرق عرض الموضوع (TODOC: ما علاقة هذا بالمحادثة/المجموعات؟)
المكونات المتعلقة بالحديث.
سيتم نسخ الملفات هنا إلى دليل الإخراج أثناء الإنشاء.
يجب أن تحتوي كل فئة مكون مهمة على مكونين ثابتين:
Summary
: يعرض ملخص ما بعد التصنيف لشرح المهام.
Editor
: المكون المستخدم لتحرير مهمة سير العمل في منشئ المشروع.
هناك أيضًا عدد قليل من الروابط في بقية واجهة التصنيف المتاحة، إذا كانت المهمة بحاجة إلى العرض خارج منطقة المهمة.
BeforeSubject
: يظهر محتوى HTML قبل صورة الموضوع أثناء المهمة.
InsideSubject
: يظهر محتوى SVG فوق صورة الموضوع أثناء المهمة.
محتوى HTML AfterSubject
ليظهر بعد صورة الموضوع أثناء المهمة.
يمكن أن تكون هذه الخطافات مسبوقة بـ Persist
، مما سيؤدي إلى ظهورها مع المهمة واستمرارها حتى بعد انتقال المستخدم إلى المهمة التالية.
Persist{Before,After}Task
بنفس الطريقة، ولكن لمنطقة المهمة. الخطافات غير المستمرة غير ضرورية لمنطقة المهمة.
يحتاج كل مكون أيضًا إلى بعض الطرق الثابتة:
getDefaultTask
: يُرجع وصف المهمة ليتم استخدامه كوصف افتراضي عندما يقوم المستخدم بإضافة المهمة إلى سير العمل في منشئ المشروع.
getTaskText
: عند إعطاء مهمة، يؤدي هذا إلى إرجاع وصف نصي أساسي للمهمة (على سبيل المثال، السؤال في مهمة السؤال، التعليمات في مهمة الرسم، وما إلى ذلك)
getDefaultAnnotation
: التعليق التوضيحي الذي سيتم إنشاؤه عندما يبدأ المصنف المهمة
isAnnotationComplete
: عند إعطاء مهمة وتعليق توضيحي، يحدد هذا ما إذا كان المصنف سيسمح للمستخدم بالانتقال إلى المهمة التالية أم لا.
testAnnotationQuality
: بالنظر إلى التعليق التوضيحي للمستخدم والتعليق التوضيحي "المعياري الذهبي" المعروف لنفس المهمة، يؤدي هذا إلى إرجاع رقم بين 0 (خطأ تمامًا) و1 (صحيح تمامًا) للإشارة إلى مدى قرب تعليق المستخدم التوضيحي من المعيار.
تأكد من استدعاء this.props.onChange
بالمهمة المحدثة عندما تتغير.
بعض الطرق الثابتة، التي يتم استدعاؤها من مكون MarkInitializer
، والتي تتحكم في قيم العلامة أثناء أول إجراء لإنشاء العلامة للمستخدم:
defaultValues
: فقط بعض الإعدادات الافتراضية للعلامة.
initStart
: لكل تمريرة للماوس/تشغيل باللمس حتى يعود isComplete
إلى القيمة true، قم بإرجاع قيم العلامة.
initMove
: لكل حركة ماوس/حركة لمس، قم بإرجاع قيم جديدة للعلامة.
initRelease
: لكل تمريرة ماوس/لمسة، قم بإرجاع قيم جديدة للعلامة.
isComplete
: هل العلامة كاملة؟ تتطلب بعض العلامات تفاعلات متعددة قبل أن يتخلى المُهيئ عن التحكم.
initValid
: إذا كانت العلامة غير صالحة (على سبيل المثال مستطيل بعرض أو ارتفاع صفر)، فسيتم تدميرها تلقائيًا.
هناك مكونان مساعدان هما DrawingToolRoot
الذي يتعامل مع الحالات المحددة/المعطلة ويعرض النوافذ المنبثقة للمهام الفرعية، و DeleteButton
و DragHandle
، اللذان يعرضان عناصر تحكم متسقة لأدوات الرسم. هناك أيضًا وظيفة deleteIfOutOfBounds
التي يجب استدعاؤها بعد أي سحب للعلامة الكاملة.
يتطلب React أن يكون لكل مكون في المصفوفة key
فريد من نوعه. عند عرض مصفوفات من الأشياء التي لا تحتوي على معرفات (التعليقات التوضيحية، الإجابات)، قم بتوفير خاصية _key
عشوائية إذا لم تكن موجودة. تأكد من عدم استمرار الخصائص ذات البادئة السفلية. يتم ذلك تلقائيًا مع فئة JSONAPIClient.Model
.
< ul >
{ for item in things
item . _key ?= Math . random ()
< li key = { item . _key }>{ item . label }</ li >}
</ ul >
هناك بعض لطيف - جيد مكونات مؤسفة (بعد فوات الأوان) للمساعدة في القيم غير المتزامنة. يأخذون وظيفة @props.children
، والتي تبدو حصانية قليلاً ولكنها تعمل بشكل جيد إلى حد ما. يتم تخزين معظم البيانات المطلوبة مؤقتًا محليًا، لذا فهي عادةً ما تكون آمنة، ولكن إذا لاحظت أن نفس الطلب يتم تقديمه عدة مرات متتالية، فهذا مكان جيد لبدء البحث عن المكالمات المتكررة. فيما يلي مثال على إعادة العرض عندما يتغير المشروع، مما يؤدي إلى التحقق من مالكي المشروع.
< ChangeListener target = { @props . project }>{ =>
< PromiseRenderer promise = { @props . project . get ( ' owners ' )}>{([owner]) =>
if owner is @props . user
< p > This project is yours.</ p >
else
< p > This project belongs to { owner . display_name }.</ p >
}</ PromiseRenderer >
}</ ChangeListener >
لا تكتب تعليمات برمجية جديدة باستخدام ChangeListener
أو PromiseRenderer
.
إذا كان ذلك معقولًا، فاستبدل مثيلات ChangeListener
و PromiseRenderer
بحالة المكون في التعليمات البرمجية التي تعمل عليها. إنه أكثر تفصيلاً، ولكنه أكثر قابلية للقراءة، وسيقربنا من العرض على الخادم في المستقبل.
قم بتضمين أي ملف CSS مطلوب لوظيفة المكون بشكل مضمن مع المكون، وإلا احتفظ به في ملف منفصل، واحد لكل مكون. بالنسبة لمكون معين، اختر اسمًا فريدًا لفئة المستوى الأعلى لذلك المكون وقم بدمج الفئات الفرعية تحته. احتفظ بالأنماط والمتغيرات الأساسية المشتركة في common.styl . تنسيق القلم: نعم نقطتان، لا توجد فواصل منقوطة، لا توجد أقواس. @extends
إلى الأعلى، ثم الخصائص (أبجديًا)، ثم المحددات التابعة. تفضيل استخدام display: flex
و flex-wrap: wrap
لاستعلامات الوسائط الصريحة حيثما أمكن ذلك.
لقد أصبح CSS الخاص بنا ضخمًا جدًا، لذلك نحن نجرب BEM للتنظيم.
// <special-button.styl>
.special-button
background : red
color : white
.special-button__icon
width : 1 em ;
// <special-container.styl>
.special-container
margin : 1 em 1 vw
.special-container__button
border : 1 px solid
نحن ننتقل من Coffeescript إلى ES6. يمكن القيام بذلك بشكل تدريجي عن طريق كتابة مكون جديد أو إعادة كتابة مكون موجود في ES6. ينبغي أن نذكر بعض المعضلات:
العامل الوجودي غير موجود في ES6. إما أن تقارن بشكل صريح بـ null
أو تستخدم !!thing
إذا كانت تحتاج فقط إلى أن تكون صادقة.
يُفضل استخدام فئات ES6 الأصلية نظرًا لإهمال React.createClass()
، ومع ذلك، إذا كان المكون الحالي يعتمد على mixins، ففكر في استخدام createReactClass()
.
تم إهمال Mixins وعدم دعمه مع الفئات الأصلية، لذلك لا تستخدمها في مكونات جديدة.
استخدم العلامات الخلفية لاستيراد مكونات ES6 إلى مكونات Coffeescript:
`import NewComponent from './new-component'`
يتم إعداد ملف تكوين ESLint في جذر المستودع لتستخدمه مع محرر النصوص الخاص بك لفحص كل من ES6 واستخدام دليل نمط React الخاص بـ Airbnb.
دليل حول كتابة الفئات الأصلية مقابل استخدام createReactClass()
راجع مكتبة عميل panoptes : https://www.npmjs.com/package/panoptes-client.
يعتمد تنسيق قيمة التعليق التوضيحي على المهمة المستخدمة لإنشائه.
مفرد: فهرس الإجابة المختارة.
متعددة: مصفوفة من مؤشرات الإجابات المختارة (بترتيب اختيارها).
الرسم: مجموعة من علامات أدوات الرسم (يتبع وصفها أدناه).
المسح: مجموعة من التعريفات كأشياء. كل تحديد choice
(معرف الحيوان الذي تم تحديده) answers
، كائن. كل مفتاح في answers
هو معرف السؤال. إذا كان هذا السؤال يسمح بإجابات متعددة، فستكون القيمة عبارة عن مجموعة من معرفات الإجابات، وإلا فستكون مجرد معرف إجابة واحد.
الاقتصاص: كائن يحتوي على x
و y
width
height
المنطقة التي تم اقتصاصها.
النص: سلسلة.
التحرير والسرد: مجموعة فرعية من التعليقات التوضيحية.
القائمة المنسدلة: مصفوفة من الكائنات حيث تشير value
السلسلة إلى إجابة السؤال المقابل ويشير option
المنطقي إلى أن الإجابة كانت موجودة في قائمة الخيارات.
جميع الإحداثيات مرتبطة بأعلى يسار الصورة.
تحتوي جميع العلامات على tool
، وهي فهرس الأداة (على سبيل المثال، workflow.tasks.T0.tools[0]
) المستخدمة في إنشاء العلامة.
تحتوي جميع العلامات على frame
، وهو فهرس إطار الموضوع (على سبيل المثال، subject.locations[0]
) التي تم وضع العلامة عليه.
إذا تم تعريف المهام details
لأداة ما، فستحتوي علاماتها على مصفوفة details
من التصنيفات الفرعية (لكل منها value
، تتبع الأوصاف أعلاه).
قيمة التعليق التوضيحي للرسم هي كما يلي:
النقطة: إحداثيات x
و y
.
السطر: إحداثيات البداية ( x1
, y1
) والنهاية ( x2
, y2
).
المضلع: مصفوفة من الكائنات، يحتوي كل منها على إحداثيي x
و y
للرأس. إذا لم يتم إغلاق العلامة بشكل صريح من قبل المستخدم، فإن auto_closed
يكون true
.
المستطيل: إحداثي x
و y
للنقطة العلوية اليسرى للمستطيل مع width
height
.
الدائرة: إحداثيات x
و y
لمركز الدائرة ونصف قطرها r
.
القطع الناقص: إحداثيات x
و y
لمركز القطع الناقص، ونصف قطرها rx
و ry
، angle
rx
بالنسبة إلى المحور x بالدرجات (عكس اتجاه عقارب الساعة من 3:00).
بيزير: مثل المضلع، ولكن كل نقطة مفهرسة بشكل فردي هي إحداثيات نقطة التحكم لمنحنى بيزير التربيعي.
العمود: بكسل x
الموجود في أقصى اليسار width
العمود المحدد.
شكرًا لـ BrowserStack لدعمه مفتوح المصدر والسماح لنا باختبار هذا المشروع على منصات متعددة.