Compare commits

...

2 Commits

Author SHA1 Message Date
489c0271c9 refactor: rename package scope from @caldav to @calchat
Rename all workspace packages to reflect the actual project name:
- @caldav/client -> @calchat/client
- @caldav/server -> @calchat/server
- @caldav/shared -> @calchat/shared
- Root package: caldav-mono -> calchat-mono

Update all import statements across client and server to use the new
package names. Update default MongoDB database name and logging service
identifier accordingly.
2026-01-12 19:46:53 +01:00
fef30d428d feat: add EAS build configuration for local APK builds
- Add eas.json with development, preview, and production profiles
- Configure preview profile for APK builds (arm64-v8a only)
- Add build:apk npm script for local builds
- Update app.json with app name and Android package
- Add expo-build-properties dependency
- Update CLAUDE.md with build documentation
2026-01-12 19:29:52 +01:00
40 changed files with 180 additions and 3327 deletions

View File

@@ -18,18 +18,19 @@ npm run format # Format all TypeScript files with Prettier
### Client (apps/client) - Expo React Native app ### Client (apps/client) - Expo React Native app
```bash ```bash
npm run start -w @caldav/client # Start Expo dev server npm run start -w @calchat/client # Start Expo dev server
npm run android -w @caldav/client # Start on Android npm run android -w @calchat/client # Start on Android
npm run ios -w @caldav/client # Start on iOS npm run ios -w @calchat/client # Start on iOS
npm run web -w @caldav/client # Start web version npm run web -w @calchat/client # Start web version
npm run lint -w @caldav/client # Run ESLint npm run lint -w @calchat/client # Run ESLint
npm run build:apk -w @calchat/client # Build APK locally with EAS
``` ```
### Server (apps/server) - Express.js backend ### Server (apps/server) - Express.js backend
```bash ```bash
npm run dev -w @caldav/server # Start dev server with hot reload (tsx watch) npm run dev -w @calchat/server # Start dev server with hot reload (tsx watch)
npm run build -w @caldav/server # Compile TypeScript npm run build -w @calchat/server # Compile TypeScript
npm run start -w @caldav/server # Run compiled server (port 3000) npm run start -w @calchat/server # Run compiled server (port 3000)
``` ```
## Technology Stack ## Technology Stack
@@ -42,6 +43,7 @@ npm run start -w @caldav/server # Run compiled server (port 3000)
| | NativeWind | Tailwind CSS for React Native | | | NativeWind | Tailwind CSS for React Native |
| | Zustand | State management | | | Zustand | State management |
| | FlashList | High-performance lists | | | FlashList | High-performance lists |
| | EAS Build | Local APK/IPA builds |
| Backend | Express.js | Web framework | | Backend | Express.js | Web framework |
| | MongoDB | Database | | | MongoDB | Database |
| | Mongoose | ODM | | | Mongoose | ODM |
@@ -55,9 +57,9 @@ npm run start -w @caldav/server # Run compiled server (port 3000)
### Workspace Structure ### Workspace Structure
``` ```
apps/client - @caldav/client - Expo React Native app apps/client - @calchat/client - Expo React Native app
apps/server - @caldav/server - Express.js backend apps/server - @calchat/server - Express.js backend
packages/shared - @caldav/shared - Shared TypeScript types and models packages/shared - @calchat/shared - Shared TypeScript types and models
``` ```
### Frontend Architecture (apps/client) ### Frontend Architecture (apps/client)
@@ -404,6 +406,29 @@ NODE_ENV=development # development = pretty logs, production = JSON
- `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages() - loads from server on mount and persists across tab switches - `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages() - loads from server on mount and persists across tab switches
- Event Detail and Note screens exist as skeletons - Event Detail and Note screens exist as skeletons
## Building
### Local APK Build with EAS
```bash
npm run build:apk -w @calchat/client
```
This uses the `preview` profile from `eas.json` which builds an APK with:
- `arm64-v8a` architecture only (smaller APK size)
- No credentials required (`withoutCredentials: true`)
- Internal distribution
**Requirements:** Android SDK and Java must be installed locally.
**EAS Configuration:** `apps/client/eas.json` contains build profiles:
- `development`: Development client with internal distribution
- `preview`: APK build for testing (used by `build:apk`)
- `production`: Production build with auto-increment versioning
**App Identity:**
- Package name: `com.gilmour109.calchat`
- EAS Project ID: `b722dde6-7d89-48ff-9095-e007e7c7da87`
## Documentation ## Documentation
Detailed architecture diagrams are in `docs/`: Detailed architecture diagrams are in `docs/`:

