Can someone please explain to me how the User Card pop-up works?

Guys, i’m trying to work out how the code for User Card works.

How does it create a pop-up and then escalate to a new page?

<a href="/u/postinguser" data-user-card="postinguser" class=""><img alt="" src="/letter_avatar_proxy/v2/letter/m/bbce88/50.png" class="avatar" title="User - Frequent Poster" width="25" height="25"></a>

I can see how it is constructed from the .hbs template file, but not how the action of clicking it causes the user card to appear …

<a href="{{poster.user.path}}" data-user-card="{{poster.user.username}}" class="{{poster.extraClasses}}">{{avatar poster avatarTemplatePath="user.avatar_template" usernamePath="user.username" imageSize="small"}}</a>

But how does the javascript fit together?

What code is causing the user card to load instead of going to the user route?

I see:

/app/assets/javascripts/discourse/widgets/poster-name.js.es6

and specifically:

export default createWidget("poster-name", {
  tagName: "div.names.trigger-user-card",

  settings: {
    showNameAndGroup: true,
    showGlyph: true
  },

  // TODO: Allow extensibility
  posterGlyph(attrs) {
    if (attrs.moderator) {
      return iconNode("shield", { title: I18n.t("user.moderator_tooltip") });
    }
  },

  userLink(attrs, text) {
    return h(
      "a",
      {
        attributes: {
          href: attrs.usernameUrl,
          "data-user-card": attrs.username
        }
      },
      formatUsername(text)
    );
  },

  html(attrs) {
    const username = attrs.username;
    const name = attrs.name;
    const nameFirst =
      this.siteSettings.display_name_on_posts &&
      !this.siteSettings.prioritize_username_in_ux &&
      name &&
      name.trim().length > 0;
    const classNames = nameFirst
      ? ["first", "full-name"]
      : ["first", "username"];

    if (attrs.staff) {
      classNames.push("staff");
    }
    if (attrs.admin) {
      classNames.push("admin");
    }
    if (attrs.moderator) {
      classNames.push("moderator");
    }
    if (attrs.new_user) {
      classNames.push("new-user");
    }

    let afterNameContents =
      applyDecorators(this, "after-name", attrs, this.state) || [];

    const primaryGroupName = attrs.primary_group_name;
    if (primaryGroupName && primaryGroupName.length) {
      classNames.push(primaryGroupName);
    }
    let nameContents = [this.userLink(attrs, nameFirst ? name : username)];

    if (this.settings.showGlyph) {
      const glyph = this.posterGlyph(attrs);
      if (glyph) {
        nameContents.push(glyph);
      }
    }
    nameContents = nameContents.concat(afterNameContents);

    const contents = [
      h("span", { className: classNames.join(" ") }, nameContents)
    ];

    if (!this.settings.showNameAndGroup) {
      return contents;
    }

    if (
      name &&
      this.siteSettings.display_name_on_posts &&
      sanitizeName(name) !== sanitizeName(username)
    ) {
      contents.push(
        h(
          "span.second." + (nameFirst ? "username" : "full-name"),
          [this.userLink(attrs, nameFirst ? username : name)].concat(
            afterNameContents
          )
        )
      );
    }

    const title = attrs.user_title;
    if (title && title.length) {
      contents.push(
        this.attach("poster-name-title", { title, primaryGroupName })
      );
    }

    return contents;
  }

Can someone talk me through this latter code wrt to launching the user card in a state which will permit a further click to the user’s profile?

1 Like

I think you’re looking for card-contents-base.js.es6, which defines a series of events which point to the _show method.

# card-contents-base.js.es6#103
$("#main-outlet").on(clickDataExpand, `[data-${id}]`, e => {});

$("#main-outlet").on(clickMention, `a.${triggeringLinkClass}`, e => {});

this.appEvents.on(previewClickEvent, $target => {});
# card-contents-base.js.es6#20
_show(username, $target) {}
6 Likes

Thanks James! I’ll have a read through that code … head spinning already though … :slight_smile:

1 Like

What is the thing that you’re trying to do?

1 Like

Trying to understand how the user card is wired in so I can ultimately code the same behaviour in a plug-in.

2 Likes

@merefield were you ever able to figure out how to do this?

I’m trying to figure out how to create a theme component to modify the User Map of the the Locations plugin so that when clicked, it shows the profile card instead of going directly to the profile page, but I also have no idea where to start or look.

oooh this is 5 years old … I can’t remember :sweat_smile: … I should imagine the code is very different now.

I would accept that functionality as a PR to Locations as it’s been requested before plenty of times.

2 Likes

haha yes that makes sense. I may dig into it soon and then if I figure out how to PR plugins, I’ll put it up :pray:t2:

1 Like