11import { AppServer , AppSession , ViewType , AuthenticatedRequest , PhotoData } from '@mentra/sdk' ;
22import { Request , Response } from 'express' ;
3+ import * as ejs from 'ejs' ;
4+ import * as path from 'path' ;
35
46/**
57 * Interface representing a stored photo with metadata
@@ -26,6 +28,7 @@ class ExampleMentraOSApp extends AppServer {
2628 private photos : Map < string , StoredPhoto > = new Map ( ) ; // Store photos by userId
2729 private latestPhotoTimestamp : Map < string , number > = new Map ( ) ; // Track latest photo timestamp per user
2830 private isStreamingPhotos : Map < string , boolean > = new Map ( ) ; // Track if we are streaming photos for a user
31+ private nextPhotoTime : Map < string , number > = new Map ( ) ; // Track next photo time for a user
2932
3033 constructor ( ) {
3134 super ( {
@@ -39,16 +42,11 @@ class ExampleMentraOSApp extends AppServer {
3942 }
4043
4144 /**
42- * Configure EJS as the view engine for Express
45+ * Configure EJS for manual template rendering
4346 */
4447 private setupEJS ( ) : void {
45- const app = this . getExpressApp ( ) ;
46-
47- // Set EJS as the view engine
48- app . set ( 'view engine' , 'ejs' ) ;
49-
50- // Set the views directory
51- app . set ( 'views' , './views' ) ;
48+ // EJS will be used manually to avoid module resolution issues
49+ // No Express view engine configuration needed
5250 }
5351
5452 /**
@@ -58,7 +56,7 @@ class ExampleMentraOSApp extends AppServer {
5856 const app = this . getExpressApp ( ) ;
5957
6058 // Main webview route - displays the photo viewer interface
61- app . get ( '/webview' , ( req : any , res : any ) => {
59+ app . get ( '/webview' , async ( req : any , res : any ) => {
6260 const userId = ( req as AuthenticatedRequest ) . authUserId ;
6361
6462 if ( ! userId ) {
@@ -73,8 +71,23 @@ class ExampleMentraOSApp extends AppServer {
7371 return ;
7472 }
7573
76- // Render the photo viewer EJS template
77- res . render ( 'photo-viewer' ) ;
74+ try {
75+ // Manually render the EJS template to avoid module resolution issues
76+ const templatePath = path . join ( process . cwd ( ) , 'views' , 'photo-viewer.ejs' ) ;
77+ const html = await ejs . renderFile ( templatePath , { } ) ;
78+ res . send ( html ) ;
79+ } catch ( error ) {
80+ this . logger . error ( `Error rendering EJS template: ${ error } ` ) ;
81+ res . status ( 500 ) . send ( `
82+ <html>
83+ <head><title>Photo Viewer - Error</title></head>
84+ <body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
85+ <h1>Error loading photo viewer</h1>
86+ <p>Please try refreshing the page</p>
87+ </body>
88+ </html>
89+ ` ) ;
90+ }
7891 } ) ;
7992
8093 // API endpoint to get the latest photo for the authenticated user
@@ -131,6 +144,7 @@ class ExampleMentraOSApp extends AppServer {
131144 protected async onSession ( session : AppSession , sessionId : string , userId : string ) : Promise < void > {
132145 this . logger . info ( `Session started for user ${ userId } ` ) ;
133146 this . isStreamingPhotos . set ( userId , false ) ;
147+ this . nextPhotoTime . set ( userId , Date . now ( ) ) ;
134148
135149 session . events . onButtonPress ( async ( button ) => {
136150 this . logger . info ( `Button pressed: ${ button . buttonId } , type: ${ button . pressType } ` ) ;
@@ -140,25 +154,37 @@ class ExampleMentraOSApp extends AppServer {
140154 this . logger . info ( `Streaming photos for user ${ userId } is now ${ this . isStreamingPhotos . get ( userId ) } ` ) ;
141155 return ;
142156 } else {
157+ try {
143158 const photoRequest = session . camera . requestPhoto ( ) ;
144159 photoRequest . catch ( ( error ) => this . logger . error ( `Error taking photo: ${ error } ` ) ) ;
145160 photoRequest . then ( ( photo ) => {
146- this . logger . info ( `Photo taken for user ${ userId } , timestamp: ${ photo . timestamp } ` ) ;
147- this . cachePhoto ( photo , userId ) ;
148- } ) ;
161+ this . logger . info ( `Photo taken for user ${ userId } , timestamp: ${ photo . timestamp } ` ) ;
162+ this . cachePhoto ( photo , userId ) ;
163+ } ) ;
164+ } catch ( error ) {
165+ this . logger . error ( `Error taking photo: ${ error } ` ) ;
166+ }
149167 }
150168 } ) ;
151169
152170 setInterval ( async ( ) => {
153- if ( this . isStreamingPhotos . get ( userId ) ) {
171+ if ( this . isStreamingPhotos . get ( userId ) && Date . now ( ) > ( this . nextPhotoTime . get ( userId ) ?? 0 ) ) {
154172 try {
173+ this . nextPhotoTime . set ( userId , Date . now ( ) + 30000 ) ;
155174 const photo = await session . camera . requestPhoto ( ) ;
175+ this . nextPhotoTime . set ( userId , Date . now ( ) - 1 ) ;
156176 this . cachePhoto ( photo , userId ) ;
157177 } catch ( error ) {
158178 this . logger . error ( `Error auto-taking photo: ${ error } ` ) ;
159179 }
160180 }
161- } , 1000 ) ;
181+ } , 2000 ) ;
182+ }
183+
184+ protected async onStop ( sessionId : string , userId : string , reason : string ) : Promise < void > {
185+ this . isStreamingPhotos . set ( userId , false ) ;
186+ this . nextPhotoTime . delete ( userId ) ;
187+ this . logger . info ( `Session stopped for user ${ userId } , reason: ${ reason } ` ) ;
162188 }
163189
164190 private async cachePhoto ( photo : PhotoData , userId : string ) {
0 commit comments