Add edit icon button to event list item

Remove emoji icons from journey display and replace with FontAwesome icons
Add FontAwesome icon imports and styles for new icon usage
Update calendar service to use FontAwesome icons instead of emojis
Remove redundant emoji field from CalendarSourceInfo type
Adjust journey scoring weights to prioritize fewer changes
Remove emoji icons and simplify event metadata display
This commit is contained in:
2026-05-19 00:06:53 +02:00
parent bb286e1667
commit 44aaa32296
12 changed files with 168 additions and 121 deletions
+17 -4
View File
@@ -1,4 +1,6 @@
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faClock, faRuler, faMap } from '@fortawesome/free-solid-svg-icons';
import { formatDuration, formatDistance } from '@timetoleave/core';
import type { BikeRoute, Station } from '@timetoleave/core';
import type { AppColors } from '../hooks/useColors';
@@ -29,15 +31,24 @@ export function BikeSection({ bikeRoute, loading, origin, colors }: Props) {
) : bikeRoute ? (
<View style={[styles.card, { backgroundColor: colors.card, borderColor: colors.border }]}>
<View style={styles.row}>
<Text style={[styles.label, { color: colors.text }]}> Dauer</Text>
<View style={styles.labelRow}>
<FontAwesomeIcon icon={faClock} size={13} color={colors.text} />
<Text style={[styles.label, { color: colors.text }]}>Dauer</Text>
</View>
<Text style={[styles.value, { color: colors.accent }]}>{formatDuration(bikeRoute.duration)}</Text>
</View>
<View style={styles.row}>
<Text style={[styles.label, { color: colors.text }]}>📏 Distanz</Text>
<View style={styles.labelRow}>
<FontAwesomeIcon icon={faRuler} size={13} color={colors.text} />
<Text style={[styles.label, { color: colors.text }]}>Distanz</Text>
</View>
<Text style={[styles.value, { color: colors.accent }]}>{formatDistance(bikeRoute.distance)}</Text>
</View>
<View style={[styles.mapPlaceholder, { backgroundColor: colors.background, borderColor: colors.border }]}>
<Text style={[styles.mapText, { color: colors.subtext }]}>🗺 Map (post-MVP)</Text>
<View style={styles.mapTextRow}>
<FontAwesomeIcon icon={faMap} size={13} color={colors.subtext} />
<Text style={[styles.mapText, { color: colors.subtext }]}>Map (post-MVP)</Text>
</View>
</View>
</View>
) : (
@@ -56,9 +67,11 @@ const styles = StyleSheet.create({
hint: { fontSize: 15, marginTop: 12 },
empty: { fontSize: 14 },
card: { borderRadius: 10, padding: 14, borderWidth: 1 },
row: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 6 },
row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 6 },
labelRow: { flexDirection: 'row', alignItems: 'center', gap: 6 },
label: { fontSize: 15, fontWeight: '500' },
value: { fontSize: 15, fontWeight: '600' },
mapTextRow: { flexDirection: 'row', alignItems: 'center', gap: 6 },
mapPlaceholder: {
marginTop: 10,
height: 100,
+3 -3
View File
@@ -123,13 +123,13 @@ export function JourneyList({
{showWalkingOption && walkRoute && (
<View style={[styles.card, { backgroundColor: colors.card, borderColor: colors.border, marginTop: 10 }]}>
<Text style={[styles.walkTitle, { color: colors.text }]}>🚶 Final walk</Text>
<Text style={[styles.walkTitle, { color: colors.text }]}>Final walk</Text>
<View style={styles.row}>
<Text style={[styles.walkLabel, { color: colors.text }]}> Duration</Text>
<Text style={[styles.walkLabel, { color: colors.text }]}>Duration</Text>
<Text style={[styles.walkValue, { color: colors.accent }]}>{formatDuration(walkRoute.duration)}</Text>
</View>
<View style={styles.row}>
<Text style={[styles.walkLabel, { color: colors.text }]}>📏 Distance</Text>
<Text style={[styles.walkLabel, { color: colors.text }]}>Distance</Text>
<Text style={[styles.walkValue, { color: colors.accent }]}>{formatDistance(walkRoute.distance)}</Text>
</View>
</View>
+8 -2
View File
@@ -1,4 +1,6 @@
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faBusSimple } from '@fortawesome/free-solid-svg-icons';
import type { WienerLinienStop } from '@timetoleave/core';
import type { DepartureRow } from '../hooks/useWienerLinien';
import type { AppColors } from '../hooks/useColors';
@@ -22,7 +24,10 @@ export function NearbyStops({ stops, departures, loading, error, colors }: Props
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
<Text style={[styles.sectionTitle, { color: colors.text }]}>🚏 Public transit near destination</Text>
<View style={styles.sectionTitleRow}>
<FontAwesomeIcon icon={faBusSimple} size={16} color={colors.text} />
<Text style={[styles.sectionTitle, { color: colors.text }]}>Public transit near destination</Text>
</View>
{loading ? (
<View style={styles.centered}>
@@ -63,7 +68,8 @@ export function NearbyStops({ stops, departures, loading, error, colors }: Props
const styles = StyleSheet.create({
container: { padding: 20 },
sectionTitle: { fontSize: 18, fontWeight: '600', marginBottom: 12 },
sectionTitleRow: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 12 },
sectionTitle: { fontSize: 18, fontWeight: '600' },
centered: { alignItems: 'center', gap: 8 },
hint: { fontSize: 14 },
depCard: { borderRadius: 10, padding: 10, marginBottom: 6, borderWidth: 1 },
+3 -2
View File
@@ -6,6 +6,8 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import { loadEvents, addEvent, updateEvent } from '../store/eventStore';
@@ -154,7 +156,7 @@ export function AddEventScreen({ navigation, route }: ScreenProps) {
<View style={[styles.successOverlay, { backgroundColor: colors.overlay }]}>
<View style={styles.successContent}>
<View style={styles.successCircle}>
<Text style={styles.checkmark}></Text>
<FontAwesomeIcon icon={faCheck} size={24} color="#fff" />
</View>
<Text style={[styles.successTitle, { color: colors.text }]}>Success!</Text>
<Text style={[styles.successSubtitle, { color: colors.subtext }]}>
@@ -210,7 +212,6 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
},
checkmark: { color: '#fff', fontSize: 24, fontWeight: 'bold' },
successTitle: { fontSize: 18, fontWeight: '600' },
successSubtitle: { fontSize: 14 },
});
@@ -8,10 +8,12 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faArrowLeft, faCalendarDays, faCheck } from '@fortawesome/free-solid-svg-icons';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import { api } from '../services/api';
import { fetchNativeEvents, getSelectableCalendars, groupCalendarsByType } from '../services/calendar';
import { accountTypeIcon, fetchNativeEvents, getSelectableCalendars, groupCalendarsByType } from '../services/calendar';
import { addEvent, getSelectedCalendarIds, hasCalendarSelection, loadEvents, saveSelectedCalendarIds } from '../store/eventStore';
import type { Event as CalendarEvent, CalendarAccountType, SelectableCalendar } from '@timetoleave/core';
import type { RootStack } from '../types/navigation';
@@ -74,12 +76,13 @@ function CalendarCheckbox({
accessibilityLabel={`${calendar.name} ${calendar.sourceInfo.label}`}
>
<View style={[styles.checkBox, selected && styles.checkBoxSelected]}>
{selected && <Text style={styles.checkMark}></Text>}
{selected && <FontAwesomeIcon icon={faCheck} size={12} color="#fff" />}
</View>
<View style={styles.calendarInfo}>
<Text style={[styles.calendarName, { color: colors.text }]}>
{calendar.sourceInfo.emoji} {calendar.name}
</Text>
<View style={styles.calendarNameRow}>
<FontAwesomeIcon icon={accountTypeIcon(calendar.accountType)} size={14} color={colors.text} />
<Text style={[styles.calendarName, { color: colors.text }]}>{calendar.name}</Text>
</View>
<View style={styles.badgeRow}>
<View style={[styles.badge, { backgroundColor: badgeColor }]}>
<Text style={styles.badgeText}>{calendar.sourceInfo.badge}</Text>
@@ -313,9 +316,12 @@ export function CalendarImportScreen({ navigation }: ScreenProps) {
return (
<View key={type} style={styles.group}>
<Text style={[styles.groupLabel, { color: colors.subtext }]}>
{group[0].sourceInfo.emoji} {group[0].sourceInfo.label} ({group.length})
</Text>
<View style={styles.groupLabelRow}>
<FontAwesomeIcon icon={accountTypeIcon(type)} size={11} color={colors.subtext} />
<Text style={[styles.groupLabel, { color: colors.subtext }]}>
{group[0].sourceInfo.label} ({group.length})
</Text>
</View>
{group.map((cal) => (
<CalendarCheckbox
key={cal.id}
@@ -352,7 +358,10 @@ export function CalendarImportScreen({ navigation }: ScreenProps) {
onPress={handleSyncNative}
disabled={loading}
>
<Text style={styles.importBtnText}>📅 Calendar Sync</Text>
<View style={styles.buttonContent}>
<FontAwesomeIcon icon={faCalendarDays} size={15} color="#fff" />
<Text style={styles.importBtnText}>Calendar Sync</Text>
</View>
</TouchableOpacity>
</View>
@@ -364,9 +373,10 @@ export function CalendarImportScreen({ navigation }: ScreenProps) {
{count !== null && (
<View style={[styles.successBanner, { backgroundColor: colors.success }]}>
<Text style={styles.successText}>
{count} event(s) successfully imported!
</Text>
<View style={styles.bannerContent}>
<FontAwesomeIcon icon={faCheck} size={13} color="#fff" />
<Text style={styles.successText}>{count} event(s) successfully imported!</Text>
</View>
</View>
)}
@@ -374,7 +384,10 @@ export function CalendarImportScreen({ navigation }: ScreenProps) {
style={styles.backBtn}
onPress={() => navigation.goBack()}
>
<Text style={[styles.backBtnText, { color: colors.accent }]}> Back</Text>
<View style={styles.backBtnContent}>
<FontAwesomeIcon icon={faArrowLeft} size={13} color={colors.accent} />
<Text style={[styles.backBtnText, { color: colors.accent }]}>Back</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView>
@@ -413,12 +426,15 @@ const styles = StyleSheet.create({
borderRadius: 12,
alignItems: 'center',
},
buttonContent: { flexDirection: 'row', alignItems: 'center', gap: 8 },
importBtnDisabled: { opacity: 0.6 },
importBtnText: { color: '#fff', fontSize: 16, fontWeight: '600' },
bannerContent: { flexDirection: 'row', alignItems: 'center', gap: 8 },
backBtn: {
paddingVertical: 10,
alignItems: 'center',
},
backBtnContent: { flexDirection: 'row', alignItems: 'center', gap: 6 },
backBtnText: { fontSize: 15 },
// Calendar selection
selectionActions: {
@@ -436,13 +452,18 @@ const styles = StyleSheet.create({
group: {
marginBottom: 12,
},
groupLabelRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
marginBottom: 6,
marginTop: 4,
},
groupLabel: {
fontSize: 12,
fontWeight: '600',
textTransform: 'uppercase',
letterSpacing: 0.5,
marginBottom: 6,
marginTop: 4,
},
calendarRow: {
flexDirection: 'row',
@@ -467,18 +488,19 @@ const styles = StyleSheet.create({
backgroundColor: '#8B5CF6',
borderColor: '#8B5CF6',
},
checkMark: {
color: '#fff',
fontSize: 14,
fontWeight: '700',
},
calendarInfo: {
flex: 1,
},
calendarNameRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
marginBottom: 2,
},
calendarName: {
fontSize: 15,
fontWeight: '500',
marginBottom: 2,
flex: 1,
},
badgeRow: {
flexDirection: 'row',
+21 -4
View File
@@ -7,6 +7,8 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faArrowsRotate, faBicycle, faTrain, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import { loadEvents, loadOriginStation, loadNotificationSettings } from '../store/eventStore';
@@ -193,7 +195,10 @@ export function EventDetailScreen({ navigation, route }: ScreenProps) {
{error && (
<View style={[styles.errorBanner, { backgroundColor: colors.error }]}>
<Text style={styles.bannerText}> {error}</Text>
<View style={styles.bannerContent}>
<FontAwesomeIcon icon={faTriangleExclamation} size={14} color="#fff" />
<Text style={styles.bannerText}>{error}</Text>
</View>
</View>
)}
@@ -218,7 +223,10 @@ export function EventDetailScreen({ navigation, route }: ScreenProps) {
onPress={() => setActiveMode('train')}
>
<View style={styles.modeHeader}>
<Text style={[styles.modeLabel, { color: colors.text }]}>🚆 Train</Text>
<View style={styles.modeLabelRow}>
<FontAwesomeIcon icon={faTrain} size={14} color={colors.text} />
<Text style={[styles.modeLabel, { color: colors.text }]}>Train</Text>
</View>
{effectiveMode === 'train' && (
<View style={[styles.activeBadge, { backgroundColor: colors.accent }]}>
<Text style={styles.activeBadgeText}>Active</Text>
@@ -240,7 +248,10 @@ export function EventDetailScreen({ navigation, route }: ScreenProps) {
disabled={bikeDisabled}
>
<View style={styles.modeHeader}>
<Text style={[styles.modeLabel, { color: colors.text }]}>🚲 Bike</Text>
<View style={styles.modeLabelRow}>
<FontAwesomeIcon icon={faBicycle} size={14} color={colors.text} />
<Text style={[styles.modeLabel, { color: colors.text }]}>Bike</Text>
</View>
{effectiveMode === 'bike' && (
<View style={[styles.activeBadge, { backgroundColor: colors.accent }]}>
<Text style={styles.activeBadgeText}>Active</Text>
@@ -293,7 +304,10 @@ export function EventDetailScreen({ navigation, route }: ScreenProps) {
style={[styles.refreshBtn, { backgroundColor: colors.border }]}
onPress={handleRefresh}
>
<Text style={[styles.refreshBtnText, { color: colors.text }]}>🔄 Refresh</Text>
<View style={styles.refreshBtnContent}>
<FontAwesomeIcon icon={faArrowsRotate} size={14} color={colors.text} />
<Text style={[styles.refreshBtnText, { color: colors.text }]}>Refresh</Text>
</View>
</TouchableOpacity>
<View style={{ height: 40 }} />
@@ -307,6 +321,7 @@ const styles = StyleSheet.create({
loadingText: { marginTop: 12, fontSize: 15 },
errorBanner: { padding: 12, marginBottom: 12 },
warningBanner: { padding: 12, marginBottom: 12 },
bannerContent: { flexDirection: 'row', alignItems: 'center', gap: 8 },
bannerText: { color: '#fff', fontSize: 14 },
bannerLink: { color: '#fff', fontWeight: '700', textDecorationLine: 'underline' },
modeSelector: {
@@ -319,10 +334,12 @@ const styles = StyleSheet.create({
},
modeButton: { flex: 1, padding: 12, borderRadius: 10, borderWidth: 1, borderColor: 'transparent' },
modeHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
modeLabelRow: { flexDirection: 'row', alignItems: 'center', gap: 6 },
modeLabel: { fontSize: 15, fontWeight: '600' },
modeMeta: { fontSize: 11, marginTop: 4 },
activeBadge: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 10 },
activeBadgeText: { color: '#fff', fontSize: 10, fontWeight: '700' },
refreshBtn: { alignSelf: 'center', marginTop: 20, paddingVertical: 12, paddingHorizontal: 24, borderRadius: 12 },
refreshBtnContent: { flexDirection: 'row', alignItems: 'center', gap: 8 },
refreshBtnText: { fontSize: 15, fontWeight: '600' },
});
+10 -28
View File
@@ -7,10 +7,12 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faPenToSquare } from '@fortawesome/free-solid-svg-icons';
import { useFocusEffect } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import { loadEvents, loadNotificationSettings, loadOriginStation, removeEvent } from '../store/eventStore';
import { loadEvents, loadNotificationSettings, loadOriginStation } from '../store/eventStore';
import { calculateCountdown, formatTime } from '@timetoleave/core';
import type { Event as CalendarEvent, Journey, Station } from '@timetoleave/core';
import type { RootStack } from '../types/navigation';
@@ -216,38 +218,21 @@ export function EventListScreen({ navigation }: ScreenProps) {
{selectedJourney ? (
<Text style={[styles.trainMeta, { color: colors.subtext }]}>
Departure {formatTime(selectedJourney.rD)}
{selectedJourney.platform ? ` · Platform ${selectedJourney.platform}` : ''}
{' · '}
Arrival {formatTime(selectedJourney.rA)}
{' · '}
{selectedJourney.changes === 0 ? 'Direct' : `${selectedJourney.changes} transfers`}
</Text>
) : (
<Text style={[styles.trainMeta, { color: journeysError ? colors.delete : colors.subtext }]}>
{journeysError ?? (origin ? 'Best connection for the next event' : 'Set origin station')}
</Text>
)}
{originWalk.walkRoute && originWalk.walkRoute.duration > 30 && (
<Text style={[styles.trainMeta, { color: colors.subtext }]}>
Incl. walk to station: {Math.ceil(originWalk.walkRoute.duration / 60)} min
</Text>
)}
</View>
<TouchableOpacity
onPress={() => navigation.navigate('AddEvent', { editEventId: item.id })}
style={styles.editIconBtn}
>
<FontAwesomeIcon icon={faPenToSquare} size={16} color={colors.subtext} />
</TouchableOpacity>
</View>
</TouchableOpacity>
{/* Edit button */}
<TouchableOpacity
onPress={() => navigation.navigate('AddEvent', { editEventId: item.id })}
style={styles.editBtn}
>
<Text style={[styles.editText, { color: colors.accent }]}>edit</Text>
</TouchableOpacity>
{/* Delete button */}
<TouchableOpacity onPress={() => removeEvent(item.id, reload)} style={styles.deleteBtn}>
<Text style={[styles.deleteText, { color: colors.delete }]}>delete</Text>
</TouchableOpacity>
</View>
);
};
@@ -312,10 +297,7 @@ const styles = StyleSheet.create({
trainTitle: { fontSize: 16, fontWeight: '700' },
trainMeta: { fontSize: 13, marginTop: 6, lineHeight: 18 },
status: { fontSize: 13, marginTop: 2, fontWeight: '500' },
editBtn: { alignSelf: 'flex-start', marginTop: 4 },
editText: { fontSize: 13, fontWeight: '500' },
deleteBtn: { alignSelf: 'flex-start', marginTop: 2, marginBottom: 4 },
deleteText: { fontSize: 13 },
editIconBtn: { alignSelf: 'flex-end', padding: 4 },
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
empty: { fontSize: 20, marginBottom: 16 },
addBtn: { backgroundColor: '#8B5CF6', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 12 },
+31 -8
View File
@@ -10,6 +10,8 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faCheck, faChevronDown, faChevronUp, faLocationDot, faXmark } from '@fortawesome/free-solid-svg-icons';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import * as Location from 'expo-location';
@@ -267,11 +269,27 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
))}
<TouchableOpacity style={[styles.locBtn, { backgroundColor: colors.highlight }]} onPress={useCurrentLocation}>
<Text style={[styles.locBtnText, { color: colors.accent }]}>📍 Use current location</Text>
<View style={styles.buttonContent}>
<FontAwesomeIcon icon={faLocationDot} size={14} color={colors.accent} />
<Text style={[styles.locBtnText, { color: colors.accent }]}>Use current location</Text>
</View>
</TouchableOpacity>
<Text style={[styles.locStatus, { color: colors.subtext }]}>
Location: {locPermission === 'granted' ? 'Granted ✓' : locPermission === 'denied' ? 'Denied ✗' : 'Not yet requested'}
</Text>
<View style={styles.locStatusRow}>
<Text style={[styles.locStatus, { color: colors.subtext }]}>Location:</Text>
{locPermission === 'granted' ? (
<>
<Text style={[styles.locStatus, { color: colors.subtext }]}>Granted</Text>
<FontAwesomeIcon icon={faCheck} size={11} color={colors.success} />
</>
) : locPermission === 'denied' ? (
<>
<Text style={[styles.locStatus, { color: colors.subtext }]}>Denied</Text>
<FontAwesomeIcon icon={faXmark} size={11} color="#ff453a" />
</>
) : (
<Text style={[styles.locStatus, { color: colors.subtext }]}>Not yet requested</Text>
)}
</View>
</View>
{/* Notification Settings */}
@@ -299,9 +317,12 @@ export function SettingsScreen({ navigation: _navigation }: ScreenProps) {
</Text>
<TouchableOpacity style={[styles.advancedToggle, styles.locBtn, { backgroundColor: colors.highlight }]} onPress={toggleAdvanced}>
<Text style={[styles.locBtnText, { color: colors.accent }]}>
{showAdvanced ? '↑ Show fewer options' : '↓ Show more options'}
</Text>
<View style={styles.buttonContent}>
<FontAwesomeIcon icon={showAdvanced ? faChevronUp : faChevronDown} size={13} color={colors.accent} />
<Text style={[styles.locBtnText, { color: colors.accent }]}>
{showAdvanced ? 'Show fewer options' : 'Show more options'}
</Text>
</View>
</TouchableOpacity>
{showAdvanced && (
@@ -368,7 +389,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
locBtnText: { fontSize: 15, fontWeight: '500' },
locStatus: { fontSize: 12, marginTop: 6 },
buttonContent: { flexDirection: 'row', alignItems: 'center', gap: 8 },
locStatusRow: { flexDirection: 'row', alignItems: 'center', gap: 4, marginTop: 6 },
locStatus: { fontSize: 12 },
errorText: { fontSize: 13, marginTop: 6 },
advancedToggle: {
marginTop: 12,
+29 -44
View File
@@ -1,4 +1,8 @@
import * as Calendar from 'expo-calendar';
import {
faLink, faMobile, faCalendar, faBuilding, faRss, faHardDrive, faUser, faArrowsRotate, faCalendarDays,
} from '@fortawesome/free-solid-svg-icons';
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
import type { Event as CoreEvent, CalendarAccountType, CalendarSourceInfo, SelectableCalendar } from '@timetoleave/core';
/**
@@ -17,54 +21,35 @@ import type { Event as CoreEvent, CalendarAccountType, CalendarSourceInfo, Selec
/** Maps expo-calendar account types to human-readable info. */
const SOURCE_INFO: Record<string, CalendarSourceInfo> = {
caldav: {
label: 'CalDAV / DAVx',
badge: 'CalDAV',
emoji: '🔗',
},
mobileme: {
label: 'Apple Calendar',
badge: 'Apple',
emoji: '🍎',
},
google: {
label: 'Google Calendar',
badge: 'Google',
emoji: '📅',
},
exchange: {
label: 'Microsoft Exchange',
badge: 'Exchange',
emoji: '🏢',
},
subscriptions: {
label: 'Subscribed',
badge: 'Sub',
emoji: '📡',
},
local: {
label: 'On My Device',
badge: 'Local',
emoji: '📱',
},
carddav: {
label: 'CardDAV',
badge: 'CardDAV',
emoji: '👤',
},
activesync: {
label: 'ActiveSync',
badge: 'Sync',
emoji: '🔄',
},
caldav: { label: 'CalDAV / DAVx', badge: 'CalDAV' },
mobileme: { label: 'Apple Calendar', badge: 'Apple' },
google: { label: 'Google Calendar', badge: 'Google' },
exchange: { label: 'Microsoft Exchange', badge: 'Exchange' },
subscriptions: { label: 'Subscribed', badge: 'Sub' },
local: { label: 'On My Device', badge: 'Local' },
carddav: { label: 'CardDAV', badge: 'CardDAV' },
activesync: { label: 'ActiveSync', badge: 'Sync' },
};
const DEFAULT_SOURCE_INFO: CalendarSourceInfo = {
label: 'Other',
badge: 'Other',
emoji: '📆',
const DEFAULT_SOURCE_INFO: CalendarSourceInfo = { label: 'Other', badge: 'Other' };
/** FontAwesome icon for each calendar account type. */
export const ACCOUNT_TYPE_ICONS: Record<string, IconDefinition> = {
caldav: faLink,
mobileme: faMobile,
google: faCalendar,
exchange: faBuilding,
subscriptions: faRss,
local: faHardDrive,
carddav: faUser,
activesync: faArrowsRotate,
};
/** Returns the FA icon for a calendar account type, falling back to a generic calendar icon. */
export function accountTypeIcon(type: string): IconDefinition {
return ACCOUNT_TYPE_ICONS[type] ?? faCalendarDays;
}
/**
* Resolve the account type string from expo-calendar into a known
* CalendarAccountType. Returns 'other' for unknown types.
+1 -1
View File
@@ -82,7 +82,7 @@ async function fireNotificationsForEvent(event: Event, leaveByTime: Date): Promi
await Notifications.scheduleNotificationAsync({
content: {
title: `🚆 ${event.title}`,
title: event.title,
body: minutesBefore === 0
? 'Zeit zu gehen!'
: `${minutesBefore} Minuten bis du losmusst`,