Skip to content
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
3 changes: 2 additions & 1 deletion packages/comment-widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"javascript-time-ago": "^2.5.11",
"lit": "^3.3.1",
"ofetch": "^1.4.1",
"tiptap-extension-code-block-shiki": "file:lib/tiptap-extension-code-block-shiki-0.5.1.tgz"
"tiptap-extension-code-block-shiki": "file:lib/tiptap-extension-code-block-shiki-0.5.1.tgz",
"ua-parser-js": "^2.0.4"
},
"devDependencies": {
"@iconify/json": "^2.2.367",
Expand Down
27 changes: 21 additions & 6 deletions packages/comment-widget/src/base-comment-item.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import './user-avatar';
import { msg } from '@lit/localize';
import { css, html, LitElement, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import { property, state } from 'lit/decorators.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { codeToHtml } from 'shiki/bundle/full';
import baseStyles from './styles/base';
import contentStyles from './styles/content.css?inline';
import { formatDate, timeAgo } from './utils/date';
import './commenter-ua-bar';
import { consume } from '@lit/context';
import { configMapDataContext } from './context';
import type { ConfigMapData } from './types';

export class BaseCommentItem extends LitElement {
@property({ type: String })
Expand All @@ -30,6 +34,13 @@ export class BaseCommentItem extends LitElement {
@property({ type: String })
content = '';

@property({ type: String })
ua: string = '';

@consume({ context: configMapDataContext })
@state()
configMapData: ConfigMapData | undefined;

protected override firstUpdated() {
const codeElements = this.shadowRoot?.querySelectorAll('pre>code');
if (!codeElements?.length) return;
Expand Down Expand Up @@ -81,22 +92,26 @@ export class BaseCommentItem extends LitElement {
></user-avatar>
</div>
<div class="item-main flex-[1_1_auto] min-w-0 w-full">
<div class="item-meta flex items-center gap-3">
<div class="item-meta flex items-center gap-3 flex-wrap">
${
this.userWebsite
? html`<a
class="item-author font-medium text-sm"
class="item-author font-medium text-sm text-text-1"
target="_blank"
href=${this.userWebsite}
rel="noopener noreferrer"
>
${this.userDisplayName}
</a>`
: html`<div class="item-author font-medium text-sm">${this.userDisplayName}</div>`
: html`<span class="item-author font-medium text-sm text-text-1">${this.userDisplayName}</span>`
}
<div class="item-meta-info text-xs text-text-2" title=${formatDate(this.creationTime)}>

${this.ua && this.configMapData?.basic.showCommenterDevice ? html`<commenter-ua-bar .ua=${this.ua}></commenter-ua-bar>` : ''}

<time class="item-meta-info text-xs text-text-2" title=${formatDate(this.creationTime)}>
${timeAgo(this.creationTime)}
</div>
</time>

${
!this.approved
? html`<div class="item-meta-info text-xs text-text-2">${msg('Reviewing')}</div>`
Expand Down
1 change: 1 addition & 0 deletions packages/comment-widget/src/comment-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export class CommentItem extends LitElement {
.creationTime="${this.comment?.spec.creationTime}"
.approved=${this.comment?.spec.approved}
.userWebsite=${this.comment?.spec.owner.annotations?.website}
.ua=${this.comment?.spec.userAgent}
>
<button slot="action" class="icon-button group -ml-2" type="button" @click="${this.handleUpvote}">
<div class="icon-button-icon">
Expand Down
93 changes: 93 additions & 0 deletions packages/comment-widget/src/commenter-ua-bar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { css, html, LitElement } from 'lit';
import { property, state } from 'lit/decorators.js';
import type { UAParser } from 'ua-parser-js';
import baseStyles from './styles/base';

const OS_ICON_MAP = {
Windows: 'i-logos:microsoft-windows-icon',
macOS: 'i-logos:apple',
Linux: 'i-logos:linux-tux',
Android: 'i-logos:android-icon',
iOS: 'i-logos:apple',
'Chrome OS': 'i-logos:chrome',
Arch: 'i-logos:archlinux',
Manjaro: 'i-logos:manjaro',
Ubuntu: 'i-logos:ubuntu',
Fedora: 'i-logos:fedora',
};

const BROWSER_ICON_MAP = {
Chrome: 'i-logos:chrome',
'Mobile Chrome': 'i-logos:chrome',
'Chrome WebView': 'i-logos:chrome',
Firefox: 'i-logos:firefox',
'Mobile Firefox': 'i-logos:firefox',
Safari: 'i-logos:safari',
'Mobile Safari': 'i-logos:safari',
Edge: 'i-logos:microsoft-edge',
'Edge WebView': 'i-logos:microsoft-edge',
'Edge WebView2': 'i-logos:microsoft-edge',
Opera: 'i-logos:opera',
};

export class CommenterUABar extends LitElement {
@property({ type: String })
ua: string = '';

@state()
parser: UAParser | undefined;

getOSIcon(os?: string) {
return OS_ICON_MAP[os as keyof typeof OS_ICON_MAP];
}

getBrowserIcon(browser?: string) {
return BROWSER_ICON_MAP[browser as keyof typeof BROWSER_ICON_MAP];
}

override connectedCallback(): void {
super.connectedCallback();
this.init();
}

async init() {
const { UAParser } = await import('ua-parser-js');
this.parser = new UAParser(this.ua);
}

protected override render() {
if (!this.parser) {
return html``;
}

const osIcon = this.getOSIcon(this.parser.getOS().name);
const browserIcon = this.getBrowserIcon(this.parser.getBrowser().name);

return html`<div class="inline-flex gap-2 items-center">
<div class="inline-flex items-center gap-1 bg-muted-3 rounded-md px-1.5 py-1">
${osIcon ? html`<i class="${osIcon} opacity-90 size-3"></i>` : ''}
<span class="text-xs text-text-2">${this.parser.getOS().name}</span>
</div>
<div class="inline-flex items-center gap-1 bg-muted-3 rounded-md px-1.5 py-1">
${browserIcon ? html`<i class="${browserIcon} opacity-90 size-3"></i>` : ''}
<span class="text-xs text-text-2">${this.parser.getBrowser().name}</span>
</div>
</div>`;
}

static override styles = [
...baseStyles,
css`
@unocss-placeholder;
`,
];
}

customElements.get('commenter-ua-bar') ||
customElements.define('commenter-ua-bar', CommenterUABar);

declare global {
interface HTMLElementTagNameMap {
'commenter-ua-bar': CommenterUABar;
}
}
1 change: 1 addition & 0 deletions packages/comment-widget/src/reply-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export class ReplyItem extends LitElement {
.approved=${this.reply?.spec.approved}
.breath=${this.isQuoteReplyHovered}
.userWebsite=${this.reply?.spec.owner.annotations?.website}
.ua=${this.reply?.spec.userAgent}
>
<button slot="action" class="icon-button group -ml-2" type="button" @click="${this.handleUpvote}">
<div class="icon-button-icon ">
Expand Down
1 change: 1 addition & 0 deletions packages/comment-widget/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface BasicConfig {
size: number;
withReplySize: number;
replySize: number;
showCommenterDevice?: boolean;
}

interface SecurityConfig {
Expand Down
71 changes: 71 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/main/resources/extensions/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ spec:
key: withReplySize
validation: required
value: 5
- $formkit: checkbox
label: 显示评论者设备信息
name: showCommenterDevice
value: false
- group: security
label: 安全设置
formSchema:
Expand Down