作曲エリア下部に投票ボタンを追加

@Steven は親切にもお手伝いいただき、ほぼ機能するスクリプトを作成してくれました。ただし、ボタンをタップしても何も起きません。

<script type='text/x-handlebars' data-template-name='composer'>
{{#composer-body composer=model
                 showPreview=showPreview
                 openIfDraft=(action "openIfDraft")
                 typed=(action "typed")
                 cancelled=(action "cancelled")
                 save=(action "save")}}
  <div class="grippie"></div>
  {{#if visible}}
      {{composer-messages composer=model
                          messageCount=messageCount
                          addLinkLookup=(action "addLinkLookup")}}
      {{#if model.viewOpenOrFullscreen}}
        <div class="reply-area {{if canEditTags 'with-tags'}}">
          <div class='composer-fields'>
            {{plugin-outlet name="composer-open" args=(hash model=model)}}
            <div class='reply-to'>
              {{#unless model.viewFullscreen}}
                <div class="reply-details">
                  {{composer-action-title
                    model=model
                    openComposer=(action "openComposer")
                    closeComposer=(action "closeComposer")
                    canWhisper=canWhisper
                    tabindex=8}}
                  {{plugin-outlet name="composer-action-after" noTags=true args=(hash model=model)}}

                  {{#unless site.mobileView}}
                    {{#if isWhispering}}
                      <span class='whisper'>{{d-icon "far-eye-slash"}}</span>
                    {{/if}}
                    {{#if model.unlistTopic}}
                      <span class='whisper'>({{i18n 'composer.unlist'}})</span>
                    {{/if}}
                    {{#if model.noBump}}
                      <span class="no-bump">{{d-icon "anchor"}}</span>
                    {{/if}}
                  {{/unless}}

                  {{#if canEdit}}
                    {{#link-to-input onClick=(action "displayEditReason") showInput=showEditReason icon="info-circle" class="display-edit-reason"}}
                      {{text-field value=editReason tabindex="7" id="edit-reason" maxlength="255" placeholderKey="composer.edit_reason_placeholder"}}
                    {{/link-to-input}}
                  {{/if}}
                </div>
              {{/unless}}
              {{composer-toggles composeState=model.composeState
                        toggleComposer=(action "toggle")
                        toggleToolbar=(action "toggleToolbar")
                        toggleFullscreen=(action "fullscreenComposer")}}
            </div>
            {{#unless model.viewFullscreen}}
              {{#if model.canEditTitle}}
                {{#if model.creatingPrivateMessage}}
                  <div class='user-selector'>
                    {{composer-user-selector topicId=topicModel.id
                                             usernames=model.targetRecipients
                                             hasGroups=model.hasTargetGroups
                                             focusTarget=focusTarget
                                             class="users-input"}}
                    {{#if showWarning}}
                      <label class='add-warning'>
                        {{input type="checkbox" checked=model.isWarning tabindex="3"}}
                        {{i18n "composer.add_warning"}}
                      </label>
                    {{/if}}
                  </div>
                {{/if}}

                <div class="title-and-category {{if showPreview 'with-preview'}}">

                  {{composer-title composer=model lastValidatedAt=lastValidatedAt focusTarget=focusTarget}}

                  {{#if model.showCategoryChooser}}
                    <div class="category-input">
                      {{category-chooser
                        value=model.categoryId
                        tabindex="3"
                        onChange=(action (mut model.categoryId))
                        isDisabled=disableCategoryChooser
                        options=(hash
                          scopedCategoryId=scopedCategoryId
                        )
                      }}
                      {{popup-input-tip validation=categoryValidation}}
                    </div>
                  {{/if}}
                  {{#if canEditTags}}
                    {{mini-tag-chooser
                      value=model.tags
                      tabindex=4
                      isDisabled=disableTagsChooser
                      onChange=(action (mut model.tags))
                      options=(hash
                        categoryId=model.categoryId
                        minimum=model.minimumRequiredTags
                      )
                    }}
                    {{popup-input-tip validation=tagValidation}}
                  {{/if}}
                </div>
              {{/if}}

              {{plugin-outlet name="composer-fields" args=(hash model=model)}}
            {{/unless}}

          </div>

          {{composer-editor topic=topic
                            composer=model
                            lastValidatedAt=lastValidatedAt
                            canWhisper=canWhisper
                            storeToolbarState=(action "storeToolbarState")
                            onPopupMenuAction=(action "onPopupMenuAction")
                            showUploadModal=(route-action "showUploadSelector")
                            popupMenuOptions=popupMenuOptions
                            draftStatus=model.draftStatus
                            isUploading=isUploading
                            allowUpload=allowUpload
                            uploadIcon=uploadIcon
                            isCancellable=isCancellable
                            uploadProgress=uploadProgress
                            groupsMentioned=(action "groupsMentioned")
                            cannotSeeMention=(action "cannotSeeMention")
                            importQuote=(action "importQuote")
                            togglePreview=(action "togglePreview")
                            showToolbar=showToolbar
                            afterRefresh=(action "afterRefresh")
                            focusTarget=focusTarget}}

          <div class='submit-panel'>
            {{plugin-outlet name="composer-fields-below" args=(hash model=model)}}

            <div class='save-or-cancel'>
              {{#unless model.viewFullscreen}}
                {{composer-save-button action=(action "save")
                                       icon=saveIcon
                                       label=saveLabel
                                       disableSubmit=disableSubmit}}

              {{#if site.mobileView}}
                <a href {{action "cancel"}} class='cancel' tabindex="6" title="{{i18n 'cancel'}}">
                  {{#if canEdit}}
                    {{d-icon "times"}}
                  {{else}}
                    {{d-icon "far-trash-alt"}}
                  {{/if}}
                </a>
              {{else}}
                <a href {{action "cancel"}} class='cancel' tabindex="6" >{{i18n 'cancel'}}</a>
              {{/if}}
            {{/unless}}


              {{#if site.mobileView}}
                {{#if whisperOrUnlistTopic}}
                  <span class='whisper'>
                    {{d-icon "far-eye-slash"}}
                  </span>
                {{/if}}
                {{#if model.noBump}}
                  <span class="no-bump">{{d-icon "anchor"}}</span>
                {{/if}}
              {{/if}}


              {{#if isUploading}}
                <div id="file-uploading">
                  {{loading-spinner size="small"}}<span>{{i18n 'upload_selector.uploading'}} {{uploadProgress}}%</span>
                  {{#if isCancellable}}
                    <a href id="cancel-file-upload" {{action "cancelUpload"}}>{{d-icon "times"}}</a>
                  {{/if}}
                </div>
              {{/if}}
              <div id='draft-status' class="{{if isUploading 'hidden'}}">
                {{#if model.draftSaving}}<div class="spinner small"></div>{{/if}}
                {{#if model.draftSaved}}{{d-icon 'check' class='save-animation'}}{{/if}}
                {{#if model.draftStatus}}
                  <span title="{{model.draftStatus}}">
                    {{#if model.draftConflictUser}}
                      {{avatar model.draftConflictUser imageSize="small"}} {{d-icon 'user-edit'}}
                    {{else}}
                      {{d-icon 'sync-alt'}}
                    {{/if}}
                    {{#unless site.mobileView}}
                      {{model.draftStatus}}
                    {{/unless}}
                  </span>
                {{/if}}
              </div>
            </div>

              {{#if site.mobileView}}
                {{#if allowUpload}}
                  <a class="btn btn-default no-text mobile-file-upload {{if isUploading 'hidden'}}">
                    {{d-icon uploadIcon}}
                  </a>
                {{/if}}

                <a href class="btn btn-default no-text mobile-preview" {{action "showPollBuilder"}}>
                  {{d-icon "chart-bar"}}
                </a>
                
                <a href class="btn btn-default no-text mobile-preview" title="{{i18n 'composer.show_preview'}}" {{action "togglePreview"}}>
                  {{d-icon "desktop"}}
                </a>

                {{#if showPreview}}
                  {{d-button action=(action "togglePreview") class="hide-preview" label="composer.hide_preview"}}
                {{/if}}
              {{else}}
                <a href {{action "togglePreview"}} class='toggle-preview'>{{{toggleText}}}</a>
              {{/if}}

          </div>
        </div>

      {{else}}
        <div class='saving-text'>
          {{#if model.createdPost}}
            {{i18n 'composer.saved'}} <a class='permalink' href="{{unbound createdPost.url}}" {{action "viewNewReply"}}>{{i18n 'composer.view_new_post'}}</a>
          {{else}}
            {{i18n 'composer.saving'}} {{loading-spinner size="small"}}
          {{/if}}
        </div>

        <div class='draft-text'>
          {{#if model.topic}}
            {{d-icon "share"}} {{{draftTitle}}}
          {{else}}
            {{i18n "composer.saved_draft"}}
          {{/if}}
        </div>

        {{composer-toggles composeState=model.composeState
          toggleFullscreen=(action "openIfDraft")
          toggleComposer=(action "toggle")
          toggleToolbar=(action "toggleToolbar")}}

      {{/if}}

  {{/if}}

{{/composer-body}}
</script>

彼の推測は以下の通りです:「コンポーザーの投票ボタンを事前に使用した場合にのみ機能します。Discourse は、ツールバーのアクション一覧を開くアイコン(詳細の非表示、スプォイラーのぼかし、投票の作成など)である onPopupMenuAction アクションをトリガーした後でなければ、投票ビルダーを開く showPollBuilder アクションをトリガーしないためではないかと思われます。」

どなたか引き受けていただけないでしょうか?:slight_smile:
(Steven さん、このような親切なご支援をいただき、改めてありがとうございます!)

おそらく、composer テンプレート全体を上書きする必要はありません。代わりに composer-fields-below アウトレットを使用してください。

ついつい、うさぎの穴に少し入り込んでしまいました。哈哈、気づけばもう2時間も経っちゃいましたね。


ボタンを表示するには、handlebars ファイルをこのように追加する必要があります。


そして、poll プラグインにあるようなアクションが必要です。

poll ビルダーモーダルに「偽の」ツールバーイベントを渡すことはできますが、問題は、エディターコンポーネント内のロジックも再現する必要があることです。

_getSelected_addText の部分です。「外部」からエディターのテキストエリアにプログラムでテキストを追加する API が存在するかどうか、とても興味があります。

それが当初の計画でした。テスト目的では、プラグインアウトレットが彼のテーマに合わせて調整する必要があるかもしれないため、composer テンプレートを書き換える方が簡単でした。

しかし、まずはコードを動作させることを優先し、後で適応させようとしていましたが、コーディングでつまずいてしまいました。

いいえ、私の知る限り、現時点ではそのような機能はありません。

ああ、なるほど、わかったよ。よくやってくれた!:slight_smile:

これは実際には見た目以上に難しいです(ボタンを右に移動させるだけなのに、どれくらい大変だろう? :))

私は少し試してみましたが、最も整理された方法は、コンポーザーエディターのツールバーをコントローラーのプロパティに割り当て、それをOutletからアクセスできるようにすることのようです。

ツールバーの動作を再現しようとすると、@spirobel が指摘した即座の問題に直面しますが、テキスト解析ロジックが調整されるにつれて、将来的に他の問題にも直面する可能性が高いでしょう。

基本的な原則は、抽象化の最も高いレベルで機能を使おうとすることです。つまり、実際のコンポーザーエディターのツールバーが必要なので、それを取り出しましょう。

具体的には:

api.modifyClass("component:d-editor", {
  @on('didInsertElement')
  makeToolbarAccessible() {
    if (this.outletArgs && this.outletArgs.editorType === 'composer') {
      const controller = getOwner(this).lookup('controller:composer');
      controller.set('editorToolbar', this.toolbar);
    }
  }
});

その後、Outlet側でコンポーザーコントローラーからツールバーを取得し、新しいボタンを追加します。既存の投票ボタンを利用できる方法があるかもしれません(それが理想ですが)、それは popupMenu のロジックにラップされており、そこから切り離す方法が見当たりませんでした。

setupComponent(attrs, ctx) {
  const controller = getOwner(this).lookup('controller:composer');
  
  controller.addObserver('editorToolbar', function() {
    if (this._state === 'destroying') return;

    const toolbar = controller.editorToolbar;
          
    toolbar.addButton({
      group: "extras",
      icon: "chart-bar",
      title: "poll.ui_builder.title",
      sendAction: e => {
        controller.send('storeToolbarState', e);
        this.send("showPollBuilder");
      }
    });
    
    const extras = toolbar.groups.find(g => g.group == 'extras');
    const pollButton = extras.buttons.find(b => b.icon == "chart-bar");
        
    ctx.set('pb', pollButton);
  });
}

そしてテンプレート自体では:

{{d-button
  type="button"
  action=pb.action
  actionParam=pb
  translatedTitle=pb.title
  icon=pb.icon
  class=pb.className}}

@nexo このテーマコンポーネントを試してみてください:discourse-poll-button-bottom.zip (2.5 KB)

追伸:thepavilion.io にロードしてみましたが、動作しているようです(モバイルのみ)。

わあ、それは素晴らしい解決策ですね!私の方法はそれに比べると本当に地味です:

export default {
  actions: {
    showPollBuilderNextToSave() {
$("button[title='Options']").trigger('click')



console.log($("button[title='Options']"))
Ember.run.later((function() {
  $("li[data-value='showPollBuilder']").trigger('click');
    console.log($("li[data-value='showPollBuilder']"))
 
}), 50);

    }
  }
};

単に jQuery を使ってボタンをクリックしているだけです :smiley: もしこれが問題になる可能性があるなら、オプションのタイトルをハードコーディングせずに最初のセレクターを見つけるより良い方法があるかもしれませんね。

いいね!:+1: それも選択肢の一つですね。

わあ、言葉が出ないよ、アングス…これは信じられない、あなたは天才だね!今日から開発の勉強を始めるように奮い立たせてくれた。1年前にヨハニが私にCSSを学ばせてくれたのと同じように。ありがとう、アングス。本当に素晴らしいよ。:trophy: