הרחבת ממשק המשתמש של הכתיבה עם פעולות כתיבה

בנוסף לממשק שמבוסס על כרטיסים כשמשתמשים קוראים הודעה ב-Gmail, תוספים של Google Workspace שמרחיבים את Gmail יכולים לספק ממשק אחר כשמשתמשים כותבים הודעות חדשות או עונים להודעות קיימות. כך תוספים של Google Workspace יכולים לבצע אוטומציה של כתיבת אימיילים עבור המשתמש.

גישה לממשק המשתמש של התוסף לכתיבה

יש שתי דרכים להציג את ממשק המשתמש לכתיבה של תוסף. הדרך הראשונה היא להתחיל לכתוב טיוטה או תשובה חדשה כשהתוסף כבר פתוח. הדרך השנייה היא להפעיל את התוסף בזמן כתיבת טיוטה.

בכל מקרה, התוסף יבצע את פונקציית הטריגר של ה-Compose המתאימה, שהוגדרה במניפסט של התוסף. פונקציית הטריגר של כתיבת האימייל יוצרת את ממשק המשתמש של כתיבת האימייל לפעולה הזו, ואז Gmail מציג אותו למשתמש.

פיתוח תוסף לכתיבה

כדי להוסיף פונקציונליות של כתיבת אימייל לתוסף, פועלים לפי השלבים הכלליים הבאים:

  1. מוסיפים את השדה gmail.composeTrigger למניפסט של פרויקט הסקריפט של התוסף ומעדכנים את ההיקפים של המניפסט כך שיכללו את אלה שנדרשים לפעולות הכתיבה.
  2. הטמעת פונקציית טריגר של compose שיוצרת ממשק משתמש של compose כשהטריגר מופעל. פונקציות הטריגר של Compose מחזירות אובייקט Card יחיד או מערך של אובייקטים Card שמרכיבים את ממשק המשתמש של Compose עבור פעולת ה-Compose.
  3. מטמיעים פונקציות קריאה חוזרת משויכות שנדרשות כדי להגיב לאינטראקציות של המשתמש בממשק המשתמש של כתיבת ההודעה. הפונקציות האלה הן לא פעולת הכתיבה עצמה (שגורמת רק להופעת ממשק המשתמש לכתיבה), אלא הפונקציות הנפרדות שקובעות מה יקרה כשבוחרים רכיבים שונים בממשק המשתמש לכתיבה. לדוגמה, לכרטיס UI שמכיל לחצן בדרך כלל יש פונקציית קריאה חוזרת (callback) משויכת שפועלת כשמשתמש לוחץ על הלחצן הזה. פונקציית ההתקשרות חזרה של ווידג'טים שמעדכנים את תוכן טיוטת ההודעה צריכה להחזיר אובייקט UpdateDraftActionResponse.

כתיבת פונקציית טריגר

ממשק המשתמש לכתיבה של תוסף נבנה באותו אופן שבו נבנה ממשק המשתמש להודעות של התוסף – באמצעות שירות הכרטיסים של Apps Script כדי ליצור כרטיסים ולמלא אותם בווידג'טים.

צריך להטמיע את gmail.composeTrigger.selectActions[].runFunction שהגדרתם במניפסט. פונקציית הטריגר של היצירה צריכה להחזיר אובייקט Card יחיד או מערך של אובייקטים מסוג Card שמרכיבים את ממשק המשתמש ליצירה של הפעולה הזו. הפונקציות האלה דומות מאוד לפונקציות טריגר לפי הקשר, וצריך ליצור דרכן כרטיסים באותו אופן.

הרכבת אובייקטים של אירועי טריגר

כשבוחרים פעולת compose, היא מבצעת את פונקציית הטריגר המתאימה של compose ומעבירה לפונקציה אובייקט אירוע כפרמטר. אובייקט האירוע יכול לשאת מידע על ההקשר של התוסף ועל הטיוטה שנוצרת לפונקציית הטריגר.

במאמר מבנה אובייקט האירוע מוסבר איך המידע מסודר באובייקט האירוע. המידע שמכיל אובייקט האירוע נשלט באופן חלקי על ידי הערך של שדה המניפסט gmail.composeTrigger.draftAccess:

  • אם השדה gmail.composeTrigger.draftAccess במניפסט הוא NONE או לא נכלל, אובייקט האירוע מכיל רק מידע מינימלי.

  • אם הערך של gmail.composeTrigger.draftAccess מוגדר כ-METADATA, אובייקט האירוע שמוענק לפונקציית הטריגר של כתיבת האימייל מאוכלס ברשימות של נמעני האימייל שנכתב.

הוספת תוכן לטיוטות פעילות

