Skip to content

DEV: Add compatibility with the Glimmer Post Stream #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .discourse-compatibility
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
< 3.5.0.beta8-dev: 52682ad08a831c7e1a1e67ce47e20796b3fa9df0
< 3.5.0.beta5-dev: be64a5ea30dcda658a74e22a9e7b5fd8cd7632c8
< 3.5.0.beta1-dev: b7181ad63238adf843d27b2d0db13cb6354df379
< 3.4.0.beta2-dev: ff810c65d88e3a208b1126e94ec9ba637d6e997e
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Component from "@glimmer/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import icon from "discourse/helpers/d-icon";
import emoji from "discourse/helpers/emoji";
import { showUserNotes, updatePostUserNotesCount } from "../lib/user-notes";

export default class PostMetadataUserNotes extends Component {
@service siteSettings;
@service store;

@action
showNotes() {
showUserNotes(
this.store,
this.args.post.user_id,
(count) => updatePostUserNotesCount(this.args.post, count),
{
postId: this.args.post.id,
}
);
}

<template>
{{! template-lint-disable no-invalid-interactive }}
<span class="user-notes-icon" {{on "click" this.showNotes}}>
{{#if this.siteSettings.enable_emoji}}
{{emoji "memo"}}
{{else}}
{{icon "pen-to-square"}}
{{/if}}
</span>
</template>
}
194 changes: 194 additions & 0 deletions assets/javascripts/discourse/initializers/enable-user-notes.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import Component from "@glimmer/component";
import { withSilencedDeprecations } from "discourse/lib/deprecated";
import { iconNode } from "discourse/lib/icon-library";
import { withPluginApi } from "discourse/lib/plugin-api";
import { applyValueTransformer } from "discourse/lib/transformer";
import PostMetadataUserNotes from "../components/post-metadata-user-notes";
import { showUserNotes, updatePostUserNotesCount } from "../lib/user-notes";

/**
* Plugin initializer for enabling user notes functionality
*/
export default {
name: "enable-user-notes",
initialize(container) {
const siteSettings = container.lookup("service:site-settings");
const currentUser = container.lookup("service:current-user");

if (!siteSettings.user_notes_enabled || !currentUser?.staff) {
return;
}

withPluginApi((api) => {
customizePost(api, container);
customizePostMenu(api, container);
});
},
};

/**
* Customizes how user notes are displayed in posts
*
* @param {Object} api - Plugin API instance
* @param {Object} container - Container instance
*/
function customizePost(api, container) {
const siteSettings = container.lookup("service:site-settings");

const placement = applyValueTransformer(
"user-notes-icon-placement",
siteSettings.user_notes_icon_placement
);

// Component to display user notes flair icon
class UserNotesPostMetadataFlairIcon extends Component {
static shouldRender(args) {
return args.post?.user_custom_fields?.user_notes_count > 0;
}

<template><PostMetadataUserNotes @post={{@post}} /></template>
}

// Handle placement next to avatar
if (placement === "avatar") {
api.renderAfterWrapperOutlet(
"poster-avatar",
UserNotesPostMetadataFlairIcon
);
}
// Handle placement next to username
else if (placement === "name") {
// Mobile-specific version
class MobileUserNotesIcon extends UserNotesPostMetadataFlairIcon {
static shouldRender(args, context) {
return context.site.mobileView && super.shouldRender(args);
}
}

// Desktop-specific version
class DesktopUserNotesIcon extends UserNotesPostMetadataFlairIcon {
static shouldRender(args, context) {
return !context.site.mobileView && super.shouldRender(args);
}
}

api.renderBeforeWrapperOutlet(
"post-meta-data-poster-name",
MobileUserNotesIcon
);
api.renderAfterWrapperOutlet(
"post-meta-data-poster-name",
DesktopUserNotesIcon
);
}

withSilencedDeprecations("discourse.post-stream-widget-overrides", () =>
customizeWidgetPost(api)
);
}

/**
* Customizes the post widget to display user notes
*
* @param {Object} api - Plugin API instance
*/
function customizeWidgetPost(api) {
// Handler for showing user notes modal
function widgetShowUserNotes() {
showUserNotes(
this.store,
this.attrs.user_id,
(count) => {
this.sendWidgetAction("refreshUserNotes", count);
},
{
postId: this.attrs.id,
}
);
}

// Update post when notes are changed
api.attachWidgetAction("post", "refreshUserNotes", function (count) {
updatePostUserNotesCount(this.model, count);
});

const mobileView = api.container.lookup("service:site").mobileView;
const loc = mobileView ? "before" : "after";

// Helper to attach notes icon if user has notes
const attachUserNotesIconIfPresent = (dec) => {
const post = dec.getModel();
if (post?.user_custom_fields?.user_notes_count > 0) {
return dec.attach("user-notes-icon");
}
};

// Add notes icon to poster name
api.decorateWidget(`poster-name:${loc}`, (dec) => {
if (dec.widget.settings.hideNotes) {
return;
}

return attachUserNotesIconIfPresent(dec);
});

// Add notes icon after avatar
api.decorateWidget(`post-avatar:after`, (dec) => {
if (!dec.widget.settings.showNotes) {
return;
}

return attachUserNotesIconIfPresent(dec);
});

api.attachWidgetAction("post", "showUserNotes", widgetShowUserNotes);

// Create the user notes icon widget
api.createWidget("user-notes-icon", {
services: ["site-settings"],

tagName: "span.user-notes-icon",
click: widgetShowUserNotes,

html() {
if (this.siteSettings.enable_emoji) {
return this.attach("emoji", { name: "memo" });
} else {
return iconNode("pen-to-square");
}
},
});
}

/**
* Adds user notes button to post admin menu
*
* @param {Object} api - Plugin API instance
* @param {Object} container - Container instance
*/
function customizePostMenu(api, container) {
const appEvents = container.lookup("service:app-events");
const store = container.lookup("service:store");

api.addPostAdminMenuButton((attrs) => {
return {
icon: "pen-to-square",
label: "user_notes.attach",
action: (post) => {
showUserNotes(
store,
attrs.user_id,
(count) => {
updatePostUserNotesCount(post, count);
appEvents.trigger("post-stream:refresh", {
id: post.id,
});
},
{ postId: attrs.id }
);
},
secondaryAction: "closeAdminMenu",
className: "add-user-note",
};
});
}
114 changes: 0 additions & 114 deletions assets/javascripts/discourse/initializers/enable-user-notes.js

This file was deleted.

6 changes: 6 additions & 0 deletions assets/javascripts/discourse/lib/user-notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ export function showUserNotes(store, userId, callback, opts) {
});
});
}

export function updatePostUserNotesCount(post, count) {
const cfs = post.user_custom_fields || {};
cfs.user_notes_count = count;
post.user_custom_fields = cfs;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { withPluginApi } from "discourse/lib/plugin-api";

export default {
before: "freeze-valid-transformers",

initialize() {
withPluginApi((api) => {
api.addValueTransformerName("user-notes-icon-placement");
});
},
};
1 change: 1 addition & 0 deletions config/locales/server.en.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
en:
site_settings:
user_notes_enabled: "Allow staff users to attach notes to users"
user_notes_icon_placement: "Placement of the user notes indicative icon in the posts"
user_notes_moderators_delete: "Allow moderators to delete user notes"

user_notes:
Expand Down
7 changes: 7 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ plugins:
user_notes_enabled:
default: false
client: true
user_notes_icon_placement:
client: true
type: enum
default: "name"
choices:
- name
- avatar
user_notes_moderators_delete:
default: true
client: false