Skip to content

Commit

Permalink
ft: add envoy OK page at / + list notes UI + timestamp in proto struct
Browse files Browse the repository at this point in the history
  • Loading branch information
shivanshs9 committed Nov 11, 2024
1 parent 299d601 commit 63700c5
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 33 deletions.
53 changes: 33 additions & 20 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type { NoteServiceHandlers } from "./generated/notes/NoteService";
import type { CreateNoteRequest } from "./generated/notes/CreateNoteRequest";
import type { CreateNoteResponse } from "./generated/notes/CreateNoteResponse";
import { Status } from "@grpc/grpc-js/build/src/constants";
import { ListNotesRequest } from "./generated/notes/ListNotesRequest";
import { ListNotesResponse } from "./generated/notes/ListNotesResponse";

const PROTO_FILE = "notes.proto";

Expand All @@ -33,7 +35,7 @@ const NoteService: NoteServiceHandlers = {
) => {
console.log("create a new note...");
const { audio, transcription } = call.request;
console.log(call.getPeer())
console.log(call.getPeer());

if (!transcription) {
callback(null, {
Expand All @@ -44,33 +46,44 @@ const NoteService: NoteServiceHandlers = {
}

// I will not save audio, because too much work for an interview :/
DB.getRepository(DBNote).save({
transcription: transcription
}).then((newNote) => {
callback(null, {
status: "success",
errorMessage: "",
note: {
transcription: newNote.transcription,
id: "" + newNote.id
},
});
}).catch((err) => {
callback({
code: Status.INTERNAL,
details: err
DB.getRepository(DBNote)
.save({
transcription: transcription,
})
})
.then((newNote) => {
callback(null, {
status: "success",
errorMessage: "",
note: {
transcription: newNote.transcription,
id: "" + newNote.id,
},
});
})
.catch((err) => {
callback({
code: Status.INTERNAL,
details: err,
});
});
},
// RPC Method to list all notes
ListNotes: (
call: grpc.ServerUnaryCall<any, any>,
callback: grpc.sendUnaryData<any>
call: grpc.ServerUnaryCall<ListNotesRequest, ListNotesResponse>,
callback: grpc.sendUnaryData<ListNotesResponse>
) => {
console.log("fetching all notes...");
DB.getRepository(DBNote)
.find()
.then((allNotes) => callback(null, { notes: allNotes }));
.then((allNotes) =>
callback(null, {
notes: allNotes.map((note) => ({
transcription: note.transcription,
id: "" + note.id,
createdAt: note.createdAt.getMilliseconds().toString(),
})),
})
);
},
};
function startServer() {
Expand Down
34 changes: 24 additions & 10 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
import { AudioManager } from "./components/AudioManager";
import Transcript from "./components/Transcript";
import { useTranscriber } from "./hooks/useTranscriber";
import { useState } from "react";
import CreateNote from "./pages/CreateNote";
import ListNotes from "./pages/ListNotes";

// @ts-ignore
const IS_WEBGPU_AVAILABLE = !!navigator.gpu;

function App() {
const transcriber = useTranscriber();
const [menuOpen, setMenuOpen] = useState(false);
const [currentPage, setCurrentPage] = useState("create");

const toggleMenu = () => {
setMenuOpen(!menuOpen);
};

const switchPage = (page: string) => {
setCurrentPage(page);
setMenuOpen(false);
};

return IS_WEBGPU_AVAILABLE ? (
<div className='flex justify-center items-center min-h-screen'>
<div className='container flex flex-col justify-center items-center'>
<div className='absolute top-4 left-4 z-20'>
<button onClick={toggleMenu} className='hamburger'>
{menuOpen ? '✖' : '☰'}
</button>
</div>
<div className={`menu ${menuOpen ? 'open' : ''}`}>
<a href='#' onClick={() => switchPage("create")} className='menu-item'>Write a new entry...</a>
<a href='#' onClick={() => switchPage("list")} className='menu-item'>List Notes</a>
</div>
<h1 className='text-5xl font-extrabold tracking-tight text-slate-900 sm:text-7xl text-center'>
Whisper Notes
</h1>
<h2 className='mt-3 mb-5 px-4 text-center text-1xl font-semibold tracking-tight text-slate-900 sm:text-2xl'>
Write Notes on the Go! <br />
ML-powered speech recognition directly in your browser.
</h2>
<AudioManager transcriber={transcriber} />
<Transcript transcribedData={transcriber.output} />
{currentPage === "create" ? <CreateNote /> : <ListNotes />}
</div>

<div className='absolute bottom-4'>
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/Transcript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { formatAudioTimestamp } from "../utils/AudioUtils";

import { NoteServiceClient } from "../generated/notes.client";
import { GrpcTransport } from "../services";
import { GrpcStatusCode } from "@protobuf-ts/grpcweb-transport";

const noteClient = new NoteServiceClient(GrpcTransport);

Expand Down Expand Up @@ -53,7 +52,7 @@ export default function Transcript({ transcribedData }: Props) {
audio: ''
});
console.log(call.status)
if (call.status.code != GrpcStatusCode.OK.toString()) {
if (call.status.code != 'OK') {
console.warn(call)
setCreateStatus(`Error "${call.status.code}": "${call.status.detail}"`)
}
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,35 @@ audio::-webkit-media-controls-panel {
width: 41rem /* 656px */;
max-width: 95vw;
}

.hamburger {
font-size: 2rem;
background: none;
border: none;
cursor: pointer;
}

.menu {
position: absolute;
top: 0;
left: 0;
width: 100%;
background: white;
transform: translateY(-100%);
transition: transform 0.3s ease-in-out;
display: flex;
flex-direction: column;
align-items: center;
padding: 1rem;
}

.menu.open {
transform: translateY(0);
}

.menu-item {
margin: 1rem 0;
font-size: 1.5rem;
text-decoration: none;
color: black;
}
20 changes: 20 additions & 0 deletions frontend/src/pages/CreateNote.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AudioManager } from "../components/AudioManager";
import Transcript from "../components/Transcript";
import { useTranscriber } from "../hooks/useTranscriber";

const CreateNote = () => {
const transcriber = useTranscriber();

return (
<div>
<h2 className='mt-3 mb-5 px-4 text-center text-1xl font-semibold tracking-tight text-slate-900 sm:text-2xl'>
Write Notes on the Go! <br />
ML-powered speech recognition directly in your browser.
</h2>
<AudioManager transcriber={transcriber} />
<Transcript transcribedData={transcriber.output} />
</div>
);
};

export default CreateNote;
57 changes: 57 additions & 0 deletions frontend/src/pages/ListNotes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useEffect, useState } from "react";
import { NoteServiceClient } from "../generated/notes.client";
import { GrpcTransport } from "../services";
import { Note } from "../generated/notes";

const noteClient = new NoteServiceClient(GrpcTransport);

const ListNotes = () => {
const [groupedNotes, setGroupedNotes] = useState<{ [date: string]: Note[] }>({});

useEffect(() => {
const fetchNotes = async () => {
try {
const call = await noteClient.listNotes({});
if (call.status.code !== 'OK') {
console.warn(call);
return;
}
const sortedNotes = call.response.notes.sort((a, b) => parseInt(b.createdAt) - parseInt(a.createdAt));
const grouped = sortedNotes.reduce((acc: {[date: string]: Note[]}, note: Note) => {
const date = new Date(parseInt(note.createdAt)).toLocaleDateString();
if (!acc[date]) {
acc[date] = [];
}
acc[date].push(note);
return acc;
}, {});
setGroupedNotes(grouped);
} catch (error) {
console.error(error);
}
};

fetchNotes();
}, []);

return (
<div className="p-4">
<h2 className="text-2xl font-bold mb-4">List of Notes</h2>
{Object.keys(groupedNotes).map(date => (
<div key={date} className="mb-6">
<h3 className="text-xl font-bold mb-2">{date}</h3>
<ul className="space-y-4">
{groupedNotes[date].map(note => (
<li key={note.id} className="p-4 border rounded shadow">
<p className="text-lg font-semibold text-gray-600">{new Date(parseInt(note.createdAt)).toLocaleTimeString()}</p>
<p>{note.transcription}</p>
</li>
))}
</ul>
</div>
))}
</div>
);
};

export default ListNotes;
8 changes: 7 additions & 1 deletion iac/services/proxy/envoy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ static_resources:
cluster: notes-grpc-backend
max_grpc_timeout: 2s
prefix_rewrite: "/"
- match:
prefix: "/"
direct_response:
status: 200
body:
inline_string: "OK"
cors:
allow_origin_string_match:
- prefix: "*"
Expand Down Expand Up @@ -65,5 +71,5 @@ admin:
access_log_path: /dev/stdout
address:
socket_address:
address: 127.0.0.1
address: 0.0.0.0
port_value: 8090
2 changes: 2 additions & 0 deletions protos/notes.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";

package notes;


service NoteService {
rpc CreateNote(CreateNoteRequest) returns (CreateNoteResponse) {};
rpc ListNotes(ListNotesRequest) returns (ListNotesResponse) {};
Expand All @@ -11,6 +12,7 @@ message Note {
string id = 1;
string audio = 2;
string transcription = 3;
int64 createdAt = 4;
}

message CreateNoteRequest {
Expand Down

0 comments on commit 63700c5

Please sign in to comment.