View File

@@ -1,50 +1,37 @@
{ {
"expo": { "expo": {
"jsEngine": "hermes", "jsEngine": "hermes",
"name": "caldav", "name": "CalChat",
"slug": "caldav", "slug": "calchat",
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/images/icon.png", "scheme": "calchat",
"scheme": "caldav",
"userInterfaceStyle": "automatic", "userInterfaceStyle": "automatic",
"newArchEnabled": true, "newArchEnabled": true,
"ios": { "ios": {
"supportsTablet": true "supportsTablet": true
}, },
"android": { "android": {
"adaptiveIcon": { "package": "com.gilmour109.calchat",
"backgroundColor": "#E6F4FE",
"foregroundImage": "./assets/images/android-icon-foreground.png",
"backgroundImage": "./assets/images/android-icon-background.png",
"monochromeImage": "./assets/images/android-icon-monochrome.png"
},
"edgeToEdgeEnabled": true, "edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false "predictiveBackGestureEnabled": false
}, },
"web": { "web": {
"output": "static", "output": "static",
"favicon": "./assets/images/favicon.png",
"bundler": "metro" "bundler": "metro"
}, },
"plugins": [ "plugins": [
"expo-router", "expo-router"
[
"expo-splash-screen",
{
"image": "./assets/images/splash-icon.png",
"imageWidth": 200,
"resizeMode": "contain",
"backgroundColor": "#ffffff",
"dark": {
"backgroundColor": "#000000"
}
}
]
], ],
"experiments": { "experiments": {
"typedRoutes": true, "typedRoutes": true,
"reactCompiler": true "reactCompiler": true
},
"extra": {
"router": {},
"eas": {
"projectId": "b722dde6-7d89-48ff-9095-e007e7c7da87"
}
} }
} }
} }

28
apps/client/eas.json Normal file
View File

@@ -0,0 +1,28 @@
{
"cli": {
"version": ">= 16.28.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk",
"withoutCredentials": true
},
"env": {
"ORG_GRADLE_PROJECT_reactNativeArchitectures": "arm64-v8a"
}
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {}
}
}

View File

