Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
12 changes: 6 additions & 6 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
"one-var": ["warn"],
"padded-blocks": ["warn"],
"no-extend-native": ["warn"],
camelcase: ["warn"],
"camelcase": ["warn"],
"no-multi-spaces": ["error", { ignoreEOLComments: true }],
"space-before-function-paren": [
"error",
Expand All @@ -26,20 +26,20 @@ module.exports = {
],
"arrow-parens": "off",
"prefer-promise-reject-errors": "off",
quotes: "off",
indent: "off",
"quotes": "off",
"indent": "off",
"no-constant-condition": "off",
"no-async-promise-executor": "off",
},
overrides: [
{
files: ["src/**/*.ts", "test/**/*.ts"],
extends: ["plugin:matrix-org/typescript", "prettier"],
extends: ["plugin:matrix-org/typescript"],
rules: {
// TypeScript has its own version of this
"babel/no-invalid-this": "off",

quotes: "off",
"quotes": "off",
},
},
{
Expand All @@ -49,4 +49,4 @@ module.exports = {
},
},
],
}
};
3 changes: 3 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ jobs:
- name: Check Linting Rules and Types
run: yarn lint

- name: Check Formatting
run: yarn prettier:check

- name: test
run: yarn test --coverage

Expand Down
4 changes: 0 additions & 4 deletions .prettierrc

This file was deleted.

1 change: 1 addition & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.formatOnSave": false
}
56 changes: 24 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,40 +36,34 @@ to instantiate the `WidgetApi` class.
The general usage for this would be:

```typescript
const widgetId = null // if you know the widget ID, supply it.
const api = new WidgetApi(widgetId)
const widgetId = null; // if you know the widget ID, supply it.
const api = new WidgetApi(widgetId);

// Before doing anything else, request capabilities:
api.requestCapability(MatrixCapabilities.Screenshots)
api.requestCapabilities(StickerpickerCapabilities)
api.requestCapability(MatrixCapabilities.Screenshots);
api.requestCapabilities(StickerpickerCapabilities);

// Add custom action handlers (if needed)
api.on(
`action:${WidgetApiToWidgetAction.UpdateVisibility}`,
(ev: CustomEvent<IVisibilityActionRequest>) => {
ev.preventDefault() // we're handling it, so stop the widget API from doing something.
console.log(ev.detail) // custom handling here
api.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{})
},
)
api.on(
"action:com.example.my_action",
(ev: CustomEvent<ICustomActionRequest>) => {
ev.preventDefault() // we're handling it, so stop the widget API from doing something.
console.log(ev.detail) // custom handling here
api.transport.reply(ev.detail, { custom: "reply" })
},
)
api.on(`action:${WidgetApiToWidgetAction.UpdateVisibility}`, (ev: CustomEvent<IVisibilityActionRequest>) => {
ev.preventDefault(); // we're handling it, so stop the widget API from doing something.
console.log(ev.detail); // custom handling here
api.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
});
api.on("action:com.example.my_action", (ev: CustomEvent<ICustomActionRequest>) => {
ev.preventDefault(); // we're handling it, so stop the widget API from doing something.
console.log(ev.detail); // custom handling here
api.transport.reply(ev.detail, { custom: "reply" });
});

// Start the messaging
api.start()
api.start();

// If waitForIframeLoad is false, tell the client that we're good to go
api.sendContentLoaded()
api.sendContentLoaded();

// Later, do something else (if needed)
api.setAlwaysOnScreen(true)
api.transport.send("com.example.my_action", { isExample: true })
api.setAlwaysOnScreen(true);
api.transport.send("com.example.my_action", { isExample: true });
```

For a more complete example, see the `examples` directory of this repo.
Expand All @@ -83,17 +77,15 @@ SDK to provide an interface for other platforms.
TODO: Improve this

