ملاحظات حول javascript "on-discourse" لإعداد JS مخصص لكل صفحة؟

لقد كنت أكتب تطبيق Svelte يحتاج إلى التثبيت الذاتي في كل صفحة. اكتشفت كيفية إضافة ملف JS الخاص بي إلى قسم الرأس وجعله يتم تحميله عند تحميل الصفحة الأولى. ومع ذلك، مع تجربتي، أدركت أن discourse يجلب محتوى جديدًا عبر XHR ويستبدل أقسامًا معينة، وبالتالي لم يتم إعادة تهيئة تطبيقي عند تحميل صفحة جديدة.

لقد لعبت بمحاولات مختلفة للحصول على إشعار عند تغيير الصفحة، ولكن لا يبدو أن Ember يوفر الخطافات، ولم أتمكن من العثور على أي أحداث مخصصة يمكنني الاستماع إليها.

يبدو أن إحدى الطرق هي إضافة مراقب طفرة إلى DOM ومراقبة التغييرات. وجدت أن "موضوع # يبدو أنه هو الذي يتم إعادة تحميله (وتتغير السمات بحيث يمكنك فقط مراقبة تغييرات السمات). لقد قمت بإعداد مراقب طفرة هناك (مراقبو الطفرة هم الطريقة الجديدة والأكثر أداءً لمراقبة تغييرات DOM). الطريقة التي تعمل بها هي مراقبة التغييرات، وعندما تحدث، تشغيل رد اتصال لإعادة تحميل تطبيق Svelte الخاص بي لتلك الصفحة.

أنا معجب بهذا النهج، وأود الحصول على ملاحظات.

سؤال واحد: هل يجب علي بدلاً من ذلك مراقبة التغييرات في عنوان URL؟ هل هذه فكرة أفضل لتسجيل مستمع لـ popstate؟

لاستخدامه، قم بشيء مثل هذا في السمة / الرأس الخاص بك:

<script src="https://files.extrastatic.dev/community/on-discourse.js"></script>
<script src="https://files.extrastatic.dev/community/index.js"></script>
<link rel="stylesheet" type="text/css" href="https://files.extrastatic.dev/community/index.css">

بعد ذلك، داخل مكتبتك يمكنك استدعاء on-discourse على هذا النحو:

function log(...msg) {
  console.log('svelte', ...msg);
}

// هذا هو كود تثبيت Svelte
function setup() {
  try {
    const ID = 'my-special-target-id';
    log('Inside setup()');
    const el = document.getElementById(ID);
    if (el) {
      log('Removed existing element', ID);
      el.remove();
    }
    const target = document.createElement("div");
    target.setAttribute("id", ID);
    log('Created target');
    document.body.appendChild(target);
    log('Appended child to body');
    const app = new App({
      // eslint-disable-next-line no-undef
      target
    });
    log('Created app and installed');
  } catch(err) {
    console.error('Unable to complete setup()', err.toString() );
  }
}