@@ -1,5 +1,5 @@
{ {
"name": "@caldav/client", "name": "@calchat/client",
"main": "expo-router/entry", "main": "expo-router/entry",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
@@ -8,16 +8,18 @@
"android": "expo start --android", "android": "expo start --android",
"ios": "expo start --ios", "ios": "expo start --ios",
"web": "expo start --web", "web": "expo start --web",
"lint": "expo lint" "lint": "expo lint",
"build:apk": "eas build --platform android --profile preview --local"
}, },
"dependencies": { "dependencies": {
"@caldav/shared": "*", "@calchat/shared": "*",
"@expo/vector-icons": "^15.0.3", "@expo/vector-icons": "^15.0.3",
"@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/bottom-tabs": "^7.4.0",
"@react-navigation/elements": "^2.6.3", "@react-navigation/elements": "^2.6.3",
"@react-navigation/native": "^7.1.8", "@react-navigation/native": "^7.1.8",
"@shopify/flash-list": "^2.0.2", "@shopify/flash-list": "^2.0.2",
"expo": "~54.0.25", "expo": "~54.0.25",
"expo-build-properties": "^1.0.10",
"expo-constants": "~18.0.10", "expo-constants": "~18.0.10",
"expo-font": "~14.0.9", "expo-font": "~14.0.9",
"expo-haptics": "~15.0.7", "expo-haptics": "~15.0.7",

View File

@@ -7,7 +7,7 @@ import {
ScrollView, ScrollView,
Alert, Alert,
} from "react-native"; } from "react-native";
import { DAYS, MONTHS, Month, ExpandedEvent } from "@caldav/shared"; import { DAYS, MONTHS, Month, ExpandedEvent } from "@calchat/shared";
import Header from "../../components/Header"; import Header from "../../components/Header";
import { EventCard } from "../../components/EventCard"; import { EventCard } from "../../components/EventCard";
import React, { import React, {

View File

@@ -18,7 +18,7 @@ import {
chatMessageToMessageData, chatMessageToMessageData,
MessageData, MessageData,
} from "../../stores"; } from "../../stores";
import { ProposedEventChange } from "@caldav/shared"; import { ProposedEventChange } from "@calchat/shared";
import { ProposedEventCard } from "../../components/ProposedEventCard"; import { ProposedEventCard } from "../../components/ProposedEventCard";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";

View File

@@ -1,5 +1,5 @@
import { View, Pressable } from "react-native"; import { View, Pressable } from "react-native";
import { ExpandedEvent } from "@caldav/shared"; import { ExpandedEvent } from "@calchat/shared";
import { Feather } from "@expo/vector-icons"; import { Feather } from "@expo/vector-icons";
import currentTheme from "../Themes"; import currentTheme from "../Themes";
import { EventCardBase } from "./EventCardBase"; import { EventCardBase } from "./EventCardBase";

View File

@@ -1,5 +1,5 @@
import { View, Text, Modal, Pressable } from "react-native"; import { View, Text, Modal, Pressable } from "react-native";
import { CreateEventDTO } from "@caldav/shared"; import { CreateEventDTO } from "@calchat/shared";
type EventConfirmDialogProps = { type EventConfirmDialogProps = {
visible: boolean; visible: boolean;

View File

@@ -1,5 +1,5 @@
import { View, Text, Pressable } from "react-native"; import { View, Text, Pressable } from "react-native";
import { ProposedEventChange } from "@caldav/shared"; import { ProposedEventChange } from "@calchat/shared";
import currentTheme from "../Themes"; import currentTheme from "../Themes";
import { EventCardBase } from "./EventCardBase"; import { EventCardBase } from "./EventCardBase";

View File

@@ -1,4 +1,4 @@
import { LoginDTO, CreateUserDTO, AuthResponse } from "@caldav/shared"; import { LoginDTO, CreateUserDTO, AuthResponse } from "@calchat/shared";
import { ApiClient } from "./ApiClient"; import { ApiClient } from "./ApiClient";
import { useAuthStore } from "../stores"; import { useAuthStore } from "../stores";

View File

@@ -7,7 +7,7 @@ import {
CreateEventDTO, CreateEventDTO,
UpdateEventDTO, UpdateEventDTO,
EventAction, EventAction,
} from "@caldav/shared"; } from "@calchat/shared";
import { ApiClient } from "./ApiClient"; import { ApiClient } from "./ApiClient";
interface ConfirmEventRequest { interface ConfirmEventRequest {

View File

@@ -3,7 +3,7 @@ import {
CreateEventDTO, CreateEventDTO,
UpdateEventDTO, UpdateEventDTO,
ExpandedEvent, ExpandedEvent,
} from "@caldav/shared"; } from "@calchat/shared";
import { ApiClient } from "./ApiClient"; import { ApiClient } from "./ApiClient";
export const EventService = { export const EventService = {

View File

@@ -1,6 +1,6 @@
import { create } from "zustand"; import { create } from "zustand";
import { Platform } from "react-native"; import { Platform } from "react-native";
import { User } from "@caldav/shared"; import { User } from "@calchat/shared";
import * as SecureStore from "expo-secure-store"; import * as SecureStore from "expo-secure-store";
const USER_STORAGE_KEY = "auth_user"; const USER_STORAGE_KEY = "auth_user";

View File

@@ -1,5 +1,5 @@
import { create } from "zustand"; import { create } from "zustand";
import { ChatMessage, ProposedEventChange } from "@caldav/shared"; import { ChatMessage, ProposedEventChange } from "@calchat/shared";
type BubbleSide = "left" | "right"; type BubbleSide = "left" | "right";

View File

@@ -1,5 +1,5 @@
import { create } from "zustand"; import { create } from "zustand";
import { ExpandedEvent } from "@caldav/shared"; import { ExpandedEvent } from "@calchat/shared";
interface EventsState { interface EventsState {
events: ExpandedEvent[]; events: ExpandedEvent[];

View File

@@ -1,5 +1,5 @@
{ {
"name": "@caldav/server", "name": "@calchat/server",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -8,7 +8,7 @@
"start": "node dist/app.js" "start": "node dist/app.js"
}, },
"dependencies": { "dependencies": {
"@caldav/shared": "*", "@calchat/shared": "*",
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"express": "^5.2.1", "express": "^5.2.1",

View File

@@ -1,5 +1,5 @@
import OpenAI from "openai"; import OpenAI from "openai";
import { ProposedEventChange } from "@caldav/shared"; import { ProposedEventChange } from "@calchat/shared";
import { AIProvider, AIContext, AIResponse } from "../services/interfaces"; import { AIProvider, AIContext, AIResponse } from "../services/interfaces";
import { import {
buildSystemPrompt, buildSystemPrompt,

View File

@@ -1,4 +1,4 @@
import { CalendarEvent } from "@caldav/shared"; import { CalendarEvent } from "@calchat/shared";
// German date/time formatting helpers // German date/time formatting helpers
export const formatDate = (d: Date) => d.toLocaleDateString("de-DE"); export const formatDate = (d: Date) => d.toLocaleDateString("de-DE");

View File

@@ -3,7 +3,7 @@ import {
getDay, getDay,
Day, Day,
DAY_TO_GERMAN, DAY_TO_GERMAN,
} from "@caldav/shared"; } from "@calchat/shared";
import { AIContext } from "../../services/interfaces"; import { AIContext } from "../../services/interfaces";
import { formatDate, formatTime, formatDateTime } from "./eventFormatter"; import { formatDate, formatTime, formatDateTime } from "./eventFormatter";

View File

@@ -20,7 +20,7 @@ import { logger } from "./logging";
const app = express(); const app = express();
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
const mongoUri = process.env.MONGODB_URI || "mongodb://localhost:27017/caldav"; const mongoUri = process.env.MONGODB_URI || "mongodb://localhost:27017/calchat";
// Middleware // Middleware
app.use(express.json()); app.use(express.json());

View File

@@ -5,7 +5,7 @@ import {
UpdateEventDTO, UpdateEventDTO,
EventAction, EventAction,
GetMessagesOptions, GetMessagesOptions,
} from "@caldav/shared"; } from "@calchat/shared";
import { ChatService } from "../services"; import { ChatService } from "../services";
import { createLogger } from "../logging"; import { createLogger } from "../logging";
import { AuthenticatedRequest } from "./AuthMiddleware"; import { AuthenticatedRequest } from "./AuthMiddleware";

View File

@@ -32,7 +32,7 @@ export const logger = pino({
} }
: undefined, : undefined,
base: { base: {
service: "caldav-server", service: "calchat-server",
}, },
}); });

View File

@@ -4,7 +4,7 @@ import {
CreateMessageDTO, CreateMessageDTO,
GetMessagesOptions, GetMessagesOptions,
UpdateMessageDTO, UpdateMessageDTO,
} from "@caldav/shared"; } from "@calchat/shared";
import { ChatRepository } from "../../services/interfaces"; import { ChatRepository } from "../../services/interfaces";
import { Logged } from "../../logging"; import { Logged } from "../../logging";
import { ChatMessageModel, ConversationModel } from "./models"; import { ChatMessageModel, ConversationModel } from "./models";

View File

@@ -1,4 +1,4 @@
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared"; import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@calchat/shared";
import { EventRepository } from "../../services/interfaces"; import { EventRepository } from "../../services/interfaces";
import { Logged } from "../../logging"; import { Logged } from "../../logging";
import { EventModel } from "./models"; import { EventModel } from "./models";

View File

@@ -1,4 +1,4 @@
import { User } from "@caldav/shared"; import { User } from "@calchat/shared";
import { UserRepository, CreateUserData } from "../../services/interfaces"; import { UserRepository, CreateUserData } from "../../services/interfaces";
import { Logged } from "../../logging"; import { Logged } from "../../logging";
import { UserModel, UserDocument } from "./models"; import { UserModel, UserDocument } from "./models";

View File

@@ -5,7 +5,7 @@ import {
CreateEventDTO, CreateEventDTO,
UpdateEventDTO, UpdateEventDTO,
ProposedEventChange, ProposedEventChange,
} from "@caldav/shared"; } from "@calchat/shared";
import { IdVirtual } from "./types"; import { IdVirtual } from "./types";
export interface ChatMessageDocument extends Omit<ChatMessage, "id">, Document { export interface ChatMessageDocument extends Omit<ChatMessage, "id">, Document {

View File

@@ -1,5 +1,5 @@
import mongoose, { Schema, Document, Model } from "mongoose"; import mongoose, { Schema, Document, Model } from "mongoose";
import { CalendarEvent } from "@caldav/shared"; import { CalendarEvent } from "@calchat/shared";
import { IdVirtual } from "./types"; import { IdVirtual } from "./types";
export interface EventDocument extends Omit<CalendarEvent, "id">, Document { export interface EventDocument extends Omit<CalendarEvent, "id">, Document {

View File

@@ -1,5 +1,5 @@
import mongoose, { Schema, Document, Model } from "mongoose"; import mongoose, { Schema, Document, Model } from "mongoose";
import { User } from "@caldav/shared"; import { User } from "@calchat/shared";
import { IdVirtual } from "./types"; import { IdVirtual } from "./types";
export interface UserDocument extends Omit<User, "id">, Document { export interface UserDocument extends Omit<User, "id">, Document {

View File

@@ -1,4 +1,4 @@
import { User, CreateUserDTO, LoginDTO, AuthResponse } from "@caldav/shared"; import { User, CreateUserDTO, LoginDTO, AuthResponse } from "@calchat/shared";
import { UserRepository } from "./interfaces"; import { UserRepository } from "./interfaces";
import * as jwt from "../utils/jwt"; import * as jwt from "../utils/jwt";
import * as password from "../utils/password"; import * as password from "../utils/password";

View File

@@ -10,7 +10,7 @@ import {
UpdateEventDTO, UpdateEventDTO,
EventAction, EventAction,
CreateMessageDTO, CreateMessageDTO,
} from "@caldav/shared"; } from "@calchat/shared";
import { ChatRepository, EventRepository, AIProvider } from "./interfaces"; import { ChatRepository, EventRepository, AIProvider } from "./interfaces";
import { getWeeksOverview, getMonthOverview } from "../utils/eventFormatters"; import { getWeeksOverview, getMonthOverview } from "../utils/eventFormatters";

View File

@@ -3,7 +3,7 @@ import {
CreateEventDTO, CreateEventDTO,
UpdateEventDTO, UpdateEventDTO,
ExpandedEvent, ExpandedEvent,
} from "@caldav/shared"; } from "@calchat/shared";
import { EventRepository } from "./interfaces"; import { EventRepository } from "./interfaces";
import { expandRecurringEvents } from "../utils/recurrenceExpander"; import { expandRecurringEvents } from "../utils/recurrenceExpander";

View File

@@ -2,7 +2,7 @@ import {
CalendarEvent, CalendarEvent,
ChatMessage, ChatMessage,
ProposedEventChange, ProposedEventChange,
} from "@caldav/shared"; } from "@calchat/shared";
export interface AIContext { export interface AIContext {
userId: string; userId: string;

View File

@@ -4,7 +4,7 @@ import {
CreateMessageDTO, CreateMessageDTO,
GetMessagesOptions, GetMessagesOptions,
UpdateMessageDTO, UpdateMessageDTO,
} from "@caldav/shared"; } from "@calchat/shared";
export interface ChatRepository { export interface ChatRepository {
// Conversations // Conversations

View File

@@ -1,4 +1,4 @@
import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared"; import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@calchat/shared";
export interface EventRepository { export interface EventRepository {
findById(id: string): Promise<CalendarEvent | null>; findById(id: string): Promise<CalendarEvent | null>;

View File

@@ -1,4 +1,4 @@
import { User } from "@caldav/shared"; import { User } from "@calchat/shared";
export interface CreateUserData { export interface CreateUserData {
email: string; email: string;

View File

@@ -5,7 +5,7 @@ import {
DAY_TO_GERMAN_SHORT, DAY_TO_GERMAN_SHORT,
MONTH_TO_GERMAN, MONTH_TO_GERMAN,
ExpandedEvent, ExpandedEvent,
} from "@caldav/shared"; } from "@calchat/shared";
import { EventRepository } from "../services/interfaces"; import { EventRepository } from "../services/interfaces";
import { expandRecurringEvents } from "./recurrenceExpander"; import { expandRecurringEvents } from "./recurrenceExpander";

View File

@@ -1,5 +1,5 @@
import { RRule, rrulestr } from "rrule"; import { RRule, rrulestr } from "rrule";
import { CalendarEvent, ExpandedEvent } from "@caldav/shared"; import { CalendarEvent, ExpandedEvent } from "@calchat/shared";
// Convert local time to "fake UTC" for rrule // Convert local time to "fake UTC" for rrule
// rrule interprets all dates as UTC internally, so we need to trick it // rrule interprets all dates as UTC internally, so we need to trick it

3317
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
"name": "caldav-mono", "name": "calchat-mono",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"workspaces": [ "workspaces": [

View File

@@ -1,5 +1,5 @@
{ {
"name": "@caldav/shared", "name": "@calchat/shared",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"main": "./src/index.ts", "main": "./src/index.ts",