בדרך כלל, ממשק המשתמש לכתיבה בתוסף ל-Google Workspace מספק למשתמשים את האפשרויות ואת אמצעי הבקרה שיעזרו להם לכתוב הודעה. בתרחישי השימוש האלה, אחרי שהמשתמש מבצע בחירות בממשק המשתמש, התוסף מפרש את הבחירות ומעדכן את טיוטת האימייל הקיימת בהתאם.

כדי שיהיה קל יותר לעדכן את טיוטת האימייל הנוכחית, הוספנו לשירות הכרטיסים את הכיתות הבאות:

  • ContentType – משתנה מוגדר מראש שמגדיר אם להוסיף HTML שניתן לשינוי, HTML שלא ניתן לשינוי (משתמשי Gmail לא יכולים לערוך אותו) או תוכן טקסט פשוט.
  • UpdateDraftActionResponse – מייצגת תגובה לפעולה שמעדכנת את טיוטת האימייל הנוכחית.
  • UpdateDraftActionResponseBuilder – ה-builder של אובייקטים מסוג UpdateDraftActionResponse.
  • UpdateDraftBodyAction – מייצג פעולה שמעדכנת את גוף טיוטת האימייל הנוכחית.
  • UpdateDraftBodyType – enum שמגדיר את אופן השינוי של הגוף.
  • UpdateDraftSubjectAction – מייצגת פעולה שמעדכנת את שדה הנושא של טיוטת האימייל הנוכחית.
  • UpdateDraftToRecipientsAction – מייצגת פעולה שמעדכנת את הנמענים בשדה 'אל' של טיוטת האימייל הנוכחית.
  • UpdateDraftCcRecipientsAction – מייצגת פעולה שמעדכנת את הנמענים בשדה 'עותק' בטיוטת האימייל הנוכחית.
  • UpdateDraftBccRecipientsAction – הפעולה הזו מייצגת עדכון של נמעני העותק המוסתר של טיוטת האימייל הנוכחית.

בדרך כלל, ממשק המשתמש ליצירת אימייל של התוסף כולל ווידג'ט של 'שמירה' או 'הוספה', שעליו המשתמש יכול ללחוץ כדי לציין שהוא סיים לבצע בחירות בממשק המשתמש ושהוא רוצה שהבחירות שלו יתווספו לאימייל שהוא יוצר. כדי להוסיף את האינטראקטיביות הזו, צריך לשייך לווידג'ט אובייקט Action שמורה לתוסף להריץ פונקציית קריאה חוזרת ספציפית כשלוחצים על הווידג'ט. צריך להטמיע את פונקציות הקריאה החוזרת האלה. כל פונקציית קריאה חוזרת צריכה להחזיר אובייקט UpdateDraftActionResponse מובנה שמפרט את השינויים שצריך לבצע בטיוטה הנוכחית של הודעת האימייל.

דוגמה 1

