Feature: Viewing a Future Message
Relevant source files
The following files were used as context for generating this wiki page:
The "Viewing a Future Message" feature is the culmination of the Heyme application's core promise: allowing users to receive and watch a video message they recorded for themselves or someone else in the past. This process begins with an automated email dispatch on a scheduled date and concludes with the user viewing the video on a dedicated, dynamically generated page.
This system is composed of a scheduled API endpoint for email dispatch, a database for storing video metadata, an email service for delivery, and a server-rendered Next.js page for the final viewing experience. It ensures that messages are delivered reliably on the intended day, providing a powerful and personal connection to the past.
Sources: README.md, pages/api/dispatch.ts:43-46, pages/watch/[id].tsx:62-64
System Architecture & Data Flow
The process of viewing a future message is initiated by a scheduled task that triggers an API endpoint. This endpoint queries the database for messages to be sent, dispatches emails via Zeptomail, and updates the message status. The user receives an email with a unique link, which directs them to a page where the video is fetched from the database and displayed.
The following sequence diagram illustrates the end-to-end data flow from the scheduled trigger to the user watching the video.
This diagram shows the two main phases: the automated email dispatch and the user-initiated video viewing.
Sources: pages/api/dispatch.ts, pages/watch/[id].tsx
Components
Email Dispatch (/api/dispatch)
This is a Next.js API route responsible for sending out email notifications for videos scheduled for the current day. It is designed to be called by a scheduled task (e.g., a cron job).
Key Functions:
| Function | Description | Source File |
|---|---|---|
handleDispatchEmails | Main handler that orchestrates fetching videos, sending emails, and updating the database. | pages/api/dispatch.ts:40-101 |
fetchAllVideosForToday | Queries the Videos table for all records where send_at matches the current date. | pages/api/dispatch.ts:25-28 |
updateVideoToSent | Updates a video record's sent status to 1 to prevent re-sending. | pages/api/dispatch.ts:30-34 |
getEmailContent | Reads the HTML email template and replaces placeholders with dynamic content. | pages/api/dispatch.ts:10-19 |
Logic Flow:
- A connection to the MySQL database is established.
fetchAllVideosForTodayretrieves all videos scheduled for the current date.- The system iterates through the fetched videos. If a video has already been marked as
sent, it is skipped. - The email subject and greeting are customized based on the
send_tofield (yourselfor someone else). - The
getEmailContentfunction populates the email template with a unique watch link (/watch/[id]) and other details. - The Zeptomail client sends the composed email.
- After successful dispatch,
updateVideoToSentflags the video as sent in the database.
Sources: pages/api/dispatch.ts:43-101
Email Template (public/mails/email.html)
A standard HTML file serves as the template for the notification email. It uses simple placeholders that are replaced by the getEmailContent function before sending.
Placeholders:
| Placeholder | Replaced With |
|---|---|
{{REPLACE_WATCH_LINK_HERE}} | The full URL to the watch page, e.g., https://heyme.io/watch/123. |
{{REPLACE_GREETING_HERE}} | A dynamic greeting, e.g., "Do you still remember sending yourself a message on Mar 8, 2023?". |
{{REPLACE_BODY_HERE}} | A static body text explaining what Heyme is. |
{{REPLACE_OUTRO_HERE}} | A static closing message. |
This replacement logic is tested in tests/api/dispatch.spec.ts to ensure correctness.
Sources: public/mails/email.html, pages/api/dispatch.ts:10-19, tests/api/dispatch.spec.ts:4-22
Video Watch Page (/watch/[id])
This is a dynamic Next.js page that displays the video message. It uses Server-Side Rendering (SSR) to fetch the video data before the page is sent to the client.
Server-Side Rendering (getServerSideProps)
The getServerSideProps function is critical for this page's functionality. It runs on the server for each request.
- It receives the video
idfrom the URL parameters. - It establishes a database connection using
createMysqlConnection. - It calls
fetchVideoByIdto retrieve the specific video record from the database. - If no video is found, it returns
{ notFound: true }, which renders a 404 page. - If the video is found, it formats the
created_atdate usingdayjsand passes a cleanvideoobject as props to theWatchVideocomponent.
This server-side logic is validated by unit tests in tests/api/watch.spec.tsx.
Sources: pages/watch/[id].tsx:62-86, tests/api/watch.spec.tsx:40-75
Frontend Component (WatchVideo)
The WatchVideo React component is responsible for rendering the user interface.
- It receives the
videoobject as a prop fromgetServerSideProps. - It displays a title with the message's creation date, e.g., "A message from Mar 8, 2023".
- It uses the
react-html5videolibrary to render the video player, with thevideo_urlas the source. - It includes a browser-detection check to display a warning for
.mkvfiles on iOS, as they may not be playable. - A "Record a message" button is included to encourage user re-engagement.
Sources: pages/watch/[id].tsx:23-50
Data Model
The Videos table in the MySQL database is central to this feature. Its structure can be inferred from its usage across the store, dispatch, and watch features.
| Column | Type | Description | Inferred From |
|---|---|---|---|
id | number | Primary key for the video record. | pages/watch/[id].tsx:14 |
video_url | string | The secure URL of the video file stored on Cloudinary. | pages/api/store.ts:12 |
email | string | The recipient's email address. | pages/api/store.ts:13 |
send_at | string | The scheduled send date in 'YYYY-MM-DD' format. | pages/api/dispatch.ts:27 |
send_to | string | Recipient type, either 'yourself' or 'someone_else'. | pages/api/store.ts:15 |
created_at | string | The date the message was recorded. | pages/watch/[id].tsx:16 |
sent | number | A boolean flag (0 or 1) to indicate if the email has been sent. | pages/api/dispatch.ts:32 |
Sources: pages/api/store.ts:11-17, pages/api/dispatch.ts:25-34, pages/watch/[id].tsx:12-18
Testing
The feature is supported by unit tests to ensure the reliability of its server-side logic.
tests/api/watch.spec.tsx: These tests verify the behavior ofgetServerSidePropson the watch page. They mock the database connection andfetchVideoByIdfunction to test two primary scenarios:- A valid video ID correctly returns the video data as props.
- An invalid video ID correctly returns a
notFound: trueresult.
tests/api/dispatch.spec.ts: This test focuses on thegetEmailContenthelper function. It confirms that all placeholders in theemail.htmltemplate are correctly replaced with the provided dynamic data, ensuring the email body is generated as expected.
Sources: tests/api/watch.spec.tsx, tests/api/dispatch.spec.ts