-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
85ad071
commit f719a42
Showing
9 changed files
with
391 additions
and
351 deletions.
There are no files selected for viewing
155 changes: 155 additions & 0 deletions
155
javascripts/discourse/components/topic-list-thumbnail.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import Component from "@glimmer/component"; | ||
import { service } from "@ember/service"; | ||
import concatClass from "discourse/helpers/concat-class"; | ||
import dIcon from "discourse-common/helpers/d-icon"; | ||
|
||
export default class TopicListThumbnail extends Component { | ||
@service topicThumbnails; | ||
|
||
responsiveRatios = [1, 1.5, 2]; | ||
|
||
// Make sure to update about.json thumbnail sizes if you change these variables | ||
get displayWidth() { | ||
return this.topicThumbnails.displayList | ||
? settings.list_thumbnail_size | ||
: 400; | ||
} | ||
|
||
get topic() { | ||
return this.args.topic; | ||
} | ||
|
||
get hasThumbnail() { | ||
return !!this.topic.thumbnails; | ||
} | ||
|
||
get srcSet() { | ||
const srcSetArray = []; | ||
|
||
this.responsiveRatios.forEach((ratio) => { | ||
const target = ratio * this.displayWidth; | ||
const match = this.topic.thumbnails.find( | ||
(t) => t.url && t.max_width === target | ||
); | ||
if (match) { | ||
srcSetArray.push(`${match.url} ${ratio}x`); | ||
} | ||
}); | ||
|
||
if (srcSetArray.length === 0) { | ||
srcSetArray.push(`${this.original.url} 1x`); | ||
} | ||
|
||
return srcSetArray.join(","); | ||
} | ||
|
||
get original() { | ||
return this.topic.thumbnails[0]; | ||
} | ||
|
||
get width() { | ||
return this.original.width; | ||
} | ||
|
||
get isLandscape() { | ||
return this.original.width >= this.original.height; | ||
} | ||
|
||
get height() { | ||
return this.original.height; | ||
} | ||
|
||
get fallbackSrc() { | ||
const largeEnough = this.topic.thumbnails.filter((t) => { | ||
if (!t.url) { | ||
return false; | ||
} | ||
return t.max_width > this.displayWidth * this.responsiveRatios.lastObject; | ||
}); | ||
|
||
if (largeEnough.lastObject) { | ||
return largeEnough.lastObject.url; | ||
} | ||
|
||
return this.original.url; | ||
} | ||
|
||
get url() { | ||
return this.topic.linked_post_number | ||
? this.topic.urlForPostNumber(this.topic.linked_post_number) | ||
: this.topic.get("lastUnreadUrl"); | ||
} | ||
|
||
<template> | ||
<div | ||
class={{concatClass | ||
"topic-list-thumbnail" | ||
(if this.hasThumbnail "has-thumbnail" "no-thumbnail") | ||
}} | ||
> | ||
<a href={{this.url}}> | ||
{{#if this.hasThumbnail}} | ||
<img | ||
class="background-thumbnail" | ||
src={{this.fallbackSrc}} | ||
srcset={{this.srcSet}} | ||
width={{this.width}} | ||
height={{this.height}} | ||
loading="lazy" | ||
/> | ||
<img | ||
class="main-thumbnail" | ||
src={{this.fallbackSrc}} | ||
srcset={{this.srcSet}} | ||
width={{this.width}} | ||
height={{this.height}} | ||
loading="lazy" | ||
/> | ||
{{else}} | ||
<div class="thumbnail-placeholder"> | ||
{{dIcon settings.placeholder_icon}} | ||
</div> | ||
{{/if}} | ||
</a> | ||
</div> | ||
|
||
{{#if this.topicThumbnails.showLikes}} | ||
<div class="topic-thumbnail-likes"> | ||
{{dIcon "heart"}} | ||
<span class="number"> | ||
{{this.topic.like_count}} | ||
</span> | ||
</div> | ||
{{/if}} | ||
|
||
{{#if this.topicThumbnails.displayBlogStyle}} | ||
<div class="topic-thumbnail-blog-data"> | ||
<div class="topic-thumbnail-blog-data-views"> | ||
{{dIcon "eye"}} | ||
<span class="number"> | ||
{{this.topic.views}} | ||
</span> | ||
</div> | ||
<div class="topic-thumbnail-blog-data-likes"> | ||
{{dIcon "heart"}} | ||
<span class="number"> | ||
{{this.topic.like_count}} | ||
</span> | ||
</div> | ||
<div class="topic-thumbnail-blog-data-comments"> | ||
{{dIcon "comment"}} | ||
<span class="number"> | ||
{{this.topic.reply_count}} | ||
</span> | ||
</div> | ||
{{! TODO }} | ||
{{!-- {{raw | ||
"list/activity-column" | ||
topic=this.topic | ||
class="topic-thumbnail-blog-data-activity" | ||
tagName="div" | ||
}} --}} | ||
</div> | ||
{{/if}} | ||
</template> | ||
} |
45 changes: 45 additions & 0 deletions
45
javascripts/discourse/connectors/before-topic-list-body/topic-list-resize-observer.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import Component from "@glimmer/component"; | ||
import { service } from "@ember/service"; | ||
import { modifier } from "ember-modifier"; | ||
|
||
export default class extends Component { | ||
@service topicThumbnails; | ||
|
||
attachResizeObserver = modifier((element) => { | ||
const topicList = element.closest(".topic-list"); | ||
|
||
if (!topicList) { | ||
// eslint-disable-next-line no-console | ||
console.error( | ||
"topic-list-thumbnails resize-observer must be inside a topic-list" | ||
); | ||
return; | ||
} | ||
|
||
this.topicThumbnails.masonryContainerWidth = | ||
topicList.getBoundingClientRect().width; | ||
|
||
const observer = new ResizeObserver(() => { | ||
this.topicThumbnails.masonryContainerWidth = | ||
topicList.getBoundingClientRect().width; | ||
console.log( | ||
"ResizeObserver triggered", | ||
this.topicThumbnails.masonryContainerWidth | ||
); | ||
}); | ||
observer.observe(topicList); | ||
|
||
return () => { | ||
observer.disconnect(); | ||
this.topicThumbnails.masonryContainerWidth = null; | ||
}; | ||
}); | ||
|
||
<template> | ||
{{#if this.topicThumbnails.displayMasonry}} | ||
<div style="display: none;" {{this.attachResizeObserver}}> | ||
<!-- topic-list-thumbnails resize-observer --> | ||
</div> | ||
{{/if}} | ||
</template> | ||
} |
1 change: 1 addition & 0 deletions
1
javascripts/discourse/connectors/topic-list-before-columns/topic-thumbnail.hbr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
{{!-- has-modern-replacement --}} | ||
{{~raw "topic-list-thumbnail" topic=context.topic location="before-columns"}} |
1 change: 1 addition & 0 deletions
1
javascripts/discourse/connectors/topic-list-before-link/topic-thumbnail.hbr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
{{!-- has-modern-replacement --}} | ||
{{~raw "topic-list-thumbnail" topic=context.topic location="before-link"}} | ||
|
113 changes: 113 additions & 0 deletions
113
javascripts/discourse/initializers/topic-thumbnails-init.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { readOnly } from "@ember/object/computed"; | ||
import { service } from "@ember/service"; | ||
import { apiInitializer } from "discourse/lib/api"; | ||
import TopicListThumbnail from "../components/topic-list-thumbnail"; | ||
import MasonryCalculator from "../lib/masonry-calculator"; | ||
|
||
export default apiInitializer("0.8", (api) => { | ||
const ttService = api.container.lookup("service:topic-thumbnails"); | ||
|
||
api.registerValueTransformer("topic-list-class", ({ value }) => { | ||
if (ttService.displayMinimalGrid) { | ||
value.push("topic-thumbnails-minimal"); | ||
} else if (ttService.displayGrid) { | ||
value.push("topic-thumbnails-grid"); | ||
} else if (ttService.displayList) { | ||
value.push("topic-thumbnails-list"); | ||
} else if (ttService.displayMasonry) { | ||
value.push("topic-thumbnails-masonry"); | ||
} else if (ttService.displayBlogStyle) { | ||
value.push("topic-thumbnails-blog-style-grid"); | ||
} | ||
return value; | ||
}); | ||
|
||
api.registerValueTransformer("topic-list-columns", ({ value: columns }) => { | ||
if (ttService.enabledForRoute && !ttService.displayList) { | ||
columns.add( | ||
"thumbnail", | ||
{ | ||
item: TopicListThumbnail, | ||
}, | ||
{ before: "topic" } | ||
); | ||
} | ||
return columns; | ||
}); | ||
|
||
api.renderInOutlet("topic-list-before-link", <template> | ||
{{#if ttService.displayList}} | ||
<TopicListThumbnail @topic={{@outletArgs.topic}} /> | ||
{{/if}} | ||
</template>); | ||
|
||
api.registerValueTransformer("topic-list-item-mobile-layout", ({ value }) => { | ||
if (ttService.enabledForRoute && !ttService.displayList) { | ||
// Force the desktop layout | ||
return false; | ||
} | ||
return value; | ||
}); | ||
|
||
// Todo: what do do with docs? | ||
const siteSettings = api.container.lookup("service:site-settings"); | ||
if (settings.docs_thumbnail_mode !== "none" && siteSettings.docs_enabled) { | ||
api.modifyClass("component:docs-topic-list", { | ||
pluginId: "topic-thumbnails", | ||
topicThumbnailsService: service("topic-thumbnails"), | ||
classNameBindings: [ | ||
"isMinimalGrid:topic-thumbnails-minimal", | ||
"isThumbnailGrid:topic-thumbnails-grid", | ||
"isThumbnailList:topic-thumbnails-list", | ||
"isMasonryList:topic-thumbnails-masonry", | ||
"isBlogStyleGrid:topic-thumbnails-blog-style-grid", | ||
], | ||
isMinimalGrid: readOnly("topicThumbnailsService.displayMinimalGrid"), | ||
isThumbnailGrid: readOnly("topicThumbnailsService.displayGrid"), | ||
isThumbnailList: readOnly("topicThumbnailsService.displayList"), | ||
isMasonryList: readOnly("topicThumbnailsService.displayMasonry"), | ||
isBlogStyleGrid: readOnly("topicThumbnailsService.displayBlogStyle"), | ||
}); | ||
} | ||
|
||
// Masonry Layout | ||
api.registerValueTransformer( | ||
"topic-list-style", | ||
({ value, context: { topics } }) => { | ||
if (!ttService.displayMasonry) { | ||
return; | ||
} | ||
|
||
if (!ttService.masonryContainerWidth) { | ||
return; | ||
} | ||
|
||
const calculator = new MasonryCalculator( | ||
api.container, | ||
topics, | ||
ttService.masonryContainerWidth | ||
); | ||
calculator.calculateMasonryLayout(); | ||
value += calculator.masonryStyle; | ||
|
||
return value; | ||
} | ||
); | ||
|
||
api.registerValueTransformer( | ||
"topic-list-item-style", | ||
({ value, context }) => { | ||
if (ttService.displayMasonry) { | ||
const masonryData = context.topic.get("masonryData"); | ||
if (masonryData) { | ||
value += [ | ||
`--masonry-height: ${Math.round(masonryData.height)}px;`, | ||
`--masonry-height-above: ${Math.round(masonryData.heightAbove)}px;`, | ||
`--masonry-column-index: ${masonryData.columnIndex};`, | ||
].join(""); | ||
} | ||
} | ||
return value; | ||
} | ||
); | ||
}); |
Oops, something went wrong.