קטע הקוד הבא מראה איך ליצור ממשק משתמש לכתיבה שמעדכן את הנושא ואת הנמענים של 'אל', 'עותק' ו'עותק מוסתר' של טיוטת האימייל הנוכחית.

    /**
     * Compose trigger function that fires when the compose UI is
     * requested. Builds and returns a compose UI for inserting images.
     *
     * @param {event} e The compose trigger event object. Not used in
     *         this example.
     * @return {Card[]}
     */
    function getComposeUI(e) {
      return [buildComposeCard()];
    }

    /**
     * Build a card to display interactive buttons to allow the user to
     * update the subject, and To, Cc, Bcc recipients.
     *
     * @return {Card}
     */
    function buildComposeCard() {

      var card = CardService.newCardBuilder();
      var cardSection = CardService.newCardSection().setHeader('Update email');
      cardSection.addWidget(
          CardService.newTextButton()
              .setText('Update subject')
              .setOnClickAction(CardService.newAction()
                  .setFunctionName('applyUpdateSubjectAction')));
      cardSection.addWidget(
          CardService.newTextButton()
              .setText('Update To recipients')
              .setOnClickAction(CardService.newAction()
                  .setFunctionName('updateToRecipients')));
      cardSection.addWidget(
          CardService.newTextButton()
              .setText('Update Cc recipients')
              .setOnClickAction(CardService.newAction()
                  .setFunctionName('updateCcRecipients')));
      cardSection.addWidget(
          CardService.newTextButton()
              .setText('Update Bcc recipients')
              .setOnClickAction(CardService.newAction()
                  .setFunctionName('updateBccRecipients')));
      return card.addSection(cardSection).build();
    }

    /**
     * Updates the subject field of the current email when the user clicks
     * on "Update subject" in the compose UI.
     *
     * Note: This is not the compose action that builds a compose UI, but
     * rather an action taken when the user interacts with the compose UI.
     *
     * @return {UpdateDraftActionResponse}
     */
    function applyUpdateSubjectAction() {
      // Get the new subject field of the email.
      // This function is not shown in this example.
      var subject = getSubject();
      var response = CardService.newUpdateDraftActionResponseBuilder()
          .setUpdateDraftSubjectAction(CardService.newUpdateDraftSubjectAction()
              .addUpdateSubject(subject))
          .build();
      return response;
    }

    /**
     * Updates the To recipients of the current email when the user clicks
     * on "Update To recipients" in the compose UI.
     *
     * Note: This is not the compose action that builds a compose UI, but
     * rather an action taken when the user interacts with the compose UI.
     *
     * @return {UpdateDraftActionResponse}
     */
    function applyUpdateToRecipientsAction() {
      // Get the new To recipients of the email.
      // This function is not shown in this example.
      var toRecipients = getToRecipients();
      var response = CardService.newUpdateDraftActionResponseBuilder()
          .setUpdateDraftToRecipientsAction(CardService.newUpdateDraftToRecipientsAction()
              .addUpdateToRecipients(toRecipients))
          .build();
      return response;
    }

    /**
     * Updates the Cc recipients  of the current email when the user clicks
     * on "Update Cc recipients" in the compose UI.
     *
     * Note: This is not the compose action that builds a compose UI, but
     * rather an action taken when the user interacts with the compose UI.
     *
     * @return {UpdateDraftActionResponse}
     */
    function applyUpdateCcRecipientsAction() {
      // Get the new Cc recipients of the email.
      // This function is not shown in this example.
      var ccRecipients = getCcRecipients();
      var response = CardService.newUpdateDraftActionResponseBuilder()
          .setUpdateDraftCcRecipientsAction(CardService.newUpdateDraftCcRecipientsAction()
              .addUpdateToRecipients(ccRecipients))
          .build();
      return response;
    }

    /**
     * Updates the Bcc recipients  of the current email when the user clicks
     * on "Update Bcc recipients" in the compose UI.
     *
     * Note: This is not the compose action that builds a compose UI, but
     * rather an action taken when the user interacts with the compose UI.
     *
     * @return {UpdateDraftActionResponse}
     */
    function applyUpdateBccRecipientsAction() {
      // Get the new Bcc recipients of the email.
      // This function is not shown in this example.
      var bccRecipients = getBccRecipients();
      var response = CardService.newUpdateDraftActionResponseBuilder()
          .setUpdateDraftBccRecipientsAction(CardService.newUpdateDraftBccRecipientsAction()
              .addUpdateToRecipients(bccRecipients))
          .build();
      return response;
    }

דוגמה 2

קטע הקוד הבא מראה איך ליצור ממשק משתמש לכתיבה שמוסיף תמונות לטיוטת האימייל הנוכחית.

    /**
     * Compose trigger function that fires when the compose UI is
     * requested. Builds and returns a compose UI for inserting images.
     *
     * @param {event} e The compose trigger event object. Not used in
     *         this example.
     * @return {Card[]}
     */
    function getInsertImageComposeUI(e) {
      return [buildImageComposeCard()];
    }

    /**
     * Build a card to display images from a third-party source.
     *
     * @return {Card}
     */
    function buildImageComposeCard() {
      // Get a short list of image URLs to display in the UI.
      // This function is not shown in this example.
      var imageUrls = getImageUrls();

      var card = CardService.newCardBuilder();
      var cardSection = CardService.newCardSection().setHeader('My Images');
      for (var i = 0; i < imageUrls.length; i++) {
        var imageUrl = imageUrls[i];
        cardSection.addWidget(
            CardService.newImage()
                .setImageUrl(imageUrl)
                .setOnClickAction(CardService.newAction()
                      .setFunctionName('applyInsertImageAction')
                      .setParameters({'url' : imageUrl})));
      }
      return card.addSection(cardSection).build();
    }

    /**
     * Adds an image to the current draft email when the image is clicked
     * in the compose UI. The image is inserted at the current cursor
     * location. If any content of the email draft is currently selected,
     * it is deleted and replaced with the image.
     *
     * Note: This is not the compose action that builds a compose UI, but
     * rather an action taken when the user interacts with the compose UI.
     *
     * @param {event} e The incoming event object.
     * @return {UpdateDraftActionResponse}
     */
    function applyInsertImageAction(e) {
      var imageUrl = e.parameters.url;
      var imageHtmlContent = '<img style=\"display: block\" src=\"'
           + imageUrl + '\"/>';
      var response = CardService.newUpdateDraftActionResponseBuilder()
          .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()
              .addUpdateContent(
                  imageHtmlContent,
                  CardService.ContentType.MUTABLE_HTML)
              .setUpdateType(
                  CardService.UpdateDraftBodyType.IN_PLACE_INSERT))
          .build();
      return response;
    }