Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement logging #9

Merged
merged 1 commit into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import SearchScreen, { Item as InventoryItem } from "./src/screens/feed/search";
import ItemScreen from "./src/screens/feed/item";
import { SerializedComponent } from "unfucked-ical";
import EventScreen from "./src/screens/calendar/event";
import "./src/logging";

Notifications.setNotificationHandler({
handleNotification: async () => ({
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"expo-image-picker": "~15.0.5",
"expo-notifications": "~0.28.3",
"expo-secure-store": "~13.0.1",
"expo-sharing": "^12.0.1",
"expo-status-bar": "~1.12.1",
"fast-html-parser": "^1.0.1",
"html-to-text": "^9.0.5",
Expand Down
10 changes: 8 additions & 2 deletions src/components/register/listing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { StyleSheet, TouchableOpacity } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { NativeStackNavigationProp } from "react-native-screens/native-stack";
import { StackParamList } from "../../../App";
import logging from "../../logging";

type SlotNavigationProps = NativeStackNavigationProp<StackParamList>;

Expand Down Expand Up @@ -43,11 +44,16 @@ export default function Listing() {

useEffect(() => {
async function fetchDays() {
const { slots, members } = await getSlots(
const token =
authState.authenticated === Authed.AUTHENTICATED
? await authState.token
: null,
: null;

logging.log(
"REGISTER",
`Fetching initial register data with auth state ${authState.authenticated}, token is ${token ? "defined" : "undefined"}`,
);
const { slots, members } = await getSlots(token);

setSlots(slots);
setMembers(members || []);
Expand Down
91 changes: 91 additions & 0 deletions src/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import * as FileSystem from "expo-file-system";

const LOG_DIRECTORY = `${FileSystem.documentDirectory}logs`;

class Logging {
private _log = "";
public currentFile: string | null = null;

constructor() {
this.loadLog().then((restored) => {
if (restored) this.log("LOGGING", "Log restored");
else this.log("LOGGING", "New log created");
});
}

limitSize() {
this._log = this._log.slice(-102400);
}

async ensureExists() {
const { exists } = await FileSystem.getInfoAsync(LOG_DIRECTORY);
if (exists) return;

await FileSystem.makeDirectoryAsync(LOG_DIRECTORY);
}

async loadLog() {
await this.ensureExists();

const logFiles = await FileSystem.readDirectoryAsync(LOG_DIRECTORY);
const [log] = logFiles
.filter((file) => file.endsWith(".txt"))
.map((file) => ({
date: new Date(file.split(".").at(-1) || new Date()).getTime(),
file,
}))
.sort((a, b) => a.date - b.date);

if (!log) return false;

const contents = await FileSystem.readAsStringAsync(
`${LOG_DIRECTORY}/${log.file}`,
);
this.currentFile = `${LOG_DIRECTORY}/${log.file}`;
this._log = contents + this._log;
this.limitSize();

return true;
}

async saveLog() {
await this.ensureExists();

const name = `log.${new Date().getTime()}.txt`;
const temp = `${LOG_DIRECTORY}/${name}`;

await FileSystem.writeAsStringAsync(temp, this.export());

this.currentFile = `${LOG_DIRECTORY}/${name}`;
const logs = await FileSystem.readDirectoryAsync(LOG_DIRECTORY);
await Promise.all(
logs.map(async (log) => {
if (log === name) return;

try {
await FileSystem.deleteAsync(`${LOG_DIRECTORY}/${log}`);
} catch {}
}),
);
}

async clear() {
this._log = "";
this.saveLog().then();
this.log("LOGGING", "Log cleared");
}

export() {
return this._log;
}

log(category: string, message: string) {
console.log(`[${new Date().toISOString()}] (${category}) ${message}`);
this._log += `[${new Date().toISOString()}] (${category}) ${message}\n`;
this.limitSize();

this.saveLog().then();
}
}

export default new Logging();
6 changes: 6 additions & 0 deletions src/screens/feed/feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import AuthContext, { Authed } from "../../auth";
import { NativeStackNavigationProp } from "react-native-screens/native-stack";
import { StackParamList } from "../../../App";
import { useNavigation } from "@react-navigation/native";
import logging from "../../logging";

type NavigationProps = NativeStackNavigationProp<StackParamList, "Search">;

Expand All @@ -33,6 +34,11 @@ export default function FeedScreen() {
authState.authenticated === Authed.AUTHENTICATED
? await authState.token
: null;
logging.log(
"FEED",
`Refreshing feed with auth state ${authState.authenticated}, token is ${token ? "defined" : "undefined"}`,
);

await Promise.all([
new Promise<void>(async (resolve) => {
setSlots(null);
Expand Down
37 changes: 36 additions & 1 deletion src/screens/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ScrollView, StyleSheet, View } from "react-native";
import { Appbar, Button, Card, Text } from "react-native-paper";
import * as WebBrowser from "expo-web-browser";
import auth from "../auth";
import logging from "../logging";
import * as Sharing from "expo-sharing";

export default function SettingsScreen() {
const authState = useContext(AuthContext);
Expand All @@ -14,6 +16,16 @@ export default function SettingsScreen() {
);
}

async function exportLog() {
if (!logging.currentFile) return;

await Sharing.shareAsync(logging.currentFile);
}

async function clearLog() {
await logging.clear();
}

return (
<>
<Appbar.Header>
Expand All @@ -27,6 +39,22 @@ export default function SettingsScreen() {
<Button mode={"contained"} onPress={orderList}>
Bestellijst
</Button>
<View style={styles.related}>
<Button
mode={"contained-tonal"}
onPress={clearLog}
style={styles.relatedButton}
>
Leeg log
</Button>
<Button
mode={"contained"}
onPress={exportLog}
style={styles.relatedButton}
>
Exporteer log
</Button>
</View>
</Card.Content>
</Card>
<Card>
Expand Down Expand Up @@ -63,8 +91,15 @@ const styles = StyleSheet.create({
gap: 10,
},
content: {
gap: 5,
gap: 10,
padding: 17,
paddingTop: 0,
},
related: {
flexDirection: "row",
gap: 10,
},
relatedButton: {
flex: 1,
},
});
3 changes: 3 additions & 0 deletions src/stores/register.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { atom } from "jotai";
import { nextFriday, nextSaturday } from "date-fns";
import { AANMELDEN } from "../env";
import logging from "../logging";

export interface Presence {
id: number;
Expand Down Expand Up @@ -58,13 +59,15 @@ export const slotsAtom = atom<Slot[] | null>([]);
export const membersAtom = atom<Member[]>([]);
export async function getSlots(token: string | null) {
if (!token) {
logging.log("REGISTER", "No valid token, reverting to demo");
await new Promise((resolve) =>
setTimeout(resolve, Math.random() * 500 + 250),
);

return { slots: demoSlots };
}

logging.log("REGISTER", "Valid token, fetching slots");
const { slots, members }: { slots: Slot[]; members?: Member[] } = await fetch(
`${AANMELDEN}/api/v1/slots`,
{
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3261,6 +3261,11 @@ expo-secure-store@~13.0.1:
resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-13.0.1.tgz#fc886d3e0ed12890c8dd74e7cc44e35b03f2d79b"
integrity sha512-5DTKjbv98X7yPbm+1jER/sOEIlt2Ih7qwabTvkWDXry5bPcQGoulxH5zIX9+JvVH7of8GI4t7NSEbpAO3P7FZA==

expo-sharing@^12.0.1:
version "12.0.1"
resolved "https://registry.yarnpkg.com/expo-sharing/-/expo-sharing-12.0.1.tgz#6c4d951beda47dac47112e679d60fc06c233b7aa"
integrity sha512-wBT+WeXwapj/9NWuLJO01vi9bdlchYu/Q/xD8slL/Ls4vVYku8CPqzkTtDFcjLrjtlJqyeHsdQXwKLvORmBIew==

expo-status-bar@~1.12.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.12.1.tgz#52ce594aab5064a0511d14375364d718ab78aa66"
Expand Down
Loading