```typescript
const driver = new CustomDriver() // an implementation of WidgetDriver
const api = new ClientWidgetApi(widget, iframe, driver)
const driver = new CustomDriver(); // an implementation of WidgetDriver
const api = new ClientWidgetApi(widget, iframe, driver);

// The API is automatically started, so we just have to wait for a ready before doing something
api.on("ready", () => {
api.updateVisibility(true).then(() =>
console.log("Widget knows it is visible now"),
)
api.transport.send("com.example.my_action", { isExample: true })
})
api.updateVisibility(true).then(() => console.log("Widget knows it is visible now"));
api.transport.send("com.example.my_action", { isExample: true });
});

// Eventually, stop the API handling
api.stop()
api.stop();
```
46 changes: 20 additions & 26 deletions examples/widget/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,63 +56,57 @@
<!-- The actual widget functionality -->
<script type="text/javascript">
try {
const qs = parseFragment()
const widgetId = assertParam(qs, "widgetId")
const userId = assertParam(qs, "userId")
const qs = parseFragment();
const widgetId = assertParam(qs, "widgetId");
const userId = assertParam(qs, "userId");
// Allow all origins
const targetOrigin = "*"
let isSticky = false
const targetOrigin = "*";
let isSticky = false;

// Set up the widget API as soon as possible to avoid problems with the client
const widgetApi = new mxwidgets.WidgetApi(
widgetId,
targetOrigin,
)
widgetApi.requestCapability(
mxwidgets.MatrixCapabilities.AlwaysOnScreen,
)
const widgetApi = new mxwidgets.WidgetApi(widgetId, targetOrigin);
widgetApi.requestCapability(mxwidgets.MatrixCapabilities.AlwaysOnScreen);

widgetApi.on("ready", function () {
// Fill in the basic widget details now that we're allowed to operate.
document.getElementById("container").innerHTML =
"Hello <span id='userId'></span>!<br /><br />" +
"Currently stuck on screen: <span id='stickyState'></span><br /><br />" +
"<button onclick='toggleSticky()'>Toggle sticky state</button>"
"<button onclick='toggleSticky()'>Toggle sticky state</button>";

// Fill in the user ID using innerText to avoid XSS
document.getElementById("userId").innerText = userId
document.getElementById("userId").innerText = userId;

// Update the UI and ensure that we end up not sticky to start
sendStickyState()
})
sendStickyState();
});

// Start the widget as soon as possible too, otherwise the client might time us out.
widgetApi.start()
widgetApi.start();

function toggleSticky() {
// called by the button when clicked - toggle the sticky state
isSticky = !isSticky
sendStickyState()
isSticky = !isSticky;
sendStickyState();
}

function updateStickyState() {
document.getElementById("stickyState").innerText =
isSticky.toString()
document.getElementById("stickyState").innerText = isSticky.toString();
}

function sendStickyState() {
updateStickyState() // update first to make the UI go faster than the request
updateStickyState(); // update first to make the UI go faster than the request
widgetApi
.setAlwaysOnScreen(isSticky)
.then(function (r) {
console.log("[Widget] Client responded with: ", r)
console.log("[Widget] Client responded with: ", r);
})
.catch(function (e) {
handleError(e)
})
handleError(e);
});
}
} catch (e) {
handleError(e)
handleError(e);
}
</script>
</body>
Expand Down
18 changes: 7 additions & 11 deletions examples/widget/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,17 @@
*/

function parseFragment() {
const fragmentString = window.location.hash || "?"
return new URLSearchParams(
fragmentString.substring(Math.max(fragmentString.indexOf("?"), 0)),
)
const fragmentString = window.location.hash || "?";
return new URLSearchParams(fragmentString.substring(Math.max(fragmentString.indexOf("?"), 0)));
}

function assertParam(fragment, name) {
const val = fragment.get(name)
if (!val)
throw new Error(`${name} is not present in URL - cannot load widget`)
return val
const val = fragment.get(name);
if (!val) throw new Error(`${name} is not present in URL - cannot load widget`);
return val;
}

function handleError(e) {
console.error(e)
document.getElementById("container").innerText =
"There was an error with the widget. See JS console for details."
console.error(e);
document.getElementById("container").innerText = "There was an error with the widget. See JS console for details.";
}
10 changes: 5 additions & 5 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { env } from "process"
import { env } from "process";

import type { Config } from "jest"
import type { Config } from "jest";

const config: Config = {
testEnvironment: "jsdom",
testMatch: ["<rootDir>/test/**/*-test.[jt]s?(x)"],
collectCoverageFrom: ["<rootDir>/src/**/*.{js,ts,tsx}"],
coverageReporters: ["text-summary", "lcov"],
testResultsProcessor: "@casualbot/jest-sonar-reporter",
}
};

// if we're running under GHA, enable the GHA reporter
if (env["GITHUB_ACTIONS"] !== undefined) {
config.reporters = [["github-actions", { silent: false }], "summary"]
config.reporters = [["github-actions", { silent: false }], "summary"];
}

export default config
export default config;
Loading
Loading