(function start() {
  log('Starting custom Svelte app');
  // إعادة التثبيت عند حدوث تغييرات
  window.onDiscourse && window.onDiscourse( setup );
  // تحميل التطبيق عند تحميل الصفحة الأولى
  window.addEventListener('load', () => {
    setup();
  });
  log('Finished custom Svelte app);  
})();

بشكل أساسي، ما عليك سوى استدعاء window.onDiscourse(callback) مع رد الاتصال الخاص بك (ويمكنك تشغيله عدة مرات لتثبيت ردود اتصال متعددة)، وعندما يحدث طفرة، يتم تشغيل رد الاتصال هذا لتهيئة تطبيقك.

هنا الكود الكامل لـ on-discourse.js. (تعديل: قمت بتحديث هذا لاستخدام #topic، والذي يبدو أنه شيء جيد لمراقبته، لأن السمات تتغير عند تحميل الصفحة، ثم يحتاج الطفرة فقط إلى مراقبة تغييرات السمات، بدلاً من البحث في شجرة DOM بأكملها عن #main-outlet)

let mutationObservers = [];

function log(...msg) {
  console.log("on-discourse", ...msg);
}

function observeMainOutlet() {
  log('Observing main outlet');
  // حدد العقدة التي سيتم مراقبتها للطفرات
  const targetNode = document.getElementById("topic");
 
  if (targetNode) {
    // خيارات للمراقب (الطفرات التي سيتم مراقبتها)
    const config = { attributes: true };
    
   // إنشاء مثيل مراقب لإعادة التعيين عند تغيير childList
    const observer = new MutationObserver(function(mutations) {
      let reset = false;
      mutations.forEach(function(mutation) {
	if (mutation.type === 'attributes') {
	  log('Found main-outlet mutation, running callbacks');
	    mutationObservers.forEach( (s,i) => {
		try {
		    log(`Running div callback ${i+1}`);	    
		    s();
		    log(`Finished div callback ${i+1}`);	    
		} catch( err ) {
		    log(`Div callback error (${i+1})`, err );	    	      
		}
	 });
       }
      });
    });
    
    // ابدأ مراقبة العقدة المستهدفة للطفرات المكونة
    observer.observe(targetNode, config);
    
    // لاحقًا، يمكنك إيقاف المراقبة
    // observer.disconnect();
    log('Done with outlet observer');
  } else {
    console.error('on-discourse FATAL ERROR: Unable to find main-outlet');
  }
}

window.addDiscourseDivMutationObserver = (cb) => {
    log('Adding on-discourse div mutation callback');  
    mutationObservers.push(cb);
    log('Added on-discourse div mutation callback');    
}

window.addEventListener("load", () => {
    log('Setting up topic observer');
    if (mutationObservers.length > 0) {
	observeMainOutlet();
    }
    log('Created topic observer');  
});

log('Completed setup of on-discourse.js');

أعتقد أنك تريد وضع الكود الخاص بك في مُهيئ بدلاً من وضعه في رأس الصفحة. انظر إلى دليل المطور لترى كيف يمكنك وضع ملفات JavaScript الخاصة بك في ملفات منفصلة.

إعجاب واحد (1)

شكراً جزيلاً على الرد!
أعتقد أن هذا جزء مما يجعلني مرتبكًا، حيث أواجه صعوبة في العثور على أي إشارات إلى توسيع Discourse عن طريق إضافة JS الخاص بي.
عندما أجري بحثًا على DuckDuckGo عن “دليل مطوري Discourse”، فإن الرابط الأول الذي أحصل عليه هو رابط إلى مستودع GitHub.
الرابط التالي هو لـ “دليل تثبيت مطوري Discourse المتقدم”. هذا الدليل مخصص لإخبارك بكيفية إعداد Rails للتطوير، ولكنه لا يحتوي على أي روابط حول كيفية تثبيت JS مخصص على حد علمي. أحاول تجنب عملية بناء معقدة وهو ما أتذكره من أيام Rails الخاصة بي. أود حقًا تطوير كود امتداد JS هذا بشكل منفصل، ثم وضع علامة نص برمجي في موقعي. لذا، لا أرغب حقًا في إعداد بيئة Rails محليًا حتى أتمكن من بنائها؛ ربما أفتقد فائدة ذلك؟ لكنني أحب أن أكون قادرًا على تحديث حاوية Docker تستخدم سمة مع بضعة علامات نص برمجي <script>.
الرابط التالي هو “دليل المبتدئين لتطوير سمات Discourse” وهو يتعلق بتطوير السمات، وليس ما أحتاجه، صحيح؟
أرى روابط لواجهة برمجة تطبيقات Discourse وهي ليست ما أريده بوضوح.
إذا بحثت عن “initializer javascript discourse”، أرى هذا الرابط من 5 سنوات: Execute JavaScript code from a plugin once after load ولكن هذا يبدو وكأنني أقوم بالتوصيل بـ Rails، وأشعر أنه يجب أن تكون هناك طريقة أبسط، ويبدو أن هذا الموضوع لم يتم حله أيضًا؟
رابط آخر لـ “initializer javascript discourse” يقترح القيام بما أقوم به لتثبيت JS، ولكنه لا يقدم اقتراحات حول كيفية التأكد من أنه في كل مرة يتغير فيها محتوى الصفحة (إما من خلال تحديث كامل للصفحة أو طلب XHR يشبه “turbolinks”): https://stackoverflow.com/questions/48611621/how-do-i-add-an-external-javascript-file-into-discourse
هل هذا هو النقاش الذي يجب أن أراجعه؟ A versioned API for client side plugins
أو ربما هذا؟ للوهلة الأولى، لا أفهم بناء الجملة (تلك التعليقات لا تبدو مثل JS، هل هذه اصطلاحات Rails؟) لذلك لست متأكدًا مما إذا كان هذا ما أحتاجه: Using Plugin Outlet Connectors from a Theme or Plugin

نعم، الأمر ليس سهلاً على الإطلاق.

Discourse هو تطبيق EmberJS.

من الناحية المثالية، يجب كتابة امتدادات JavaScript في إطار عمل EmberJS باستخدام مكون سمة (Theme Component) (أو إضافة Plugin)، وواجهة برمجة تطبيقات Discourse JavaScript (Discourse JavaScript API) عند الاقتضاء ومنافذ الإضافات (plugin outlets).

القضية الرئيسية التي ستواجهها عند استخدام نصوص خارجية مخصصة هي جعلها تعمل في الوقت المناسب.

للتأكد من ذلك، تحتاج إلى ربطها بالإجراءات في مكون (Component) (والذي يمكن جعله يعمل عند الإدراج أو التحديث).

إعجاب واحد (1)

هذا تأكيد جيد. لكن، يجب أن أقول، أنا سعيد حقًا بكود مراقب التغييرات الخاص بي. إنه يكتشف متى يتغير محتوى الصفحة بشكل صحيح، ثم يسمح لي بتشغيل كود جافاسكريبت المخصص الخاص بي. إنه يعمل بشكل رائع، ولم أضطر إلى تعلم Ember، ولا تعديل تطبيق RoR بأي شكل من الأشكال. أنا سعيد حقًا بهذا الحل في الوقت الحالي. شكرًا على كل التعليقات.

إعجاب واحد (1)

لقد جربت للتو استخدام مراقب حدث popstate. لو كان ذلك قد نجح، لكان الكود 5 أسطر بدلاً من 20. ومع ذلك، يبدو أن النقر حول لا يؤدي إلى تشغيل هذا الحدث. إذا استخدمت أزرار الأمام أو الخلف، فأرى الحدث. أنا لا أفهم popstate بوضوح بما فيه الكفاية ولكن في الوقت الحالي أنا ألتزم بمراقب طفرة div.

أدركت أنني كنت أفعل شيئًا غبيًا هنا. أقوم بتعديل السمة وإضافة التعليمات البرمجية إلى الرأس. إذا تحولت إلى سمة مختلفة، فستضيع هذه التغييرات. الطريقة الصحيحة هي إضافة التعليمات البرمجية باستخدام مكون إضافي. لقد استخدمت القالب هنا: GitHub - discourse/discourse-plugin-skeleton: Template for Discourse plugins. ثم أضفت تعليمات برمجية JavaScript مثل هذا:

export default {
  name: 'alert',
    initialize() {
        const scripts = [
            'https://files.extrastatic.dev/community/on-discourse.js',
            'https://files.extrastatic.dev/community/index.js'
        ];
        scripts.forEach( s => {
            const script = document.createElement('script');
            script.setAttribute('src', s);
            document.head.appendChild(script);
        });

        const link = document.createElement('link');
        link.setAttribute('rel', "stylesheet");
        link.setAttribute('type', "text/css");
        link.setAttribute('href', "https://files.extrastatic.dev/community/index.css");
        document.head.appendChild(link);
    }
};

ثم قمت بتشغيل ./launcher rebuild app ثم قمت بتمكين المكون الإضافي.
أخيرًا، احتجت إلى إضافة سياسة CSP في الإعدادات للسماح بتحميل هذه البرامج النصية. ذهبت إلى المسؤول → الإعدادات → الأمان ثم أضفت files.extrastatic.dev إلى “content security policy script src” ونقرت على تطبيق.

غير صحيح.

إذا لم تكن تعدل واجهة برمجة التطبيقات (API) (على سبيل المثال، باستخدام جافاسكريبت بنسبة 100%)، فلا داعي لاستخدام إضافة (plugin) تكون أكثر صعوبة في النشر والتبديل.

إذا كان كل ما تفعله هو تعديل أو إضافة بعض جافاسكريبت، فيجب أن يكون مكون السمة (Theme Component) كافياً.

إعجابَين (2)

حسنًا. ولكن، عندما أقوم بتبديل السمات (أو إذا سمحت للمستخدم باختيار سمته الخاصة)، فهل لا أواجه مشكلة أنني بحاجة إلى التأكد من أن كل سمة قابلة للتعديل حتى أتمكن من إضافة علامات الرأس الجديدة، بل والأسوأ من ذلك، الحاجة إلى صيانة الكود الخاص بي عبر جميع السمات؟

عندما بدأت في استخدام سمات مختلفة، أشار البعض إلى أنه لتعديل السمة، تحتاج إلى تعديل مستودع GitHub. يبدو هذا مرهقًا وغير مرن للغاية. ولكن، فإن صيانة علامات النص البرمجي الخاصة بي عبر كل سمة تبدو عرضة للخطأ وهي مشكلة أكبر.

ما الذي أفتقده؟ هل هناك طريقة لحل هذه المشاكل باستخدام السمات فقط؟

إعجاب واحد (1)

في هذه الحالة، ستقوم بإنشاء مكون سمة (Theme Component) وإضافة هذا المكون إلى جميع السمات. (أعترف أن هذا يضيف تعقيدًا إضافيًا).

إذا كنت ترغب في تشغيل الموقع بشكل احترافي، فإن التأكد من أن كل الكود الخاص بك موجود على Github فكرة جيدة جدًا بالفعل.

ولكن، في البداية عندما تجرب بعض الأفكار، بالتأكيد، يمكنك العبث به “محليًا” إذا أردت.

عندما تستقر تغييرات الكود، يجب عليك على الأرجح وضعه في نظام التحكم بالمصادر (source control)، ولكني أجادل بأن كلما كان ذلك مبكرًا كان أفضل.

إحدى طرق الجمع بين التطور السريع و GitHub هي دمجه مع هذا:

ونشره في مكون سمة اختبار (Test Theme Component) في سمة اختبار (Test Theme) … ولكننا الآن ندخل في الأمور المعقدة حقًا …

مما يسمح لك بالنشر فورًا، ثم يمكنك تأمين تغييراتك في مستودع git بمجرد رضاك.

إعجاب واحد (1)

ألق نظرة على GitHub - discourse/discourse-theme-skeleton: Template for Discourse themes

هذا ما تحتاجه How do you force a script to refire on every page load in Discourse? - #5 by simon

<script type="text/discourse-plugin" version="0.8">
    api.onPageChange(() =>{
        // code
    });
</script>
3 إعجابات

رائع @RGJ يبدو مثالياً تماماً! شكراً!

إعجابَين (2)

مرحباً @RGJ لقد كنت أحاول معرفة كيفية القيام بذلك، ويجب أن أعترف أنني أصبحت مرتبكًا. يبدو أن واجهة برمجة تطبيقات المكونات الإضافية قد تغيرت قليلاً على مر السنين ولست متأكدًا من كيفية الحصول على ما هو حالي، أو رؤية مثال.

يبدو أن الكود الخاص بك يدخل في صفحة HTML في مكان ما لأنه محاط بعلامات النص البرمجي. كنت أستخدم كودًا مثل هذا وهو ملف JS نفسه.

https://extrastatic.dev/publicdo/publicdo-discourse-plugin/-/blob/main/assets/javascripts/discourse/initializers/kanji.js?ref_type=heads

كيف يمكنني تعديل المكون الإضافي الخاص بي لاستخدام الكود الذي قدمته؟ هل هذا الكود الذي اقترحته شيء يدخل في مكون إضافي، أم أضعه في سمة، أو أضيفه إلى الصفحات بطريقة أخرى؟

حاولت استخدام كود مثل هذا:

export default {
  name: 'publicdo',
    initialize() {
     withPluginApi('0.1', api => {
                api.onPageChange(() => {
                   console.log('Run my code here.');
                });
      });
    }
}

لكن هذا يفشل مع Uncaught (in promise) ReferenceError: withPluginApi is not defined لذلك من الواضح أنه ليس شيئًا يتلقاه JS المحمل بشكل عام.

يجب عليك ببساطة

import { withPluginApi } from "discourse/lib/plugin-api";

3 إعجابات

يجب عليك بالفعل تصفح المصادر المرتبطة في Theme component (النظام البيئي مفتوح المصدر في الغالب - املأ حذائك!).
ستلاحظ أن الاستيرادات ضرورية وشائعة غالبًا (وكذلك استخدام api.onPageChange ووظائف api المفيدة الأخرى).

4 إعجابات

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.