Ex09: New Viewport adapter

In this example we create a new viewport adapter and a dapplet for it.

Here is the initial code for this example: ex09-new-viewport-adapter-exercise.

Our template has an adapter that in structure is similar to the adapter in the previous exercise.

1adapter
2├── .gitignore
3├── dapplet.json
4├── package-lock.json
5├── package.json
6├── rollup.config.js
7├── src
8| ├── button.ts
9| ├── close_cross.svg
10| ├── globals.d.ts
11| ├── index.ts
12| └── popup.ts
13└── tsconfig.json

When you create an adapter don't forget to set contextIds in /adapter/dapplet.json. On these sites, the adapter will work:

1{
2 ...
3 "contextIds": [
4 "dapplets.org",
5 "127.0.0.1",
6 "forum.dapplets.org",
7 "youtube.com",
8 "www.youtube.com",
9 "twitter.com",
10 "mobile.twitter.com",
11 "google.com",
12 "www.google.com",
13 "www.instagram.com",
14 "instagram.com"
15 ],
16 ...
17}
  1. Implement communication between dapplets and pages in /adapter/src/index.ts:
1public config = {
2 BODY: {
3 containerSelector: 'html',
4 contextSelector: 'body',
5 insPoints: {
6 BODY: {},
7 },
8 // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
9 contextBuilder: (): ContextBuilder => ({
10 id: document.location.href,
11 }),
12 },
13};
  1. Implement the button markup with image and tooltip in /adapter/src/button.ts:
1// class Button
2// LP: 2. implement the button markup with "image" and "tooltip".
3const { img, tooltip } = this.state;
4const htmlString = `
5 <div style="
6 position:fixed;
7 width:60px;
8 height:60px;
9 bottom:40px;
10 left:40px;
11 color:#FFF;
12 border-radius:99em;
13 text-align:center;
14 box-shadow: 2px 2px 3px #999;
15 cursor: pointer;
16 display: block;
17 box-sizing: content-box;
18 background: no-repeat center/100% url(${img}) #d4e0e9;
19 ">
20 </div>
21`;
22this.el.title = tooltip ?? '';
23this.el.innerHTML = htmlString;
24// LP end

Define contextInsPoints for the button

public static contextInsPoints = {
BODY: 'BODY'
}
  1. Implement the popup markup with text, link, img and closed:
1// LP: 3. Implement the popup markup with "text", "link", "img" and "closed".
2const { text, link, img, closed } = this.state;
3const htmlString = `
4 <style>
5 .dapplet-widget-basic-container {
6 position: absolute;
7 overflow-wrap: break-word;
8 width: 380px;
9 min-height: 60px;
10 top: 80px;
11 left: 50%;
12 margin-left: -212px;
13 padding: 20px;
14 background-color: #fff;
15 color: #d10019;
16 border: 2px solid #d10019;
17 border-radius: 15px;
18 text-align: center;
19 box-shadow: 2px 2px 3px #999;
20 box-sizing: content-box;
21 font-size: 19px;
22 font-weight: 600;
23 font-family: system-ui, -apple-system, sans-serif, BlinkMacSystemFont, Roboto, Ubuntu;
24 z-index: 9998;
25 }
26 .dapplet-widget-close {
27 position: initial;
28 }
29 .dapplet-widget-close-icon {
30 width: 18px;
31 height: 18px;
32 position: absolute;
33 right: 12px;
34 top: 12px;
35 cursor: pointer;
36 }
37 .dapplet-widget-mascot-img {
38 position: initial;
39 }
40 .dapplet-widget-mascot-img img {
41 width: 18px;
42 height: 18px;
43 position: absolute;
44 left: 12px;
45 top: 12px;
46 cursor: pointer;
47 }
48 .dapplet-widget-basic-container a {
49 text-decoration: none;
50 color: #d10019;
51 }
52 .dapplet-widget-basic-container a:active {
53 text-decoration: none;
54 color: #d10019;
55 }
56 .dapplet-widget-basic-container a:visited {
57 text-decoration: none;
58 color: #d10019;
59 }
60 .displayed {
61 display: block;
62 }
63 .no-displayed {
64 display: none;
65 }
66 </style>
67 <div class='dapplet-widget-basic-container ${closed ? 'no-displayed' : 'displayed'} '>
68 <div class='dapplet-widget-close'>
69 <img src="${CLOSE_ICON}" class='dapplet-widget-close-icon' alt='close icon'>
70 </div>
71 <div class='dapplet-widget-mascot-img'>
72 <img src='${img}' alt='CLOSE'>
73 </div>
74 <div>
75 <a href='${link}' target='_blank'>${text}</a>
76 </div>
77 </div>
78`;
79this.el.innerHTML = htmlString;
80// LP end

Define contextInsPoints for the popup

public static contextInsPoints = {
BODY: 'BODY'
}
  1. Change dependencies and contextIds in /dapplet-feature/dapplet.json to new adapter:
1{
2 ...
3 "contextIds": ["example-viewport-adapter.dapplet-base.eth"],
4 ...
5 "dependencies": {
6 "example-viewport-adapter.dapplet-base.eth": "0.2.0"
7 }
8}
  1. Add a valid adapter in /dapplet-feature/src/index.ts:
@Inject('example-viewport-adapter.dapplet-base.eth') public adapter: any;
  1. Add popup with text, image and some link from the page in BODY:
1popup({
2 id: 'popup',
3 initial: 'DEFAULT',
4 DEFAULT: {
5 text: 'The link opens this page in a new tab',
6 img: EXAMPLE_IMG,
7 init: (_, me) => (me.link = ctx.id),
8 },
9}),
  1. On button click show the popup:
1button({
2 ...
3 DEFAULT: {
4 ...
5 exec: () => ($(ctx, 'popup').closed = false),
6 },
7}),

Here is the result: ex09-new-viewport-adapter-solution.

Run the dapplet:

npm i
npm start

In this example we run two servers concurrently. So you have to add two registry addresses to Dapplet extension in Development tab. Click here for instructions.