diff --git a/framework/core/js/src/common/compat.ts b/framework/core/js/src/common/compat.ts
index 3ebb6a65f8..3df7ff183b 100644
--- a/framework/core/js/src/common/compat.ts
+++ b/framework/core/js/src/common/compat.ts
@@ -91,6 +91,7 @@ import AlertManagerState from './states/AlertManagerState';
import ModalManagerState from './states/ModalManagerState';
import PageState from './states/PageState';
import LabelValue from './components/LabelValue';
+import IPAddress from './components/IPAddress';
export default {
extenders,
@@ -169,6 +170,7 @@ export default {
'components/Tooltip': Tooltip,
'components/EditUserModal': EditUserModal,
'components/LabelValue': LabelValue,
+ 'components/IPAddress': IPAddress,
Model: Model,
Application: Application,
'helpers/fullTime': fullTime,
diff --git a/framework/core/js/src/common/components/IPAddress.tsx b/framework/core/js/src/common/components/IPAddress.tsx
new file mode 100644
index 0000000000..7534fc6edd
--- /dev/null
+++ b/framework/core/js/src/common/components/IPAddress.tsx
@@ -0,0 +1,38 @@
+import Component, { ComponentAttrs } from '../Component';
+import type Mithril from 'mithril';
+import ItemList from '../utils/ItemList';
+
+export interface IIPAddressAttrs extends ComponentAttrs {
+ ip: string | undefined | null;
+}
+
+/**
+ * A component to wrap an IP address for display.
+ * Designed to be customizable for different use cases.
+ *
+ * @example
+ *
+ * @example
+ *
+ */
+export default class IPAddress extends Component {
+ ip!: string;
+
+ oninit(vnode: Mithril.Vnode) {
+ super.oninit(vnode);
+
+ this.ip = this.attrs.ip || '';
+ }
+
+ view() {
+ return {this.viewItems().toArray()};
+ }
+
+ viewItems(): ItemList {
+ const items = new ItemList();
+
+ items.add('ip', {this.ip}, 100);
+
+ return items;
+ }
+}
diff --git a/framework/core/js/src/forum/components/AccessTokensList.tsx b/framework/core/js/src/forum/components/AccessTokensList.tsx
index 09499458a4..23044e61ba 100644
--- a/framework/core/js/src/forum/components/AccessTokensList.tsx
+++ b/framework/core/js/src/forum/components/AccessTokensList.tsx
@@ -11,6 +11,7 @@ import Tooltip from '../../common/components/Tooltip';
import type Mithril from 'mithril';
import type AccessToken from '../../common/models/AccessToken';
import { NestedStringArray } from '@askvortsov/rich-icu-message-formatter';
+import IPAddress from '../../common/components/IPAddress';
export interface IAccessTokensListAttrs extends ComponentAttrs {
tokens: AccessToken[];
@@ -100,7 +101,12 @@ export default class AccessTokensList
{humanTime(token.lastActivityAt())}
- {token.lastIpAddress() && ` — ${token.lastIpAddress()}`}
+ {token.lastIpAddress() && (
+
+ {' '}
+ —
+
+ )}
{this.attrs.type === 'developer_token' && token.device() && (
<>
{' '}
diff --git a/framework/core/js/src/forum/components/PostMeta.js b/framework/core/js/src/forum/components/PostMeta.js
index 4a82f82a66..43e73fc88c 100644
--- a/framework/core/js/src/forum/components/PostMeta.js
+++ b/framework/core/js/src/forum/components/PostMeta.js
@@ -3,6 +3,7 @@ import Component from '../../common/Component';
import humanTime from '../../common/helpers/humanTime';
import fullTime from '../../common/helpers/fullTime';
import ItemList from '../../common/utils/ItemList';
+import IPAddress from '../../common/components/IPAddress';
/**
* The `PostMeta` component displays the time of a post, and when clicked, shows
@@ -78,7 +79,13 @@ export default class PostMeta extends Component {
items.add('post-time', {fullTime(time)}, 90);
- items.add('post-ip', {post.data.attributes.ipAddress}, 80);
+ items.add(
+ 'post-ip',
+
+
+ ,
+ 80
+ );
items.add(
'permalink',