From 4908845fa31dfe4c63e22292dc7c59224340706e Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 11:08:50 +0100 Subject: [PATCH 01/13] ASSET_NODE_TYPE -> M_ASSET Signed-off-by: Kerry Archibald --- spec/unit/location.spec.ts | 6 +++--- src/@types/location.ts | 11 +++++++++-- src/content-helpers.ts | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index a58b605e6be..f47ab465939 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -16,7 +16,7 @@ limitations under the License. import { makeLocationContent } from "../../src/content-helpers"; import { - ASSET_NODE_TYPE, + M_ASSET, LocationAssetType, LOCATION_EVENT_TYPE, TIMESTAMP_NODE_TYPE, @@ -33,7 +33,7 @@ describe("Location", function() { uri: "geo:foo", description: undefined, }); - expect(ASSET_NODE_TYPE.findIn(loc)).toEqual({ type: LocationAssetType.Self }); + expect(M_ASSET.findIn(loc)).toEqual({ type: LocationAssetType.Self }); expect(TEXT_NODE_TYPE.findIn(loc)).toEqual("txt"); expect(TIMESTAMP_NODE_TYPE.findIn(loc)).toEqual(134235435); }); @@ -49,7 +49,7 @@ describe("Location", function() { uri: "geo:bar", description: "desc", }); - expect(ASSET_NODE_TYPE.findIn(loc)).toEqual({ type: LocationAssetType.Pin }); + expect(M_ASSET.findIn(loc)).toEqual({ type: LocationAssetType.Pin }); expect(TEXT_NODE_TYPE.findIn(loc)).toEqual("txxt"); expect(TIMESTAMP_NODE_TYPE.findIn(loc)).toEqual(134235436); }); diff --git a/src/@types/location.ts b/src/@types/location.ts index 09ef3117161..1bb0e16f2fb 100644 --- a/src/@types/location.ts +++ b/src/@types/location.ts @@ -23,7 +23,7 @@ import { TEXT_NODE_TYPE } from "./extensible_events"; export const LOCATION_EVENT_TYPE = new UnstableValue( "m.location", "org.matrix.msc3488.location"); -export const ASSET_NODE_TYPE = new UnstableValue("m.asset", "org.matrix.msc3488.asset"); +export const M_ASSET = new UnstableValue("m.asset", "org.matrix.msc3488.asset"); export const TIMESTAMP_NODE_TYPE = new UnstableValue("m.ts", "org.matrix.msc3488.ts"); @@ -32,6 +32,13 @@ export enum LocationAssetType { Pin = "m.pin", } +export const M_ASSET = new UnstableValue("m.asset", "org.matrix.msc3488.asset"); +export type MAssetContent = { type: LocationAssetType }; +/** + * The event definition for an m.asset event (in content) + */ +export type MAssetEvent = EitherAnd<{ [M_ASSET.name]: MAssetContent }, { [M_ASSET.altName]: MAssetContent }>; + /* From the spec at: * https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md { @@ -62,7 +69,7 @@ export interface ILocationContent extends IContent { uri: string; description?: string; }; - [ASSET_NODE_TYPE.name]: { + [M_ASSET.name]: { type: LocationAssetType; }; [TEXT_NODE_TYPE.name]: string; diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 89955bbaee9..55ac284511b 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -19,7 +19,7 @@ limitations under the License. import { MsgType } from "./@types/event"; import { TEXT_NODE_TYPE } from "./@types/extensible_events"; import { - ASSET_NODE_TYPE, + M_ASSET, ILocationContent, LocationAssetType, LOCATION_EVENT_TYPE, @@ -131,7 +131,7 @@ export function makeLocationContent( uri, description, }, - [ASSET_NODE_TYPE.name]: { + [M_ASSET.name]: { type: assetType ?? LocationAssetType.Self, }, [TEXT_NODE_TYPE.name]: text, From 164587bad332b0b014161dc5f9cadb642efb06e4 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 11:09:59 +0100 Subject: [PATCH 02/13] export const M_TIMESTAMP = new UnstableValue("m.ts", "org.matrix.msc3488.ts"); Signed-off-by: Kerry Archibald --- spec/unit/location.spec.ts | 6 +++--- src/@types/location.ts | 13 ++++++++----- src/content-helpers.ts | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index f47ab465939..92213b394ec 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -19,7 +19,7 @@ import { M_ASSET, LocationAssetType, LOCATION_EVENT_TYPE, - TIMESTAMP_NODE_TYPE, + M_TIMESTAMP, } from "../../src/@types/location"; import { TEXT_NODE_TYPE } from "../../src/@types/extensible_events"; @@ -35,7 +35,7 @@ describe("Location", function() { }); expect(M_ASSET.findIn(loc)).toEqual({ type: LocationAssetType.Self }); expect(TEXT_NODE_TYPE.findIn(loc)).toEqual("txt"); - expect(TIMESTAMP_NODE_TYPE.findIn(loc)).toEqual(134235435); + expect(M_TIMESTAMP.findIn(loc)).toEqual(134235435); }); it("should create a valid location with explicit properties", function() { @@ -51,6 +51,6 @@ describe("Location", function() { }); expect(M_ASSET.findIn(loc)).toEqual({ type: LocationAssetType.Pin }); expect(TEXT_NODE_TYPE.findIn(loc)).toEqual("txxt"); - expect(TIMESTAMP_NODE_TYPE.findIn(loc)).toEqual(134235436); + expect(M_TIMESTAMP.findIn(loc)).toEqual(134235436); }); }); diff --git a/src/@types/location.ts b/src/@types/location.ts index 1bb0e16f2fb..9631997e59a 100644 --- a/src/@types/location.ts +++ b/src/@types/location.ts @@ -15,6 +15,7 @@ limitations under the License. */ // Types for MSC3488 - m.location: Extending events with location data +import { EitherAnd } from "matrix-events-sdk"; import { UnstableValue } from "../NamespacedValue"; import { IContent } from "../models/event"; @@ -23,10 +24,6 @@ import { TEXT_NODE_TYPE } from "./extensible_events"; export const LOCATION_EVENT_TYPE = new UnstableValue( "m.location", "org.matrix.msc3488.location"); -export const M_ASSET = new UnstableValue("m.asset", "org.matrix.msc3488.asset"); - -export const TIMESTAMP_NODE_TYPE = new UnstableValue("m.ts", "org.matrix.msc3488.ts"); - export enum LocationAssetType { Self = "m.self", Pin = "m.pin", @@ -39,6 +36,12 @@ export type MAssetContent = { type: LocationAssetType }; */ export type MAssetEvent = EitherAnd<{ [M_ASSET.name]: MAssetContent }, { [M_ASSET.altName]: MAssetContent }>; +export const M_TIMESTAMP = new UnstableValue("m.ts", "org.matrix.msc3488.ts"); +/** + * The event definition for an m.ts event (in content) + */ +export type MTimestampEvent = EitherAnd<{ [M_TIMESTAMP.name]: number }, { [M_TIMESTAMP.altName]: number }>; + /* From the spec at: * https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md { @@ -73,6 +76,6 @@ export interface ILocationContent extends IContent { type: LocationAssetType; }; [TEXT_NODE_TYPE.name]: string; - [TIMESTAMP_NODE_TYPE.name]: number; + [M_TIMESTAMP.name]: number; } /* eslint-enable camelcase */ diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 55ac284511b..b3797c2dde3 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -23,7 +23,7 @@ import { ILocationContent, LocationAssetType, LOCATION_EVENT_TYPE, - TIMESTAMP_NODE_TYPE, + M_TIMESTAMP, } from "./@types/location"; /** @@ -135,7 +135,7 @@ export function makeLocationContent( type: assetType ?? LocationAssetType.Self, }, [TEXT_NODE_TYPE.name]: text, - [TIMESTAMP_NODE_TYPE.name]: ts, + [M_TIMESTAMP.name]: ts, // TODO: MSC1767 fallbacks m.image thumbnail }; } From 347410910285a87eafb6da9078a597b2765ce0c9 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 11:11:21 +0100 Subject: [PATCH 03/13] LOCATION_EVENT_TYPE -> M_LOCATION Signed-off-by: Kerry Archibald --- spec/unit/location.spec.ts | 6 +++--- src/@types/location.ts | 13 +++++++++---- src/content-helpers.ts | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index 92213b394ec..71af2a0d7a3 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -18,7 +18,7 @@ import { makeLocationContent } from "../../src/content-helpers"; import { M_ASSET, LocationAssetType, - LOCATION_EVENT_TYPE, + M_LOCATION, M_TIMESTAMP, } from "../../src/@types/location"; import { TEXT_NODE_TYPE } from "../../src/@types/extensible_events"; @@ -29,7 +29,7 @@ describe("Location", function() { expect(loc.body).toEqual("txt"); expect(loc.msgtype).toEqual("m.location"); expect(loc.geo_uri).toEqual("geo:foo"); - expect(LOCATION_EVENT_TYPE.findIn(loc)).toEqual({ + expect(M_LOCATION.findIn(loc)).toEqual({ uri: "geo:foo", description: undefined, }); @@ -45,7 +45,7 @@ describe("Location", function() { expect(loc.body).toEqual("txxt"); expect(loc.msgtype).toEqual("m.location"); expect(loc.geo_uri).toEqual("geo:bar"); - expect(LOCATION_EVENT_TYPE.findIn(loc)).toEqual({ + expect(M_LOCATION.findIn(loc)).toEqual({ uri: "geo:bar", description: "desc", }); diff --git a/src/@types/location.ts b/src/@types/location.ts index 9631997e59a..cccaacd4f69 100644 --- a/src/@types/location.ts +++ b/src/@types/location.ts @@ -21,9 +21,6 @@ import { UnstableValue } from "../NamespacedValue"; import { IContent } from "../models/event"; import { TEXT_NODE_TYPE } from "./extensible_events"; -export const LOCATION_EVENT_TYPE = new UnstableValue( - "m.location", "org.matrix.msc3488.location"); - export enum LocationAssetType { Self = "m.self", Pin = "m.pin", @@ -42,6 +39,14 @@ export const M_TIMESTAMP = new UnstableValue("m.ts", "org.matrix.msc3488.ts"); */ export type MTimestampEvent = EitherAnd<{ [M_TIMESTAMP.name]: number }, { [M_TIMESTAMP.altName]: number }>; +export const M_LOCATION = new UnstableValue( + "m.location", "org.matrix.msc3488.location"); + +export type MLocationContent = { + uri: string; + description?: string | null; +}; + /* From the spec at: * https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md { @@ -68,7 +73,7 @@ export interface ILocationContent extends IContent { body: string; msgtype: string; geo_uri: string; - [LOCATION_EVENT_TYPE.name]: { + [M_LOCATION.name]: { uri: string; description?: string; }; diff --git a/src/content-helpers.ts b/src/content-helpers.ts index b3797c2dde3..f1ff1f810a7 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -22,7 +22,7 @@ import { M_ASSET, ILocationContent, LocationAssetType, - LOCATION_EVENT_TYPE, + M_LOCATION, M_TIMESTAMP, } from "./@types/location"; @@ -127,7 +127,7 @@ export function makeLocationContent( "body": text, "msgtype": MsgType.Location, "geo_uri": uri, - [LOCATION_EVENT_TYPE.name]: { + [M_LOCATION.name]: { uri, description, }, From 60d9cba403a0942cd2c7e5350fc2e752e8f7d147 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 11:16:47 +0100 Subject: [PATCH 04/13] extensible event types for location Signed-off-by: Kerry Archibald --- src/@types/location.ts | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/@types/location.ts b/src/@types/location.ts index cccaacd4f69..3fc3d255849 100644 --- a/src/@types/location.ts +++ b/src/@types/location.ts @@ -47,6 +47,13 @@ export type MLocationContent = { description?: string | null; }; +export type MLocationEvent = EitherAnd< + { [M_LOCATION.name]: MLocationContent }, + { [M_LOCATION.altName]: MLocationContent } +>; + +export type MTextEvent = EitherAnd<{ [TEXT_NODE_TYPE.name]: string }, { [TEXT_NODE_TYPE.altName]: string }>; + /* From the spec at: * https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md { @@ -68,19 +75,22 @@ export type MLocationContent = { } */ -/* eslint-disable camelcase */ -export interface ILocationContent extends IContent { +/** + * The content for an m.location event +*/ +export type MLocationEventContent = & + MLocationEvent & + MAssetEvent & + MTextEvent & + // timestamp is optional + (MTimestampEvent | undefined); + +export type LegacyLocationEventContent = { body: string; msgtype: string; geo_uri: string; - [M_LOCATION.name]: { - uri: string; - description?: string; - }; - [M_ASSET.name]: { - type: LocationAssetType; - }; - [TEXT_NODE_TYPE.name]: string; - [M_TIMESTAMP.name]: number; -} +}; + +/* eslint-disable camelcase */ +export type ILocationContent = MLocationEventContent & LegacyLocationEventContent; /* eslint-enable camelcase */ From 8a47eb65c061496d5ac46c62a7d6541c1bc5db9e Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 14:51:30 +0100 Subject: [PATCH 05/13] add locationevent parsing helpers Signed-off-by: Kerry Archibald --- spec/unit/location.spec.ts | 70 +++++++++++++++++++++++++++++++++----- src/@types/location.ts | 13 ++++--- src/content-helpers.ts | 69 +++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 13 deletions(-) diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index 71af2a0d7a3..a2507a34013 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -14,19 +14,41 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { makeLocationContent } from "../../src/content-helpers"; +import { makeLocationEventContent, parseLocationEvent } from "../../src/content-helpers"; import { M_ASSET, LocationAssetType, M_LOCATION, M_TIMESTAMP, + LocationEventWireContent, } from "../../src/@types/location"; import { TEXT_NODE_TYPE } from "../../src/@types/extensible_events"; describe("Location", function() { + const defaultContent = { + "body": "Location geo:-36.24484561954707,175.46884959563613;u=10 at 2022-03-09T11:01:52.443Z", + "msgtype": "m.location", + "geo_uri": "geo:-36.24484561954707,175.46884959563613;u=10", + [M_LOCATION.name]: { "uri": "geo:-36.24484561954707,175.46884959563613;u=10", "description": null }, + [M_ASSET.name]: { "type": "m.self" }, + [TEXT_NODE_TYPE.name]: "Location geo:-36.24484561954707,175.46884959563613;u=10 at 2022-03-09T11:01:52.443Z", + [M_TIMESTAMP.name]: 1646823712443, + } as any; + + const backwardsCompatibleEventContent = { ...defaultContent }; + + // eslint-disable-next-line camelcase + const { body, msgtype, geo_uri, ...modernProperties } = defaultContent; + const modernEventContent = { ...modernProperties }; + + const legacyEventContent = { + // eslint-disable-next-line camelcase + body, msgtype, geo_uri, + } as LocationEventWireContent; + it("should create a valid location with defaults", function() { - const loc = makeLocationContent("txt", "geo:foo", 134235435); - expect(loc.body).toEqual("txt"); + const loc = makeLocationEventContent("geo:foo", 134235435); + expect(loc.body).toEqual('User Location geo:foo at 1970-01-02T13:17:15.435Z'); expect(loc.msgtype).toEqual("m.location"); expect(loc.geo_uri).toEqual("geo:foo"); expect(M_LOCATION.findIn(loc)).toEqual({ @@ -34,15 +56,15 @@ describe("Location", function() { description: undefined, }); expect(M_ASSET.findIn(loc)).toEqual({ type: LocationAssetType.Self }); - expect(TEXT_NODE_TYPE.findIn(loc)).toEqual("txt"); + expect(TEXT_NODE_TYPE.findIn(loc)).toEqual('User Location geo:foo at 1970-01-02T13:17:15.435Z'); expect(M_TIMESTAMP.findIn(loc)).toEqual(134235435); }); it("should create a valid location with explicit properties", function() { - const loc = makeLocationContent( - "txxt", "geo:bar", 134235436, "desc", LocationAssetType.Pin); + const loc = makeLocationEventContent( + "geo:bar", 134235436, "desc", LocationAssetType.Pin); - expect(loc.body).toEqual("txxt"); + expect(loc.body).toEqual('Location "desc" geo:bar at 1970-01-02T13:17:15.436Z'); expect(loc.msgtype).toEqual("m.location"); expect(loc.geo_uri).toEqual("geo:bar"); expect(M_LOCATION.findIn(loc)).toEqual({ @@ -50,7 +72,39 @@ describe("Location", function() { description: "desc", }); expect(M_ASSET.findIn(loc)).toEqual({ type: LocationAssetType.Pin }); - expect(TEXT_NODE_TYPE.findIn(loc)).toEqual("txxt"); + expect(TEXT_NODE_TYPE.findIn(loc)).toEqual('Location "desc" geo:bar at 1970-01-02T13:17:15.436Z'); expect(M_TIMESTAMP.findIn(loc)).toEqual(134235436); }); + + it('parses backwards compatible event correctly', () => { + const eventContent = parseLocationEvent(backwardsCompatibleEventContent); + + expect(eventContent).toEqual(backwardsCompatibleEventContent); + }); + + it('parses modern correctly', () => { + const eventContent = parseLocationEvent(modernEventContent); + + expect(eventContent).toEqual(backwardsCompatibleEventContent); + }); + + it('parses legacy event correctly', () => { + const eventContent = parseLocationEvent(legacyEventContent); + + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + [M_TIMESTAMP.name]: timestamp, + ...expectedResult + } = defaultContent; + expect(eventContent).toEqual({ + ...expectedResult, + [M_LOCATION.name]: { + ...expectedResult[M_LOCATION.name], + description: undefined, + }, + }); + + // don't infer timestamp from legacy event + expect(M_TIMESTAMP.findIn(eventContent)).toBeFalsy(); + }); }); diff --git a/src/@types/location.ts b/src/@types/location.ts index 3fc3d255849..bef85b9e124 100644 --- a/src/@types/location.ts +++ b/src/@types/location.ts @@ -18,7 +18,6 @@ limitations under the License. import { EitherAnd } from "matrix-events-sdk"; import { UnstableValue } from "../NamespacedValue"; -import { IContent } from "../models/event"; import { TEXT_NODE_TYPE } from "./extensible_events"; export enum LocationAssetType { @@ -74,7 +73,7 @@ export type MTextEvent = EitherAnd<{ [TEXT_NODE_TYPE.name]: string }, { [TEXT_NO } } */ - +type OptionalTimestampEvent = MTimestampEvent | undefined; /** * The content for an m.location event */ @@ -82,8 +81,9 @@ export type MLocationEventContent = & MLocationEvent & MAssetEvent & MTextEvent & + OptionalTimestampEvent; // timestamp is optional - (MTimestampEvent | undefined); + //(MTimestampEvent | undefined); export type LegacyLocationEventContent = { body: string; @@ -91,6 +91,9 @@ export type LegacyLocationEventContent = { geo_uri: string; }; -/* eslint-disable camelcase */ +/** + * Possible content for location events as sent over the wire + */ +export type LocationEventWireContent = Partial; + export type ILocationContent = MLocationEventContent & LegacyLocationEventContent; -/* eslint-enable camelcase */ diff --git a/src/content-helpers.ts b/src/content-helpers.ts index f1ff1f810a7..268903f872c 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -24,6 +24,11 @@ import { LocationAssetType, M_LOCATION, M_TIMESTAMP, + LocationEventWireContent, + MLocationEventContent, + MLocationContent, + MAssetContent, + LegacyLocationEventContent, } from "./@types/location"; /** @@ -107,6 +112,8 @@ export function makeEmoteMessage(body: string) { }; } +/** Location content helpers */ + /** * Generates the content for a Location event * @param text a text for of our location @@ -139,3 +146,65 @@ export function makeLocationContent( // TODO: MSC1767 fallbacks m.image thumbnail }; } + +export const getTextForLocationEvent = ( + uri: string, + assetType: LocationAssetType, + timestamp: number, + description?: string, +): string => { + const date = `at ${new Date(timestamp).toISOString()}`; + const assetName = assetType === LocationAssetType.Self ? 'User' : undefined; + const quotedDescription = description ? `"${description}"` : undefined; + + return [ + assetName, + 'Location', + quotedDescription, + uri, + date, + ].filter(Boolean).join(' '); +}; + +export const makeLocationEventContent = (uri: string, + timestamp?: number, + description?: string, + assetType?: LocationAssetType, + text?: string, +): LegacyLocationEventContent & MLocationEventContent => { + const defaultedText = text ?? + getTextForLocationEvent(uri, assetType || LocationAssetType.Self, timestamp, description); + const timestampEvent = timestamp ? { [M_TIMESTAMP.name]: timestamp } : {}; + return { + msgtype: "m.location", + body: defaultedText, + geo_uri: uri, + [M_LOCATION.name]: { + description, + uri, + }, + [M_ASSET.name]: { + type: assetType || LocationAssetType.Self, + }, + [TEXT_NODE_TYPE.name]: defaultedText, + ...timestampEvent, + } as LegacyLocationEventContent & MLocationEventContent; +}; + +/** + * Parse location event content and transform to + * a backwards compatible modern m.location event format + */ +export const parseLocationEvent = (wireEventContent: LocationEventWireContent): MLocationEventContent => { + const location = M_LOCATION.findIn(wireEventContent); + const asset = M_ASSET.findIn(wireEventContent); + const timestamp = M_TIMESTAMP.findIn(wireEventContent); + const text = TEXT_NODE_TYPE.findIn(wireEventContent); + + const geoUri = location?.uri ?? wireEventContent?.geo_uri; + const description = location?.description; + const assetType = asset?.type ?? LocationAssetType.Self; + const fallbackText = text ?? wireEventContent.body; + + return makeLocationEventContent(geoUri, timestamp, description, assetType, fallbackText); +}; From b1fdf28a5b937d4541fc78ec78a20781e2ad60bf Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 14:57:04 +0100 Subject: [PATCH 06/13] rename Signed-off-by: Kerry Archibald --- spec/unit/location.spec.ts | 6 ++--- src/content-helpers.ts | 48 ++++++++++---------------------------- 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index a2507a34013..a1e288b6d2c 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { makeLocationEventContent, parseLocationEvent } from "../../src/content-helpers"; +import { makeLocationContent, parseLocationEvent } from "../../src/content-helpers"; import { M_ASSET, LocationAssetType, @@ -47,7 +47,7 @@ describe("Location", function() { } as LocationEventWireContent; it("should create a valid location with defaults", function() { - const loc = makeLocationEventContent("geo:foo", 134235435); + const loc = makeLocationContent("geo:foo", 134235435); expect(loc.body).toEqual('User Location geo:foo at 1970-01-02T13:17:15.435Z'); expect(loc.msgtype).toEqual("m.location"); expect(loc.geo_uri).toEqual("geo:foo"); @@ -61,7 +61,7 @@ describe("Location", function() { }); it("should create a valid location with explicit properties", function() { - const loc = makeLocationEventContent( + const loc = makeLocationContent( "geo:bar", 134235436, "desc", LocationAssetType.Pin); expect(loc.body).toEqual('Location "desc" geo:bar at 1970-01-02T13:17:15.436Z'); diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 268903f872c..4d0f57cd27f 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -20,7 +20,6 @@ import { MsgType } from "./@types/event"; import { TEXT_NODE_TYPE } from "./@types/extensible_events"; import { M_ASSET, - ILocationContent, LocationAssetType, M_LOCATION, M_TIMESTAMP, @@ -114,39 +113,6 @@ export function makeEmoteMessage(body: string) { /** Location content helpers */ -/** - * Generates the content for a Location event - * @param text a text for of our location - * @param uri a geo:// uri for the location - * @param ts the timestamp when the location was correct (milliseconds since - * the UNIX epoch) - * @param description the (optional) label for this location on the map - * @param asset_type the (optional) asset type of this location e.g. "m.self" - */ -export function makeLocationContent( - text: string, - uri: string, - ts: number, - description?: string, - assetType?: LocationAssetType, -): ILocationContent { - return { - "body": text, - "msgtype": MsgType.Location, - "geo_uri": uri, - [M_LOCATION.name]: { - uri, - description, - }, - [M_ASSET.name]: { - type: assetType ?? LocationAssetType.Self, - }, - [TEXT_NODE_TYPE.name]: text, - [M_TIMESTAMP.name]: ts, - // TODO: MSC1767 fallbacks m.image thumbnail - }; -} - export const getTextForLocationEvent = ( uri: string, assetType: LocationAssetType, @@ -166,7 +132,17 @@ export const getTextForLocationEvent = ( ].filter(Boolean).join(' '); }; -export const makeLocationEventContent = (uri: string, +/** + * Generates the content for a Location event + * @param uri a geo:// uri for the location + * @param ts the timestamp when the location was correct (milliseconds since + * the UNIX epoch) + * @param description the (optional) label for this location on the map + * @param asset_type the (optional) asset type of this location e.g. "m.self" + * @param text optional. A text for the location + */ +export const makeLocationContent = ( + uri: string, timestamp?: number, description?: string, assetType?: LocationAssetType, @@ -206,5 +182,5 @@ export const parseLocationEvent = (wireEventContent: LocationEventWireContent): const assetType = asset?.type ?? LocationAssetType.Self; const fallbackText = text ?? wireEventContent.body; - return makeLocationEventContent(geoUri, timestamp, description, assetType, fallbackText); + return makeLocationContent(geoUri, timestamp, description, assetType, fallbackText); }; From 11c23a38aef77a4ad18f32c54320bbf88f4bfb44 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 15:07:40 +0100 Subject: [PATCH 07/13] comment Signed-off-by: Kerry Archibald --- src/@types/location.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/@types/location.ts b/src/@types/location.ts index bef85b9e124..9fc37d349e7 100644 --- a/src/@types/location.ts +++ b/src/@types/location.ts @@ -82,8 +82,6 @@ export type MLocationEventContent = & MAssetEvent & MTextEvent & OptionalTimestampEvent; - // timestamp is optional - //(MTimestampEvent | undefined); export type LegacyLocationEventContent = { body: string; From 0ecdfe2d9e50be1e64f7deaa5710fbce91a43212 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 18:19:24 +0100 Subject: [PATCH 08/13] revert makelocationcontent signature Signed-off-by: Kerry Archibald --- spec/unit/location.spec.ts | 9 +++++---- src/content-helpers.ts | 8 +++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/spec/unit/location.spec.ts b/spec/unit/location.spec.ts index a1e288b6d2c..d7bdf407fa5 100644 --- a/spec/unit/location.spec.ts +++ b/spec/unit/location.spec.ts @@ -23,6 +23,7 @@ import { LocationEventWireContent, } from "../../src/@types/location"; import { TEXT_NODE_TYPE } from "../../src/@types/extensible_events"; +import { MsgType } from "../../src/@types/event"; describe("Location", function() { const defaultContent = { @@ -47,9 +48,9 @@ describe("Location", function() { } as LocationEventWireContent; it("should create a valid location with defaults", function() { - const loc = makeLocationContent("geo:foo", 134235435); + const loc = makeLocationContent(undefined, "geo:foo", 134235435); expect(loc.body).toEqual('User Location geo:foo at 1970-01-02T13:17:15.435Z'); - expect(loc.msgtype).toEqual("m.location"); + expect(loc.msgtype).toEqual(MsgType.Location); expect(loc.geo_uri).toEqual("geo:foo"); expect(M_LOCATION.findIn(loc)).toEqual({ uri: "geo:foo", @@ -62,10 +63,10 @@ describe("Location", function() { it("should create a valid location with explicit properties", function() { const loc = makeLocationContent( - "geo:bar", 134235436, "desc", LocationAssetType.Pin); + undefined, "geo:bar", 134235436, "desc", LocationAssetType.Pin); expect(loc.body).toEqual('Location "desc" geo:bar at 1970-01-02T13:17:15.436Z'); - expect(loc.msgtype).toEqual("m.location"); + expect(loc.msgtype).toEqual(MsgType.Location); expect(loc.geo_uri).toEqual("geo:bar"); expect(M_LOCATION.findIn(loc)).toEqual({ uri: "geo:bar", diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 4d0f57cd27f..6419b98bdce 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -142,17 +142,19 @@ export const getTextForLocationEvent = ( * @param text optional. A text for the location */ export const makeLocationContent = ( + // this is first but optional + // to avoid a breaking change + text: string | undefined, uri: string, timestamp?: number, description?: string, assetType?: LocationAssetType, - text?: string, ): LegacyLocationEventContent & MLocationEventContent => { const defaultedText = text ?? getTextForLocationEvent(uri, assetType || LocationAssetType.Self, timestamp, description); const timestampEvent = timestamp ? { [M_TIMESTAMP.name]: timestamp } : {}; return { - msgtype: "m.location", + msgtype: MsgType.Location, body: defaultedText, geo_uri: uri, [M_LOCATION.name]: { @@ -182,5 +184,5 @@ export const parseLocationEvent = (wireEventContent: LocationEventWireContent): const assetType = asset?.type ?? LocationAssetType.Self; const fallbackText = text ?? wireEventContent.body; - return makeLocationContent(geoUri, timestamp, description, assetType, fallbackText); + return makeLocationContent(fallbackText, geoUri, timestamp, description, assetType); }; From e1b6ceb9f7e5ae4acb9b8b16a909460bc1192bbc Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Thu, 10 Mar 2022 15:28:36 +0100 Subject: [PATCH 09/13] add beacon event types Signed-off-by: Kerry Archibald --- src/@types/beacon.ts | 94 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/@types/beacon.ts diff --git a/src/@types/beacon.ts b/src/@types/beacon.ts new file mode 100644 index 00000000000..a799e16adf1 --- /dev/null +++ b/src/@types/beacon.ts @@ -0,0 +1,94 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { EitherAnd, RELATES_TO_RELATIONSHIP, REFERENCE_RELATION } from "matrix-events-sdk"; + +import { UnstableValue } from "../NamespacedValue"; +import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location"; + +/** + * Beacon info and beacon event types as described in MSC3489 + * https://github.com/matrix-org/matrix-spec-proposals/pull/3489 + */ + +export const M_BEACON_INFO = new UnstableValue("m.beacon_info", "org.matrix.msc3489.beacon_info"); +export const M_BEACON = new UnstableValue("m.beacon", "org.matrix.msc3489.beacon"); + +export type MBeaconInfoContent = { + description?: string; + // how long from the last event until we consider the beacon inactive in milliseconds + timeout: number; +}; + +export type MBeaconInfoEvent = EitherAnd< + { [M_BEACON_INFO.name]: MBeaconInfoContent }, + { [M_BEACON_INFO.altName]: MBeaconInfoContent } +>; + +/** + * m.beacon_info Event example from the spec + * https://github.com/matrix-org/matrix-spec-proposals/pull/3489 + * { + "type": "m.beacon_info.@matthew:matrix.org", + "state_key": "@matthew:matrix.org", + "content": { + "m.beacon_info": { + "description": "The Matthew Tracker", // same as an `m.location` description + "timeout": 86400000, // how long from the last event until we consider the beacon inactive in milliseconds + }, + "m.ts": 1436829458432, // creation timestamp of the beacon on the client + "m.asset": { + "type": "m.self" // the type of asset being tracked as per MSC3488 + } + } +} + */ + +export type MBeaconInfoEventContent = & + MBeaconInfoEvent & + // creation timestamp of the beacon on the client + MTimestampEvent & + // the type of asset being tracked as per MSC3488 + MAssetEvent; + +/** + * m.beacon event example + * https://github.com/matrix-org/matrix-spec-proposals/pull/3489 + * + * { + "type": "m.beacon", + "sender": "@matthew:matrix.org", + "content": { + "m.relates_to": { // from MSC2674: https://github.com/matrix-org/matrix-doc/pull/2674 + "rel_type": "m.reference", // from MSC3267: https://github.com/matrix-org/matrix-doc/pull/3267 + "event_id": "$beacon_info" + }, + "m.location": { + "uri": "geo:51.5008,0.1247;u=35", + "description": "Arbitrary beacon information" + }, + "m.ts": 1636829458432, + } +} +*/ + +export type MBeaconEventContent = & + MLocationEvent & + // timestamp when location was taken + MTimestampEvent & + // relates to a beacon_info event + RELATES_TO_RELATIONSHIP; + From 3b5d4ff53b1997252825ff4905df5648f413d583 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 11 Mar 2022 10:14:18 +0100 Subject: [PATCH 10/13] add variable* to type and comment Signed-off-by: Kerry Archibald --- src/@types/beacon.ts | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/@types/beacon.ts b/src/@types/beacon.ts index a799e16adf1..14ff7c4a492 100644 --- a/src/@types/beacon.ts +++ b/src/@types/beacon.ts @@ -24,6 +24,41 @@ import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location"; * https://github.com/matrix-org/matrix-spec-proposals/pull/3489 */ +/** + * Beacon info events are state events. We have two requirements for these events: + * 1. they can only be written by their owner + * 2. a user can have an arbitrary number of beacon_info events + * + * 1. is achieved by setting the state_key to the owners mxid. + * Event keys in room state are a combination of `type` + `state_key`. + * To achieve an arbitrary number of only owner-writable state events + * we introduce a variable suffix to the event type + * + * Eg + * { + * "type": "m.beacon_info.@matthew:matrix.org.1", + * "state_key": "@matthew:matrix.org", + * "content": { + * "m.beacon_info": { + * "description": "The Matthew Tracker", + * "timeout": 86400000, + * }, + * // more content as described below + * } + * }, + * { + * "type": "m.beacon_info.@matthew:matrix.org.2", + * "state_key": "@matthew:matrix.org", + * "content": { + * "m.beacon_info": { + * "description": "Another different Matthew tracker", + * "timeout": 400000, + * }, + * // more content as described below + * } + * } + */ +export const M_BEACON_INFO_VARIABLE = new UnstableValue("m.beacon_info.*", "org.matrix.msc3489.beacon_info.*"); export const M_BEACON_INFO = new UnstableValue("m.beacon_info", "org.matrix.msc3489.beacon_info"); export const M_BEACON = new UnstableValue("m.beacon", "org.matrix.msc3489.beacon"); @@ -42,7 +77,7 @@ export type MBeaconInfoEvent = EitherAnd< * m.beacon_info Event example from the spec * https://github.com/matrix-org/matrix-spec-proposals/pull/3489 * { - "type": "m.beacon_info.@matthew:matrix.org", + "type": "m.beacon_info.@matthew:matrix.org.1", "state_key": "@matthew:matrix.org", "content": { "m.beacon_info": { From 56bc6681cf6bfc88801d592bb41dcf056579e631 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 11 Mar 2022 10:43:23 +0100 Subject: [PATCH 11/13] add content helper functions for beacon_info and beacon Signed-off-by: Kerry Archibald --- spec/unit/content-helpers.spec.ts | 95 +++++++++++++++++++++++++++++++ src/@types/beacon.ts | 20 +++++-- src/content-helpers.ts | 39 +++++++++++++ 3 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 spec/unit/content-helpers.spec.ts diff --git a/spec/unit/content-helpers.spec.ts b/spec/unit/content-helpers.spec.ts new file mode 100644 index 00000000000..02d650732f7 --- /dev/null +++ b/spec/unit/content-helpers.spec.ts @@ -0,0 +1,95 @@ +import { REFERENCE_RELATION } from "matrix-events-sdk"; + +import { M_BEACON_INFO } from "../../src/@types/beacon"; +import { LocationAssetType, M_ASSET, M_LOCATION, M_TIMESTAMP } from "../../src/@types/location"; +import { makeBeaconContent, makeBeaconInfoContent } from "../../src/content-helpers"; + +describe('Beacon content helpers', () => { + describe('makeBeaconInfoContent()', () => { + const mockDateNow = 123456789; + beforeEach(() => { + jest.spyOn(global.Date, 'now').mockReturnValue(mockDateNow); + }); + afterAll(() => { + jest.spyOn(global.Date, 'now').mockRestore(); + }); + it('create fully defined event content', () => { + expect(makeBeaconInfoContent( + 1234, + 'nice beacon_info', + LocationAssetType.Pin, + )).toEqual({ + [M_BEACON_INFO.name]: { + description: 'nice beacon_info', + timeout: 1234, + }, + [M_TIMESTAMP.name]: mockDateNow, + [M_ASSET.name]: { + type: LocationAssetType.Pin, + }, + }); + }); + + it('defaults timestamp to current time', () => { + expect(makeBeaconInfoContent( + 1234, + 'nice beacon_info', + LocationAssetType.Pin, + )).toEqual(expect.objectContaining({ + [M_TIMESTAMP.name]: mockDateNow, + })); + }); + + it('defaults asset type to self when not set', () => { + expect(makeBeaconInfoContent( + 1234, + 'nice beacon_info', + // no assetType passed + )).toEqual(expect.objectContaining({ + [M_ASSET.name]: { + type: LocationAssetType.Self, + }, + })); + }); + }); + + describe('makeBeaconContent()', () => { + it('creates event content without description', () => { + expect(makeBeaconContent( + 'geo:foo', + 123, + '$1234', + // no description + )).toEqual({ + [M_LOCATION.name]: { + description: undefined, + uri: 'geo:foo', + }, + [M_TIMESTAMP.name]: 123, + "m.relates_to": { + rel_type: REFERENCE_RELATION.name, + event_id: '$1234', + }, + }); + }); + + it('creates event content with description', () => { + expect(makeBeaconContent( + 'geo:foo', + 123, + '$1234', + 'test description', + )).toEqual({ + [M_LOCATION.name]: { + description: 'test description', + uri: 'geo:foo', + }, + [M_TIMESTAMP.name]: 123, + "m.relates_to": { + rel_type: REFERENCE_RELATION.name, + event_id: '$1234', + }, + }); + }); + }); +}); diff --git a/src/@types/beacon.ts b/src/@types/beacon.ts index 14ff7c4a492..d46117dd374 100644 --- a/src/@types/beacon.ts +++ b/src/@types/beacon.ts @@ -25,15 +25,16 @@ import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location"; */ /** - * Beacon info events are state events. We have two requirements for these events: + * Beacon info events are state events. + * We have two requirements for these events: * 1. they can only be written by their owner * 2. a user can have an arbitrary number of beacon_info events - * + * * 1. is achieved by setting the state_key to the owners mxid. * Event keys in room state are a combination of `type` + `state_key`. - * To achieve an arbitrary number of only owner-writable state events + * To achieve an arbitrary number of only owner-writable state events * we introduce a variable suffix to the event type - * + * * Eg * { * "type": "m.beacon_info.@matthew:matrix.org.1", @@ -58,7 +59,15 @@ import { MAssetEvent, MLocationEvent, MTimestampEvent } from "./location"; * } * } */ + +/** + * Variable event type for m.beacon_info + */ export const M_BEACON_INFO_VARIABLE = new UnstableValue("m.beacon_info.*", "org.matrix.msc3489.beacon_info.*"); + +/** + * Non-variable type for m.beacon_info event content + */ export const M_BEACON_INFO = new UnstableValue("m.beacon_info", "org.matrix.msc3489.beacon_info"); export const M_BEACON = new UnstableValue("m.beacon", "org.matrix.msc3489.beacon"); @@ -92,6 +101,9 @@ export type MBeaconInfoEvent = EitherAnd< } */ +/** + * m.beacon_info.* event content + */ export type MBeaconInfoEventContent = & MBeaconInfoEvent & // creation timestamp of the beacon on the client diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 6419b98bdce..9ab11d6c5ec 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -16,6 +16,9 @@ limitations under the License. /** @module ContentHelpers */ +import { REFERENCE_RELATION } from "matrix-events-sdk"; + +import { MBeaconEventContent, MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon"; import { MsgType } from "./@types/event"; import { TEXT_NODE_TYPE } from "./@types/extensible_events"; import { @@ -186,3 +189,39 @@ export const parseLocationEvent = (wireEventContent: LocationEventWireContent): return makeLocationContent(fallbackText, geoUri, timestamp, description, assetType); }; + +/** + * Beacon event helpers + */ + +export const makeBeaconInfoContent = ( + timeout: number, + description?: string, + assetType?: LocationAssetType, +): MBeaconInfoEventContent => ({ + [M_BEACON_INFO.name]: { + description, + timeout, + }, + [M_TIMESTAMP.name]: Date.now(), + [M_ASSET.name]: { + type: assetType ?? LocationAssetType.Self, + }, +}); + +export const makeBeaconContent = ( + uri: string, + timestamp: number, + beaconInfoId: string, + description?: string, +): MBeaconEventContent => ({ + [M_LOCATION.name]: { + description, + uri, + }, + [M_TIMESTAMP.name]: timestamp, + "m.relates_to": { + rel_type: REFERENCE_RELATION.name, + event_id: beaconInfoId, + }, +}); From 9bbf5b7ab98a55ac84a1b756138cdd4a72aab8ec Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 11 Mar 2022 10:45:04 +0100 Subject: [PATCH 12/13] copyright Signed-off-by: Kerry Archibald --- spec/unit/content-helpers.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/unit/content-helpers.spec.ts b/spec/unit/content-helpers.spec.ts index 02d650732f7..039f704cf3b 100644 --- a/spec/unit/content-helpers.spec.ts +++ b/spec/unit/content-helpers.spec.ts @@ -1,3 +1,19 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + import { REFERENCE_RELATION } from "matrix-events-sdk"; import { M_BEACON_INFO } from "../../src/@types/beacon"; From 9367c6fb1ef4eabe468583d33f0be8bdf8b9c35f Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 11 Mar 2022 11:40:29 +0100 Subject: [PATCH 13/13] add m.beacon_info.live from msc3672 Signed-off-by: Kerry Archibald --- spec/unit/content-helpers.spec.ts | 4 ++++ src/@types/beacon.ts | 3 +++ src/content-helpers.ts | 2 ++ 3 files changed, 9 insertions(+) diff --git a/spec/unit/content-helpers.spec.ts b/spec/unit/content-helpers.spec.ts index 039f704cf3b..d43148dc8d3 100644 --- a/spec/unit/content-helpers.spec.ts +++ b/spec/unit/content-helpers.spec.ts @@ -32,12 +32,14 @@ describe('Beacon content helpers', () => { it('create fully defined event content', () => { expect(makeBeaconInfoContent( 1234, + true, 'nice beacon_info', LocationAssetType.Pin, )).toEqual({ [M_BEACON_INFO.name]: { description: 'nice beacon_info', timeout: 1234, + live: true, }, [M_TIMESTAMP.name]: mockDateNow, [M_ASSET.name]: { @@ -49,6 +51,7 @@ describe('Beacon content helpers', () => { it('defaults timestamp to current time', () => { expect(makeBeaconInfoContent( 1234, + true, 'nice beacon_info', LocationAssetType.Pin, )).toEqual(expect.objectContaining({ @@ -59,6 +62,7 @@ describe('Beacon content helpers', () => { it('defaults asset type to self when not set', () => { expect(makeBeaconInfoContent( 1234, + true, 'nice beacon_info', // no assetType passed )).toEqual(expect.objectContaining({ diff --git a/src/@types/beacon.ts b/src/@types/beacon.ts index d46117dd374..ff3cf64d264 100644 --- a/src/@types/beacon.ts +++ b/src/@types/beacon.ts @@ -75,6 +75,9 @@ export type MBeaconInfoContent = { description?: string; // how long from the last event until we consider the beacon inactive in milliseconds timeout: number; + // true when this is a live location beacon + // https://github.com/matrix-org/matrix-spec-proposals/pull/3672 + live?: boolean; }; export type MBeaconInfoEvent = EitherAnd< diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 9ab11d6c5ec..ce8aebf0af9 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -196,12 +196,14 @@ export const parseLocationEvent = (wireEventContent: LocationEventWireContent): export const makeBeaconInfoContent = ( timeout: number, + isLive?: boolean, description?: string, assetType?: LocationAssetType, ): MBeaconInfoEventContent => ({ [M_BEACON_INFO.name]: { description, timeout, + live: isLive, }, [M_TIMESTAMP.name]: Date.now(), [M_ASSET.name]: {