Skip to content

Commit 9875cc1

Browse files
authored
Add commenter device info display feature (#189)
1 parent 0a9ee14 commit 9875cc1

8 files changed

Lines changed: 194 additions & 7 deletions

File tree

packages/comment-widget/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"javascript-time-ago": "^2.5.11",
4949
"lit": "^3.3.1",
5050
"ofetch": "^1.4.1",
51-
"tiptap-extension-code-block-shiki": "file:lib/tiptap-extension-code-block-shiki-0.5.1.tgz"
51+
"tiptap-extension-code-block-shiki": "file:lib/tiptap-extension-code-block-shiki-0.5.1.tgz",
52+
"ua-parser-js": "^2.0.4"
5253
},
5354
"devDependencies": {
5455
"@iconify/json": "^2.2.367",

packages/comment-widget/src/base-comment-item.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import './user-avatar';
22
import { msg } from '@lit/localize';
33
import { css, html, LitElement, unsafeCSS } from 'lit';
4-
import { property } from 'lit/decorators.js';
4+
import { property, state } from 'lit/decorators.js';
55
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
66
import { codeToHtml } from 'shiki/bundle/full';
77
import baseStyles from './styles/base';
88
import contentStyles from './styles/content.css?inline';
99
import { formatDate, timeAgo } from './utils/date';
10+
import './commenter-ua-bar';
11+
import { consume } from '@lit/context';
12+
import { configMapDataContext } from './context';
13+
import type { ConfigMapData } from './types';
1014

1115
export class BaseCommentItem extends LitElement {
1216
@property({ type: String })
@@ -30,6 +34,13 @@ export class BaseCommentItem extends LitElement {
3034
@property({ type: String })
3135
content = '';
3236

37+
@property({ type: String })
38+
ua: string = '';
39+
40+
@consume({ context: configMapDataContext })
41+
@state()
42+
configMapData: ConfigMapData | undefined;
43+
3344
protected override firstUpdated() {
3445
const codeElements = this.shadowRoot?.querySelectorAll('pre>code');
3546
if (!codeElements?.length) return;
@@ -81,22 +92,26 @@ export class BaseCommentItem extends LitElement {
8192
></user-avatar>
8293
</div>
8394
<div class="item-main flex-[1_1_auto] min-w-0 w-full">
84-
<div class="item-meta flex items-center gap-3">
95+
<div class="item-meta flex items-center gap-3 flex-wrap">
8596
${
8697
this.userWebsite
8798
? html`<a
88-
class="item-author font-medium text-sm"
99+
class="item-author font-medium text-sm text-text-1"
89100
target="_blank"
90101
href=${this.userWebsite}
91102
rel="noopener noreferrer"
92103
>
93104
${this.userDisplayName}
94105
</a>`
95-
: html`<div class="item-author font-medium text-sm">${this.userDisplayName}</div>`
106+
: html`<span class="item-author font-medium text-sm text-text-1">${this.userDisplayName}</span>`
96107
}
97-
<div class="item-meta-info text-xs text-text-2" title=${formatDate(this.creationTime)}>
108+
109+
${this.ua && this.configMapData?.basic.showCommenterDevice ? html`<commenter-ua-bar .ua=${this.ua}></commenter-ua-bar>` : ''}
110+
111+
<time class="item-meta-info text-xs text-text-2" title=${formatDate(this.creationTime)}>
98112
${timeAgo(this.creationTime)}
99-
</div>
113+
</time>
114+
100115
${
101116
!this.approved
102117
? html`<div class="item-meta-info text-xs text-text-2">${msg('Reviewing')}</div>`

packages/comment-widget/src/comment-item.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export class CommentItem extends LitElement {
119119
.creationTime="${this.comment?.spec.creationTime}"
120120
.approved=${this.comment?.spec.approved}
121121
.userWebsite=${this.comment?.spec.owner.annotations?.website}
122+
.ua=${this.comment?.spec.userAgent}
122123
>
123124
<button slot="action" class="icon-button group -ml-2" type="button" @click="${this.handleUpvote}">
124125
<div class="icon-button-icon">
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { css, html, LitElement } from 'lit';
2+
import { property, state } from 'lit/decorators.js';
3+
import type { UAParser } from 'ua-parser-js';
4+
import baseStyles from './styles/base';
5+
6+
const OS_ICON_MAP = {
7+
Windows: 'i-logos:microsoft-windows-icon',
8+
macOS: 'i-logos:apple',
9+
Linux: 'i-logos:linux-tux',
10+
Android: 'i-logos:android-icon',
11+
iOS: 'i-logos:apple',
12+
'Chrome OS': 'i-logos:chrome',
13+
Arch: 'i-logos:archlinux',
14+
Manjaro: 'i-logos:manjaro',
15+
Ubuntu: 'i-logos:ubuntu',
16+
Fedora: 'i-logos:fedora',
17+
};
18+
19+
const BROWSER_ICON_MAP = {
20+
Chrome: 'i-logos:chrome',
21+
'Mobile Chrome': 'i-logos:chrome',
22+
'Chrome WebView': 'i-logos:chrome',
23+
Firefox: 'i-logos:firefox',
24+
'Mobile Firefox': 'i-logos:firefox',
25+
Safari: 'i-logos:safari',
26+
'Mobile Safari': 'i-logos:safari',
27+
Edge: 'i-logos:microsoft-edge',
28+
'Edge WebView': 'i-logos:microsoft-edge',
29+
'Edge WebView2': 'i-logos:microsoft-edge',
30+
Opera: 'i-logos:opera',
31+
};
32+
33+
export class CommenterUABar extends LitElement {
34+
@property({ type: String })
35+
ua: string = '';
36+
37+
@state()
38+
parser: UAParser | undefined;
39+
40+
getOSIcon(os?: string) {
41+
return OS_ICON_MAP[os as keyof typeof OS_ICON_MAP];
42+
}
43+
44+
getBrowserIcon(browser?: string) {
45+
return BROWSER_ICON_MAP[browser as keyof typeof BROWSER_ICON_MAP];
46+
}
47+
48+
override connectedCallback(): void {
49+
super.connectedCallback();
50+
this.init();
51+
}
52+
53+
async init() {
54+
const { UAParser } = await import('ua-parser-js');
55+
this.parser = new UAParser(this.ua);
56+
}
57+
58+
protected override render() {
59+
if (!this.parser) {
60+
return html``;
61+
}
62+
63+
const osIcon = this.getOSIcon(this.parser.getOS().name);
64+
const browserIcon = this.getBrowserIcon(this.parser.getBrowser().name);
65+
66+
return html`<div class="inline-flex gap-2 items-center">
67+
<div class="inline-flex items-center gap-1 bg-muted-3 rounded-md px-1.5 py-1">
68+
${osIcon ? html`<i class="${osIcon} opacity-90 size-3"></i>` : ''}
69+
<span class="text-xs text-text-2">${this.parser.getOS().name}</span>
70+
</div>
71+
<div class="inline-flex items-center gap-1 bg-muted-3 rounded-md px-1.5 py-1">
72+
${browserIcon ? html`<i class="${browserIcon} opacity-90 size-3"></i>` : ''}
73+
<span class="text-xs text-text-2">${this.parser.getBrowser().name}</span>
74+
</div>
75+
</div>`;
76+
}
77+
78+
static override styles = [
79+
...baseStyles,
80+
css`
81+
@unocss-placeholder;
82+
`,
83+
];
84+
}
85+
86+
customElements.get('commenter-ua-bar') ||
87+
customElements.define('commenter-ua-bar', CommenterUABar);
88+
89+
declare global {
90+
interface HTMLElementTagNameMap {
91+
'commenter-ua-bar': CommenterUABar;
92+
}
93+
}

packages/comment-widget/src/reply-item.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export class ReplyItem extends LitElement {
122122
.approved=${this.reply?.spec.approved}
123123
.breath=${this.isQuoteReplyHovered}
124124
.userWebsite=${this.reply?.spec.owner.annotations?.website}
125+
.ua=${this.reply?.spec.userAgent}
125126
>
126127
<button slot="action" class="icon-button group -ml-2" type="button" @click="${this.handleUpvote}">
127128
<div class="icon-button-icon ">

packages/comment-widget/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface BasicConfig {
1010
size: number;
1111
withReplySize: number;
1212
replySize: number;
13+
showCommenterDevice?: boolean;
1314
}
1415

1516
interface SecurityConfig {

pnpm-lock.yaml

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/resources/extensions/settings.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ spec:
3131
key: withReplySize
3232
validation: required
3333
value: 5
34+
- $formkit: checkbox
35+
label: 显示评论者设备信息
36+
name: showCommenterDevice
37+
value: false
3438
- group: security
3539
label: 安全设置
3640
formSchema:

0 commit comments

Comments
 (0)