Skip to content
This repository was archived by the owner on Mar 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
b45290d
Add a public key for VAPID, push ID, and push endpoint (I'm willing t…
GigiaJ Jun 8, 2025
5f52e93
add workbox precaching (for SW caching)
GigiaJ Jun 8, 2025
4cc1179
Add claim and install events
GigiaJ Jun 9, 2025
14085c7
Convert to a more generic implementation for communicating with the c…
GigiaJ Jun 9, 2025
5029ea0
add precaching from workbox import
GigiaJ Jun 9, 2025
4968e23
actually set the path (should fix android blank icon issue)
GigiaJ Jun 9, 2025
3b62bea
add param type
GigiaJ Jun 9, 2025
1d48645
Clean up fetch code
GigiaJ Jun 9, 2025
c77ba1b
Add push notif handling (clicks and push events)
GigiaJ Jun 9, 2025
ca026bf
Add SW update prompt (useful for rolling out new changes and informin…
GigiaJ Jun 9, 2025
f6acd72
Add isProduction to determine the behavior to use for the SW
GigiaJ Jun 9, 2025
1db4685
Embed a very simple message handler between the SW and client for now…
GigiaJ Jun 9, 2025
36227ef
Move MapSet position
GigiaJ Jun 9, 2025
4af5462
clean up imports
GigiaJ Jun 9, 2025
8a3eee3
remove disable import
GigiaJ Jun 9, 2025
a883963
Adds the client state listener for toggling whether to use push notif…
GigiaJ Jun 9, 2025
ac5925f
Add a check for our custom tweak in the in-app notif handling to dete…
GigiaJ Jun 9, 2025
e2fbf9e
Add WebPush separate toggle
GigiaJ Jun 9, 2025
a271a56
Add render changes
GigiaJ Jun 9, 2025
b801f07
update imports
GigiaJ Jun 9, 2025
e38eeb3
Add push notif implementation
GigiaJ Jun 9, 2025
af705f6
add push notif details to client config
GigiaJ Jun 9, 2025
7587403
Update gitignore
GigiaJ Jun 9, 2025
9989176
update package-lock
GigiaJ Jun 9, 2025
39fa3c4
Undo these
GigiaJ Jun 10, 2025
91d6a5d
Pass client config for togglePusher
GigiaJ Jun 10, 2025
f100279
Move message listener and add a togglePush listener (should ensure re…
GigiaJ Jun 10, 2025
07e15e2
Revise our fetch to be more in line with W3C spec
GigiaJ Jun 10, 2025
0c67cb4
Swap to sending to service worker for completion
GigiaJ Jun 10, 2025
1295995
add client config to parameters
GigiaJ Jun 10, 2025
a02a10e
adjust to reflect using the service worker to actually toggle
GigiaJ Jun 10, 2025
13d38e8
Fix imports
GigiaJ Jun 10, 2025
c57509a
Remove large comment block, no longer needed
GigiaJ Jun 10, 2025
b148cc3
Remove subscription / service worker check requirement
GigiaJ Jun 10, 2025
654f32b
Add store to push notif setting to local storage (so we can manage th…
GigiaJ Jun 11, 2025
b8eb1b9
Fix space navigation & view space timeline dev-option (#2358)
ajbura Jun 10, 2025
fb0a0f8
Add allow from currently selected space if no m.space.parent found (#…
ajbura Jun 10, 2025
c416785
Release v4.8.1 (#2360)
ajbura Jun 10, 2025
350f3ac
forgot to remove removed const ref
GigiaJ Jun 11, 2025
815a0ac
set app badge
GigiaJ Jun 11, 2025
f24c6cc
Prevent firefox from crashing because of no badging API
GigiaJ Jun 12, 2025
488bb72
add docs for Sygnal set-up
GigiaJ Jun 19, 2025
8e7c6ff
Bump actions/setup-node from 4.3.0 to 4.4.0 (#2307)
dependabot[bot] Jun 18, 2025
42e552f
Bump docker/build-push-action from 6.15.0 to 6.18.0 (#2351)
dependabot[bot] Jun 18, 2025
72aa386
Remove incorrect nav badge handling placement
GigiaJ Jun 19, 2025
8cbc9ce
Remove incorrect nav badge handling placement
GigiaJ Jun 19, 2025
60af716
move nav badge handling to favicon and sum total there for it
GigiaJ Jun 19, 2025
77bbb94
swap fetch to use retry logic (shouldn't occur very often, but when i…
GigiaJ Jun 19, 2025
891ad03
Merge branch 'cinnyapp:dev' into add-push-notifs
GigiaJ Jun 20, 2025
2861ccd
Add global app events
GigiaJ Jun 21, 2025
ff7c40e
Swap our visibility handler and notification implementation to use th…
GigiaJ Jun 21, 2025
b972f30
Add visibility hook to remove from the ClientRoot
GigiaJ Jun 30, 2025
b689089
Place our storage handling into a state module instead and reference it
GigiaJ Jun 30, 2025
cfea393
Revise to use atom values and simplify; modify to check for valid sub…
GigiaJ Jun 30, 2025
4123299
remove unused method for push notif visibility changes
GigiaJ Jun 30, 2025
296a246
useAppVisibility change hook instead of baking into client root for c…
GigiaJ Jun 30, 2025
d3e3ecb
update imports
GigiaJ Jun 30, 2025
e3a450a
Add deregister pushers component
GigiaJ Jun 30, 2025
35e5d90
use deregister pushers component
GigiaJ Jun 30, 2025
c909629
adjust name to reflect new values
GigiaJ Jun 30, 2025
f31087e
update name
GigiaJ Jun 30, 2025
72bb04f
Swap to using the atom values instead of raw setting them in the comp…
GigiaJ Jun 30, 2025
5831325
Update imports
GigiaJ Jun 30, 2025
6359b85
Update names to reflect real behavior
GigiaJ Jun 30, 2025
3d9f7f4
adjust name of showNotifications to useInAppNotifications and add use…
GigiaJ Jun 30, 2025
9440e63
Merge branch 'cinnyapp:dev' into add-push-notifs
GigiaJ Jun 30, 2025
bc54389
format push notification
krishukr Jul 8, 2025
bb14a04
direct to inbox when notification is clicked
krishukr Jul 8, 2025
8b55ffa
when a notification is clicked, jump to corresponding event
krishukr Jul 11, 2025
6d962a6
avoid duplicated notification from push and in-app
krishukr Jul 11, 2025
10ef89e
close push notifications when all have been read
krishukr Jul 12, 2025
7083bff
feature(sw): add invitation support for push notification
krishukr Jul 13, 2025
7151597
fix(notification): allow in-app notification in background when push …
krishukr Jul 14, 2025
25d77d5
fix(push): use unique tag for push notifications
krishukr Jul 14, 2025
6abd8ff
fix(to): load space list first
krishukr Jul 14, 2025
75816cf
fix(sw): only import matrix sdk types
krishukr Jul 14, 2025
54d2ddf
keep push notification alive
krishukr Jul 28, 2025
6a9fe21
fix(push): prevent sliding back after clicking push notification kill…
krishukr Aug 8, 2025
b56e793
fix(noti): try-catch all badge API calls
krishukr Aug 18, 2025
3ee4985
wait for sync for to room event navigation
krishukr Aug 28, 2025
f66545b
add a fallback push notification
krishukr Aug 28, 2025
ad723f6
lint code
krishukr Aug 30, 2025
3d792f3
lint: minor error during merging
krishukr Oct 23, 2025
09bd903
add `m.sticker` support in push notification
krishukr Sep 5, 2025
37c76c7
fix misleading email format (#32)
OE1KHZ Nov 29, 2025
d933a71
Merge push notifications from GigiaJ/cinny
Mar 2, 2026
641aacf
Merge pull request #2 from 7w1/dev
Just-Insane Mar 2, 2026
86a3906
Add custom theme support
Mar 2, 2026
c3a819c
Revert "Add custom theme support"
Mar 2, 2026
a8c9a7b
Fix notification issue in sw.ts
Mar 2, 2026
49c661b
Combine push notifications with the background notification handler
Mar 2, 2026
58e92c2
Fix issues with in-app and push at same time, and properly switch to …
Mar 2, 2026
759acbb
Force push notifications on mobile, as in-app notifications are unrel…
Mar 2, 2026
db69854
Merge branch 'dev' into dev
Just-Insane Mar 2, 2026
bc2ff06
Pull in upstream changes and run validations
Mar 2, 2026
4288d77
Merge branch '7w1:dev' into dev
Just-Insane Mar 2, 2026
986031c
Fixed import issues
Mar 3, 2026
5903041
Fix es-lint complains
Mar 3, 2026
461666d
Update matrix-sdk.ts - revert previous import changes
Just-Insane Mar 3, 2026
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ node_modules
devAssets

.DS_Store
.idea
.idea

.env
6 changes: 6 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
"disableAccountSwitcher": false,
"matrixToBaseUrl": "https://matrix.to",

"pushNotificationDetails": {
"pushNotifyUrl": "https://cinny.cc/_matrix/push/v1/notify",
"vapidPublicKey": "BHLwykXs79AbKNiblEtZZRAgnt7o5_ieImhVJD8QZ01MVwAHnXwZzNgQEJJEU3E5CVsihoKtb7yaNe5x3vmkWkI",
"webPushAppID": "cc.cinny.web"
},

"featuredCommunities": {
"openAsDefault": false,
"spaces": [
Expand Down
10 changes: 10 additions & 0 deletions docs/Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(tls_cloudflare) {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
}

<URL-HERE> {
import tls_cloudflare
reverse_proxy sygnal:5000
}
9 changes: 9 additions & 0 deletions docs/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM caddy:builder AS builder

RUN xcaddy build \
--with github.com/caddy-dns/cloudflare
FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
COPY Caddyfile /etc/caddy/Caddyfile
COPY .env /etc/caddy/.env
1 change: 1 addition & 0 deletions docs/sample.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CLOUDFLARE_API_TOKEN=<TOKEN-HERE>
308 changes: 308 additions & 0 deletions docs/sygnal-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
## Sygnal with Caddy & Cloudflare on Vultr

This document walks you through setting up a [Sygnal](https://github.com/matrix-org/sygnal) push gateway for Matrix, running in a Docker container. We will use [Caddy](https://caddyserver.com/) as a reverse proxy, also in Docker, to handle HTTPS automatically using DNS challenges with [Cloudflare](https://www.cloudflare.com/).

Now Cloudflare and Vultr have a deal in place where traffic from Cloudflare to Vultr and vice versa does not incur bandwidth usage. So you can pass endless amounts through without any extra billing. This is why the docs utilize Vultr, but you're free to use whatever cloud provider you want and not use Cloudflare if you so choose.

### Prerequisites

1. **Vultr Server**: A running server instance. This guide assumes a fresh server running a common Linux distribution like Debian, Ubuntu, or Alpine.
2. **Domain Name**: A domain name managed through Cloudflare.
3. **Cloudflare Account**: Your domain must be using Cloudflare's DNS.
4. **Docker & Docker Compose**: Docker and `docker-compose` must be installed on your Vultr server.
5. **A Matrix Client**: A client like [Cinny](https://github.com/cinnyapp/cinny) that you want to point to your new push gateway.

---

### Step 1: Cloudflare Configuration

Before touching the server, we need to configure Cloudflare.

#### 1.1. DNS Record

In your Cloudflare dashboard, create an **A** (for IPv4) or **AAAA** (for IPv6) record for the subdomain you'll use for Sygnal. Point it to your Vultr server's IP address.

- **Type**: `A` or `AAAA`
- **Name**: `sygnal.your-domain.com` (or your chosen subdomain)
- **Content**: Your Vultr server's IP address
- **Proxy status**: **Proxied** (Orange Cloud). This is important for Caddy's setup.

#### 1.2. API Token

Caddy needs an API token to prove to Cloudflare that you own the domain so it can create the necessary DNS records for issuing an SSL certificate.

1. Go to **My Profile** \> **API Tokens** in Cloudflare.
2. Click **Create Token**.
3. Use the **Edit zone DNS** template.
4. Under **Permissions**, ensure `Zone:DNS:Edit` is selected.
5. Under **Zone Resources**, select the specific zone for `your-domain.com`.
6. Continue to summary and create the token.
7. **Copy the generated token immediately.** You will not be able to see it again. We will use this as your `CLOUDFLARE_API_TOKEN`.

---

### Step 2: Server Preparation

#### 2.1. Connect to your Server (SSH)

If your Vultr instance uses an IPv6 address, connecting via SSH can sometimes be tricky. You can create an alias in your local `~/.ssh/config` file to make it easier.

Open or create `~/.ssh/config` on your local machine and add:

```
Host vultr-sygnal
# Replace with your server's IPv6 or IPv4 address
Hostname 2001:19f0:5400:1532:5400:05ff:fe78:fb25
User root
# For IPv6, uncomment the line below
# AddressFamily inet6
```

Now you can connect simply by typing `ssh vultr-sygnal`.

#### 2.2. Install Docker and Docker Compose

Follow the official Docker documentation to install the Docker Engine and Docker Compose for your server's operating system.

#### 2.3. Configure Firewall

We need to allow HTTP and HTTPS traffic so Caddy can obtain certificates and serve requests. If you are using `ufw`:

```sh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
```

---

### Step 3: Project Structure and Configuration

On your Vultr server, let's create a directory to hold all our configuration files.

```sh
mkdir -p /opt/matrix-sygnal
cd /opt/matrix-sygnal
```

We will create all subsequent files inside this `/opt/matrix-sygnal` directory.

#### 3.1. Sygnal VAPID Keys

WebPush requires a VAPID key pair. The private key stays on your server, and the public key is given to clients.

1. **Generate the Private Key**:
Use `openssl` to generate an EC private key.

```sh
# This command needs to be run in the /opt/matrix-sygnal directory
openssl ecparam -name prime256v1 -genkey -noout -out sygnal_private_key.pem
```

2. **Extract the Public Key**:
Extract the corresponding public key from the private key. You will need this for your client configuration later.

```sh
# This command extracts the public key in the correct format
openssl ec -in sygnal_private_key.pem -pubout -outform DER | tail -c 65 | base64 | tr '/+' '_-' | tr -d '='
```

**Save the output of this command.** This is your `vapidPublicKey`. It should look similar to the one from the `cinny.cc` example.

#### 3.2. Sygnal Configuration (`sygnal.yaml`)

Create a file named `sygnal.yaml`. This file tells Sygnal how to run.

```yaml
# /opt/matrix-sygnal/sygnal.yaml
http:
bind_addresses: ['0.0.0.0']
port: 5000

# This is where we configure our push gateway app
apps:
# This app_id must match the one used in your client's configuration
cc.cinny.web:
type: webpush
# This path is *inside the container*. We will map our generated key to it.
vapid_private_key: /data/private_key.pem
# An email for VAPID contact details
vapid_contact_email: your-email@your-domain.com
```

#### 3.3. Caddy Configuration (`Caddyfile`)

Create a file named `Caddyfile`. This tells Caddy how to proxy requests.

**Replace `sygnal.your-domain.com`** with the domain you configured in Step 1.

```caddyfile
# /opt/matrix-sygnal/Caddyfile

# Reusable snippet for Cloudflare TLS
(tls_cloudflare) {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
}

# Your public-facing URL
sygnal.your-domain.com {
# Get an SSL certificate from Let's Encrypt using the Cloudflare DNS challenge
import tls_cloudflare

# Log requests to standard output
log

# Reverse proxy requests to the sygnal container on port 5000
# 'sygnal' is the service name we will define in docker-compose.yml
reverse_proxy sygnal:5000
}
```

#### 3.4. Caddy Dockerfile

While you can use the standard `caddy:latest` image, you need one with the Cloudflare DNS provider plugin. Create a file named `Dockerfile` for Caddy.

```dockerfile
# /opt/matrix-sygnal/Dockerfile
FROM caddy:builder AS builder

RUN xcaddy build \
--with github.com/caddy-dns/cloudflare

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```

#### 3.5. Environment File (`.env`)

Create a file named `.env` to securely store your Cloudflare API Token.

```.env
# /opt/matrix-sygnal/.env
CLOUDFLARE_API_TOKEN=your-cloudflare-api-token-from-step-1
```

---

### Step 4: Docker Compose

Using `docker-compose` simplifies managing our multi-container application. Create a `docker-compose.yml` file.

```yaml
# /opt/matrix-sygnal/docker-compose.yml
version: '3.7'

services:
caddy:
# Build the Caddy image from our Dockerfile in the current directory
build: .
container_name: caddy
hostname: caddy
restart: unless-stopped
networks:
- matrix
ports:
# Expose standard web ports to the host
- '80:80'
- '443:443'
volumes:
# Mount the Caddyfile into the container
- ./Caddyfile:/etc/caddy/Caddyfile
# Create a volume for Caddy's data (certs, etc.)
- caddy_data:/data
# Load the Cloudflare token from the .env file
env_file:
- ./.env

sygnal:
# Use the official Sygnal image
image: matrixdotorg/sygnal:latest
container_name: sygnal
hostname: sygnal
restart: unless-stopped
networks:
- matrix
volumes:
# Mount the Sygnal config file
- ./sygnal.yaml:/sygnal.yaml
# Mount the generated private key to the path specified in sygnal.yaml
- ./sygnal_private_key.pem:/data/private_key.pem
# Create a volume for any other data Sygnal might store
- sygnal_data:/data
command: ['--config-path=/sygnal.yaml']

volumes:
caddy_data:
sygnal_data:

networks:
matrix:
driver: bridge
```

---

### Step 5: Launch the Services

Your directory `/opt/matrix-sygnal` should now look like this:

```
/opt/matrix-sygnal/
├── Caddyfile
├── docker-compose.yml
├── Dockerfile
├── .env
├── sygnal.yaml
└── sygnal_private_key.pem
```

Now, you can build and run everything with a single command:

```sh
cd /opt/matrix-sygnal
sudo docker-compose up --build -d
```

- `--build` tells Docker Compose to build the Caddy image from your `Dockerfile`.
- `-d` runs the containers in detached mode (in the background).

To check the status and logs:

```sh
# See if containers are running
sudo docker-compose ps

# View the live logs for both services
sudo docker-compose logs -f

# View logs for a specific service (e.g., caddy)
sudo docker-compose logs -f caddy
```

Caddy will automatically start, obtain an SSL certificate for `sygnal.your-domain.com`, and begin proxying requests to the Sygnal container.

---

### Step 6: Client Configuration

The final step is to configure your Matrix client to use your new push gateway. In Cinny, for example, you would modify its `config.json` or use a homeserver that advertises these settings.

Update the `pushNotificationDetails` section with the information from your server:

```json
"pushNotificationDetails": {
"pushNotifyUrl": "https://sygnal.your-domain.com/_matrix/push/v1/notify",
"vapidPublicKey": "YOUR_VAPID_PUBLIC_KEY_FROM_STEP_3.1",
"webPushAppID": "cc.cinny.web"
}
```

- **`pushNotifyUrl`**: The public URL of your new Sygnal instance.
- **`vapidPublicKey`**: The public key you generated in step 3.1.
- **`webPushAppID`**: The application ID you defined in your `sygnal.yaml`. This must match exactly.

After configuring your client, it will register for push notifications with your Sygnal instance, which will then handle delivering them.
9 changes: 9 additions & 0 deletions docs/sygnal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
http:
bind_addresses: ['0.0.0.0']
port: 5000

apps:
cc.cinny.web:
type: webpush
vapid_private_key: /data/private_key.pem
vapid_contact_email: help@cinny.cc
2 changes: 1 addition & 1 deletion knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"interface": true,
"type": true
},
"ignoreDependencies": ["buffer", "@element-hq/element-call-embedded"],
"ignoreDependencies": ["buffer", "@element-hq/element-call-embedded", "workbox-precaching"],
"rules": {
"exports": "off",
"types": "off",
Expand Down
9 changes: 6 additions & 3 deletions src/app/features/room/RoomTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ const useEventTimelineLoader = (
) =>
useCallback(
async (eventId: string) => {
if (!room.getUnfilteredTimelineSet().getTimelineForEvent(eventId)) {
await mx.roomInitialSync(room.roomId, PAGINATION_LIMIT);
await mx.getLatestTimeline(room.getUnfilteredTimelineSet());
}
const [err, replyEvtTimeline] = await to(
mx.getEventTimeline(room.getUnfilteredTimelineSet(), eventId)
);
Expand Down Expand Up @@ -845,10 +849,9 @@ export function RoomTimeline({

useEffect(() => {
if (eventId) {
setTimeline(getEmptyTimeline());
loadEventTimeline(eventId);
handleOpenEvent(eventId);
}
}, [eventId, loadEventTimeline]);
}, [eventId, handleOpenEvent]);

// Scroll to bottom on initial timeline load
useLayoutEffect(() => {
Expand Down
Loading