+85
-5
atproto-notifications/src/App.css
+85
-5
atproto-notifications/src/App.css
···
1
1
#root {
2
-
max-width: 1280px;
3
2
margin: 0 auto;
4
-
padding: 2rem;
3
+
max-width: 1280px;
4
+
padding: 0;
5
5
text-align: center;
6
+
min-height: 100vh;
7
+
display: flex;
8
+
flex-direction: column;
9
+
justify-content: space-between;
10
+
}
11
+
12
+
#app-header {
13
+
background-color: #0b0d0f;
14
+
padding: 0.25rem;
15
+
border-bottom: 1px solid hsla(0, 0%, 50%, 0.3);
16
+
display: flex;
17
+
justify-content: space-around;
18
+
align-items: baseline;
19
+
}
20
+
21
+
#app-header .handle {
22
+
display: inline-block;
23
+
margin-right: 0.5rem;
24
+
color: #999;
6
25
}
7
26
8
-
.card {
9
-
padding: 2em;
27
+
.demo {
28
+
background: #2c343c;
29
+
border-radius: 0.25rem;
30
+
color: #f90;
31
+
display: inline-block;
32
+
font-size: 0.8rem;
33
+
font-weight: normal;
34
+
margin: 0 0 0 0.25rem;
35
+
padding: 0.1rem 0.333rem 0.15rem;
36
+
transform: rotate(-13deg);
37
+
vertical-align: top;
38
+
}
39
+
40
+
#app-content {
41
+
padding: 0.5rem 1rem;
10
42
}
11
43
12
-
.read-the-docs {
44
+
.detail {
45
+
font-style: italic;
46
+
font-size: 0.8rem;
47
+
max-width: 24em;
48
+
margin: 0 auto;
49
+
}
50
+
51
+
.footer {
52
+
box-sizing: border-box;
53
+
background: #0b0d0f;
54
+
width: 100%;
55
+
text-align: center;
56
+
margin: 3rem 0 0;
57
+
padding: 0.667rem 0.5rem 0;
58
+
font-size: 0.85rem;
59
+
60
+
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
61
+
font-weight: normal;
62
+
line-height: 1.5rem;
63
+
64
+
border-top: 1px solid hsla(0, 0%, 50%, 0.3);
65
+
box-shadow: 0 108px 81px -81px inset #221828;
66
+
67
+
color-scheme: dark;
68
+
color: #e6ece6;
69
+
}
70
+
.footer p {
71
+
margin: 0;
72
+
}
73
+
.footer p.from {
13
74
color: #888;
14
75
}
76
+
.footer p.actions {
77
+
display: flex;
78
+
gap: 1rem;
79
+
justify-content: center;
80
+
margin: 0.5rem 0;
81
+
}
82
+
.footer p.secret-dev {
83
+
font-size: 0.667rem;
84
+
}
85
+
.footer p.secret-dev:not(:hover) {
86
+
opacity: 0.5;
87
+
}
88
+
89
+
@media screen and (max-width: 600px) {
90
+
.footer {
91
+
border-radius: 0;
92
+
position: relative;
93
+
}
94
+
}
+63
-27
atproto-notifications/src/App.tsx
+63
-27
atproto-notifications/src/App.tsx
···
100
100
} else if (notifPerm !== 'granted') {
101
101
content = (
102
102
<>
103
-
<h3>Step 2: Notification permission</h3>
104
-
<p>To show atproto notifications we need permission:</p>
103
+
<h3>Step 2: Allow notifications</h3>
104
+
<p>To show notifications we need permission:</p>
105
105
<p>
106
106
<button
107
107
onClick={requestPermission(host, setAsking)}
···
111
111
</button>
112
112
</p>
113
113
{notifPerm === 'denied' ? (
114
-
<p><em>Notification permission was denied. You may need to clear the browser setting to try again.</em></p>
114
+
<p className="detail">Notification permission was denied. You may need to clear the browser setting to try again.</p>
115
115
) : (
116
-
<p><em>You can revoke this any time</em></p>
116
+
<p className="detail">You can revoke this any time</p>
117
117
)}
118
118
</>
119
119
);
120
120
} else {
121
-
content = (
122
-
<>
123
-
<p>
124
-
@{user.handle}
125
-
<button onClick={() => setUser(null)}>×</button>
126
-
</p>
127
-
<Feed />
128
-
</>
129
-
);
121
+
content = <Feed />;
130
122
}
131
123
132
124
return (
133
125
<HostContext.Provider value={host}>
134
-
<h1>🎇 atproto notifications demo</h1>
126
+
<header id="app-header">
127
+
<h1>spacedust notifications <span className="demo">demo!</span></h1>
128
+
{user && (
129
+
<div className="current-user">
130
+
<p>
131
+
<span className="handle">@{user.handle}</span>
132
+
{/* TODO: clear *all* info on logout */}
133
+
<button className="subtle bad" onClick={() => setUser(null)}>×</button>
134
+
</p>
135
+
</div>
136
+
)}
137
+
</header>
135
138
136
-
<p>Get browser push notifications from any app</p>
139
+
<div id="app-content">
140
+
{content}
141
+
</div>
137
142
138
-
{content}
143
+
<div className="footer">
144
+
<p className="from">
145
+
This demo is part of
146
+
{' '}
147
+
<a href="https://microcosm.blue" className="external" target="_blank">
148
+
<span style={{ color: '#f396a9' }}>m</span>
149
+
<span style={{ color: '#f49c5c' }}>i</span>
150
+
<span style={{ color: '#c7b04c' }}>c</span>
151
+
<span style={{ color: '#92be4c' }}>r</span>
152
+
<span style={{ color: '#4ec688' }}>o</span>
153
+
<span style={{ color: '#51c2b6' }}>c</span>
154
+
<span style={{ color: '#54bed7' }}>o</span>
155
+
<span style={{ color: '#8fb1f1' }}>s</span>
156
+
<span style={{ color: '#ce9df1' }}>m</span>
157
+
158
+
</a>
159
+
</p>
160
+
<p className="actions">
161
+
<a href="https://bsky.app/profile/microcosm.blue" target="_blank" className="external">
162
+
🦋 follow
163
+
</a>
164
+
<a href="https://github.com/sponsors/uniphil/" target="_blank" className="external">
165
+
💸 support
166
+
</a>
167
+
<a href="https://github.com/at-microcosm/spacedust-utils" target="_blank" className="external">
168
+
👩🏻💻 source
169
+
</a>
170
+
</p>
171
+
172
+
<p className="secret-dev">
173
+
secret dev setting:
174
+
{' '}
175
+
<label>
176
+
<input
177
+
type="checkbox"
178
+
onChange={e => setDev(e.target.checked)}
179
+
checked={true /*isDev(ufosHost)*/}
180
+
/>
181
+
localhost
182
+
</label>
183
+
</p>
184
+
</div>
139
185
</HostContext.Provider>
140
186
)
141
187
}
142
188
143
-
export default App
144
-
145
-
146
-
// {user === null ? (
147
-
148
-
// ) : (
149
-
// <>
150
-
// <p>hi {user.handle}</p>
151
-
// <button onClick={() => setUser(null)}>clear</button>
152
-
// </>
153
-
// )}
189
+
export default App;
+18
-4
atproto-notifications/src/index.css
+18
-4
atproto-notifications/src/index.css
···
3
3
line-height: 1.5;
4
4
font-weight: 400;
5
5
6
-
color-scheme: light dark;
6
+
color-scheme: dark;
7
7
color: #d6dade;
8
8
background-color: #161a1e;
9
9
···
34
34
}
35
35
36
36
button {
37
-
border-radius: 8px;
37
+
border-radius: 0.5rem;
38
38
border: 1px solid transparent;
39
39
padding: 0.6em 1.2em;
40
40
font-size: 1em;
41
41
font-weight: 500;
42
42
font-family: inherit;
43
-
background-color: #1a1a1a;
43
+
background-color: #0b0d0f;
44
44
cursor: pointer;
45
-
transition: border-color 0.25s;
45
+
border: 1px solid hsla(0, 0%, 50%, 0.3);
46
+
border-bottom-color: hsla(0, 0%, 0%, 0.3);
47
+
border-right-color: hsla(0, 0%, 0%, 0.3);
48
+
box-shadow: 0 42px 42px -42px inset #221828;
46
49
}
47
50
button:hover {
48
51
border-color: #646cff;
···
50
53
button:focus,
51
54
button:focus-visible {
52
55
outline: 4px auto -webkit-focus-ring-color;
56
+
}
57
+
58
+
button.subtle {
59
+
background: transparent;
60
+
border: none;
61
+
margin: 0;
62
+
padding: 0;
63
+
font: inherit;
64
+
}
65
+
button.bad {
66
+
color: tomato;
53
67
}
54
68
55
69
@media (prefers-color-scheme: light) {