새 주제 또는 업데이트된 주제 알림을 고정으로 표시

안녕하세요,

새 주제 x개 또는 업데이트된 주제 보기 항목을 헤더 바로 아래에 고정(sticky) 처리하면, 주제 목록을 스크롤할 때 즉시 확인이 가능할 것입니다. 해당 바를 클릭하면 상단으로 이동하여 새 주제나 업데이트된 주제를 로드할 수 있습니다.

고정(sticky) 처리는 다음을 통해 잘 작동합니다 :arrow_down:

#list-area .show-more.has-topics {
  position: sticky;
  top: var(--header-offset);
}

나머지 부분(JavaScript)은 상단으로 점프하거나 스크롤하는 클릭 기능을 구현해야 하는데, 어떻게 해야 하는지 또는 가장 좋은 방법은 무엇인지 잘 모르겠습니다.

discovery/topics.hbs와 discovery/categories.hbs 템플릿에서 이 섹션을 찾았습니다. <a href를 <a href="/"로 수정하면 작동할 것 같지만 (확신은 없으며, 시도해 보지 않았습니다) 이렇게 하면 로고를 클릭할 때와 마찬가지로 매번 페이지가 새로 로드됩니다.

도와주셔서 감사합니다 :slightly_smiling_face:

네, URL을 추가하면 이벤트를 가로채는 로직이 없기 때문에 네비게이션이 발생합니다.

해당 링크는 Ember 액션을 호출하고 있습니다. Discourse의 모든 액션은 확장 가능합니다. 따라서 일종의 커스터마이징 훅처럼 작동합니다. 그러면 액션을 어떻게 수정해야 할까요? 먼저 해당 액션이 무엇을 하는지 확인해 보겠습니다.

Github 또는 로컬에서 액션 이름을 검색하세요. 액션은 항상 JS 파일에서 정의되고 handlebars에서 참조됩니다. 우리는 정의를 보고 싶으므로 검색 범위를 JS 파일로 좁혀야 합니다.

Repository search results · GitHub

4개의 파일이 나옵니다. 어떤 파일을 봐야 할까요? 우리는

discovery/topics.hbs

템플릿을 커스터마이징하려는 것이므로

discovery/topics.js

파일을 확인해야 합니다.

이 코드 중 도움이 되는 것이 있나요? 아니요, 하지만 이제 액션이 어디에서 정의되는지 알게 되었습니다. 이제 수정해 보겠습니다.

discovery/topics.js는 Ember 클래스입니다. 플러그인 API의 메서드를 사용하여 Ember 클래스를 수정할 수 있는데, 그 이름은… modifyClass 입니다 :stuck_out_tongue:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js#L166-L195

수정하려는 클래스는 이미 알고 있습니다. 바로 discovery/topics입니다. 어떤 종류의 클래스인지 알아야 합니다. 따라서 파일 디렉터리를 확인해 보겠습니다.

discourse/app/controllers/discovery/topics.js

컨트롤러입니다.

또한 해당 클래스의 showInserted 액션을 수정하려는 것도 알고 있습니다. 따라서 다음 코드로 시작합니다.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
      // 여기서 작업을 수행합니다
    }
  }
});

그 후, 사용자가 “새 주제” 배너를 클릭할 때 창을 스크롤하기 위한 원하는 코드를 추가할 수 있습니다. 저는 다음과 같은 코드를 사용했습니다.

const listControls = document.querySelector(".list-controls");
listControls.scrollIntoView();

scrollIntoView()에 대해 더 자세히 알고 싶다면 여기에서 읽을 수 있습니다.

그 다음 플러그인 API 메서드에 다음과 같이 추가합니다.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
+     const listControls = document.querySelector(".list-controls");
+     listControls.scrollIntoView();
    }
  }
});

자, 끝난 건가요? 아니요. 이렇게 하면 액션을 완전히 오버라이드하기 때문에 Discourse가 깨집니다. 링크를 클릭하면 이제 list-controls 요소로 스크롤되지만, 새 주제는 로드되지 않습니다. 왜 그런가요? 코어에 있는 코드가 더 이상 적용되지 않기 때문입니다. 즉, 이 코드 말이에요

그렇다면 어떻게 수정할까요? 이 간단한 한 줄로 가능합니다.

this._super(...arguments);

코어에서 코드를 복사할 필요 없이 단순히 추가하고 싶다면, 작업을 수행한 후 이 줄을 추가하면 됩니다. 이 줄은 코어의 코드가 적용되도록 보장하는 역할만 합니다.

api.modifyClass("controller:discovery/topics", {
  pluginId: 'sticky-new-topics-banner',
  actions: {
    showInserted() {
     const listControls = document.querySelector(".list-controls");
     listControls.scrollIntoView();
+
+    this._super(...arguments);
    }
  }
});

이것을 테스트해 보면 거의 모든 것이 잘 작동하지만, 한 가지 문제가 있습니다… 헤더가 list-controls와 겹칩니다. 왜 그런가요? 헤더가 sticky(고정)로 설정되어 있기 때문입니다.

JS에서 이를 수정하는 방법은 여러 가지가 있습니다 - 높이를 계산하거나, 오프셋을 가져오거나, Discourse에서 헬퍼를 가져오거나… 등등. 여기서는 그 세부 사항에 대해 다루지 않겠습니다.

가장 쉬운 방법은 CSS의 scroll-margin-top을 사용하는 것이며, 이에 대해 더 자세히 알고 싶다면 여기에서 읽을 수 있습니다.

따라서 다음 코드를 추가합니다.

.list-controls {
  scroll-margin-top: calc(var(--header-offset) * 2);
}

쉽게 말해, 링크가 클릭되면 list-controls의 상단에서 헤더 높이의 2배만큼 위쪽으로 스크롤하여 겹치지 않고 아래에 약간의 여백이 생기도록 하는 것입니다.

자, 이제 이 모든 것을 합쳐 보겠습니다.

공통 헤더 탭

<script type="text/discourse-plugin" version="0.8">
api.modifyClass("controller:discovery/topics", {
  pluginId: "sticky-new-topics-banner",
  actions: {
    showInserted() {
      const listControls = document.querySelector(".list-controls");
      listControls.scrollIntoView();
      this._super(...arguments);
    }
  }
});
</script>

공통 CSS

#list-area {
  // 모바일은 레이아웃이 다릅니다
  .alert-info,
  .show-more.has-topics {
    position: sticky;
    // 프리픽스가 없으면 Safari가 가끔 문제를 일으킬 수 있습니다
    position: -webkit-sticky;
    top: var(--header-offset);
    // 배너는 콘텐츠 위에 있어야 합니다
    z-index: z("header");
  }
}

.list-controls {
  scroll-margin-top: calc(var(--header-offset) * 2);
}

@Johani님, 감사합니다! 저에게 정말 큰 도움이 되었고, 이제 많은 것을 드디어 이해하게 된 것 같습니다. 상세한 답변을 정말 좋아합니다. 왜냐하면 그걸 통해 어떻게 작동하는지 많이 배울 수 있고, 실제로 완벽하게 작동하기 때문이죠. 다시 한번 감사드립니다! :slightly_smiling_face: