diff --git a/app/_layout.tsx b/app/_layout.tsx
index f454520..af5aaa8 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -16,6 +16,7 @@ export default function RootLayout() {
+
);
}
\ No newline at end of file
diff --git a/app/motd.tsx b/app/motd.tsx
new file mode 100644
index 0000000..ea85afc
--- /dev/null
+++ b/app/motd.tsx
@@ -0,0 +1,377 @@
+import AsyncStorage from "@react-native-async-storage/async-storage";
+import { useRouter } from "expo-router";
+import React, { useEffect, useState } from "react";
+import {
+ Alert,
+ ImageBackground,
+ KeyboardAvoidingView,
+ Platform,
+ ScrollView,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View
+} from "react-native";
+import { SafeAreaView } from "react-native-safe-area-context";
+import { styles } from "./styles";
+
+const motdBgImage = require("../assets/images/motd.png");
+
+// Define a map for Minecraft color codes to React Native colors
+const MINECRAFT_COLORS: { [key: string]: string } = {
+ "0": "#000000", // Black
+ "1": "#0000AA", // Dark Blue
+ "2": "#00AA00", // Dark Green
+ "3": "#00AAAA", // Dark Aqua
+ "4": "#AA0000", // Dark Red
+ "5": "#AA00AA", // Dark Purple
+ "6": "#FFAA00", // Gold
+ "7": "#AAAAAA", // Gray
+ "8": "#555555", // Dark Gray
+ "9": "#5555FF", // Blue
+ a: "#55FF55", // Green
+ b: "#55FFFF", // Aqua
+ c: "#FF5555", // Red
+ d: "#FF55FF", // Light Purple
+ e: "#FFFF55", // Yellow
+ f: "#FFFFFF" // White
+};
+
+export default function MotdCreator() {
+ const router = useRouter();
+ const [motdText, setMotdText] = useState("");
+ const [savedMotds, setSavedMotds] = useState([]);
+ const [previewMotdComponents, setPreviewMotdComponents] = useState<
+ React.ReactNode[]
+ >([]);
+
+ // Define a unique key for AsyncStorage
+ const STORAGE_KEY = "@minecraft_motd_list";
+
+ // Persistence Logic
+ useEffect(() => {
+ loadMotds();
+ }, []);
+
+ useEffect(() => {
+ saveMotds();
+ }, [savedMotds]);
+
+ useEffect(() => {
+ generateMotdPreview();
+ }, [motdText]);
+
+ // Async function to load MOTDs from AsyncStorage
+ const loadMotds = async () => {
+ try {
+ const jsonValue = await AsyncStorage.getItem(STORAGE_KEY);
+ if (jsonValue != null) {
+ const parsedMotds = JSON.parse(jsonValue);
+ if (Array.isArray(parsedMotds)) {
+ setSavedMotds(parsedMotds);
+ } else {
+ console.warn(
+ "Loaded data is not an array, initializing empty MOTD list."
+ );
+ setSavedMotds([]);
+ }
+ }
+ } catch (e) {
+ console.error("Failed to load MOTDs:", e);
+ Alert.alert("Error", "Failed to load your MOTD list.");
+ }
+ };
+
+ // Async function to save MOTDs to AsyncStorage
+ const saveMotds = async () => {
+ try {
+ const jsonValue = JSON.stringify(savedMotds);
+ await AsyncStorage.setItem(STORAGE_KEY, jsonValue);
+ } catch (e) {
+ console.error("Failed to save MOTDs:", e);
+ Alert.alert("Error", "Failed to save your MOTD list.");
+ }
+ };
+
+ // Function to apply Minecraft color codes
+ const applyCode = (code: string) => {
+ setMotdText((prevText) => prevText + "§" + code);
+ };
+
+ // Function to generate a preview of the MOTD with styled Text components
+ const generateMotdPreview = () => {
+ const parts: React.ReactNode[] = [];
+ let currentText = "";
+ let currentColor: string | undefined = undefined;
+ let isBold = false;
+ let isItalic = false;
+ let isUnderlined = false;
+ let isStrikethrough = false;
+ let isObfuscated = false;
+
+ const regex = /(§[0-9a-fk-or])|([^§]+)/g;
+ let match;
+
+ while ((match = regex.exec(motdText)) !== null) {
+ const code = match[1];
+ const textContent = match[2];
+
+ if (currentText.length > 0) {
+ const style: any = {
+ color: currentColor || MINECRAFT_COLORS.f
+ };
+ if (isBold) style.fontWeight = "bold";
+ if (isItalic) style.fontStyle = "italic";
+ if (isUnderlined)
+ style.textDecorationLine = "underline";
+ if (isStrikethrough)
+ style.textDecorationLine = style.textDecorationLine
+ ? style.textDecorationLine + " line-through"
+ : "line-through";
+
+ parts.push(
+
+ {currentText}
+
+ );
+ currentText = ""; // Reset current text
+ }
+
+ if (code) {
+ const codeChar = code.charAt(1);
+
+ // Apply color
+ if (MINECRAFT_COLORS[codeChar]) {
+ currentColor = MINECRAFT_COLORS[codeChar];
+ }
+ // Apply formatting
+ switch (codeChar) {
+ case "l": // Bold
+ isBold = true;
+ break;
+ case "m": // Strikethrough
+ isStrikethrough = true;
+ break;
+ case "n": // Underline
+ isUnderlined = true;
+ break;
+ case "o": // Italic
+ isItalic = true;
+ break;
+ case "k": // Obfuscated
+ isObfuscated = true; // Mark as obfuscated, but don't change text
+ break;
+ case "r": // Reset
+ currentColor = undefined;
+ isBold = false;
+ isItalic = false;
+ isUnderlined = false;
+ isStrikethrough = false;
+ isObfuscated = false;
+ break;
+ }
+ } else if (textContent) {
+ currentText += textContent;
+ }
+ }
+
+ // Add any remaining text after the last code or if no codes were found
+ if (currentText.length > 0) {
+ const style: any = {
+ color: currentColor || MINECRAFT_COLORS.f
+ };
+ if (isBold) style.fontWeight = "bold";
+ if (isItalic) style.fontStyle = "italic";
+ if (isUnderlined) style.textDecorationLine = "underline";
+ if (isStrikethrough)
+ style.textDecorationLine = style.textDecorationLine
+ ? style.textDecorationLine + " line-through"
+ : "line-through";
+
+ parts.push(
+
+ {currentText}
+
+ );
+ }
+
+ setPreviewMotdComponents(parts);
+ };
+
+ const saveMotd = () => {
+ if (motdText.trim().length > 0) {
+ setSavedMotds((prevMotds) => [...prevMotds, motdText.trim()]);
+ setMotdText(""); // Clear input after saving
+ } else {
+ Alert.alert("Empty MOTD", "Please enter some text to save.");
+ }
+ };
+
+ const deleteMotd = (index: number) => {
+ const motdToDelete = savedMotds[index];
+
+ Alert.alert(
+ "Delete MOTD",
+ `Are you sure you want to delete "${motdToDelete}"?`,
+ [
+ {
+ text: "Cancel",
+ onPress: () => console.log("Delete Cancelled"),
+ style: "cancel"
+ },
+ {
+ text: "Delete",
+ onPress: () => {
+ const updatedMotds = savedMotds.filter(
+ (_, i) => i !== index
+ );
+ setSavedMotds(updatedMotds);
+ console.log("MOTD deleted:", motdToDelete);
+ },
+ style: "destructive"
+ }
+ ],
+ { cancelable: true }
+ );
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {/* Custom Back Button */}
+ router.back()}
+ style={styles.backButton}
+ >
+
+ ← Go Back
+
+
+
+
+ MOTD Creator
+
+
+ {/* MOTD Input */}
+
+
+ {/* Color Codes */}
+ Color Codes:
+
+ {Object.entries(MINECRAFT_COLORS).map(
+ ([codeChar, colorHex]) => (
+ applyCode(codeChar)}
+ >
+
+ §{codeChar}
+
+
+ )
+ )}
+
+
+ {/* Formatting Codes */}
+
+ Formatting Codes:
+
+
+ {[
+ { code: "k", name: "Obfuscated" },
+ { code: "l", name: "Bold" },
+ { code: "m", name: "Strikethrough" },
+ { code: "n", name: "Underline" },
+ { code: "o", name: "Italic" },
+ { code: "r", name: "Reset" }
+ ].map((format) => (
+ applyCode(format.code)}
+ >
+
+ §{format.code} {format.name}
+
+
+ ))}
+
+
+ {/* MOTD Preview */}
+ MOTD Preview:
+
+
+ {previewMotdComponents}
+
+
+
+ {/* Save MOTD Button */}
+
+ Save MOTD
+
+
+ {/* Saved MOTDs */}
+ Saved MOTDs:
+ {savedMotds.length === 0 ? (
+
+ No MOTDs saved yet.
+
+ ) : (
+
+ {savedMotds.map((motd, index) => (
+
+
+ {motd}
+
+ deleteMotd(index)}
+ style={styles.deleteButton}
+ >
+
+ Delete
+
+
+
+ ))}
+
+ )}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/styles.ts b/app/styles.ts
index 7f7357d..068c052 100644
--- a/app/styles.ts
+++ b/app/styles.ts
@@ -354,5 +354,113 @@ export const styles = StyleSheet.create({
fontSize: 18,
fontWeight: "bold",
fontFamily: "Minecraft"
+ },
+
+ // MOTD
+ textInput: {
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
+ borderRadius: 10,
+ padding: 15,
+ fontSize: 16,
+ color: "#fff",
+ marginBottom: 20,
+ minHeight: 80,
+ textAlignVertical: "top",
+ borderColor: "#4CAF50",
+ borderWidth: 1
+ },
+ sectionTitle: {
+ fontSize: 20,
+ fontWeight: "bold",
+ color: "#fff",
+ marginBottom: 10,
+ marginTop: 15
+ },
+ buttonRow: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ justifyContent: "center",
+ marginBottom: 15
+ },
+ colorButton: {
+ backgroundColor: "#1e88e5",
+ paddingVertical: 8,
+ paddingHorizontal: 12,
+ borderRadius: 5,
+ margin: 5
+ },
+ formatButton: {
+ backgroundColor: "#ffb300",
+ paddingVertical: 8,
+ paddingHorizontal: 12,
+ borderRadius: 5,
+ margin: 5
+ },
+ buttonText: {
+ color: "#fff",
+ fontWeight: "bold",
+ fontSize: 14
+ },
+ previewContainer: {
+ backgroundColor: "#000",
+ padding: 15,
+ borderRadius: 10,
+ minHeight: 60,
+ justifyContent: "center",
+ marginBottom: 20,
+ borderColor: "#555",
+ borderWidth: 1,
+ },
+ previewBaseText: {
+ fontSize: 18,
+ },
+ saveButton: {
+ backgroundColor: "#4CAF50",
+ paddingVertical: 15,
+ borderRadius: 10,
+ alignItems: "center",
+ marginBottom: 20
+ },
+ saveButtonText: {
+ color: "#fff",
+ fontSize: 18,
+ fontWeight: "bold"
+ },
+ noMotdsText: {
+ color: "#ccc",
+ textAlign: "center",
+ fontStyle: "italic",
+ marginTop: 10
+ },
+ savedMotdsContainer: {
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
+ borderRadius: 10,
+ padding: 10
+ },
+ savedMotdItem: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
+ padding: 10,
+ borderRadius: 8,
+ marginBottom: 8,
+ borderWidth: 1,
+ borderColor: "#333"
+ },
+ savedMotdText: {
+ color: "#eee",
+ fontSize: 16,
+ flexShrink: 1
+ },
+ deleteButton: {
+ backgroundColor: "#e53935",
+ paddingVertical: 5,
+ paddingHorizontal: 10,
+ borderRadius: 5
+ },
+ deleteButtonText: {
+ color: "#fff",
+ fontWeight: "bold"
}
});
\ No newline at end of file
diff --git a/assets/images/motd.png b/assets/images/motd.png
new file mode 100644
index 0000000..8d47de4
Binary files /dev/null and b/assets/images/motd.png differ