diff --git a/tkr/bootstrap.php b/tkr/bootstrap.php index 0f46872..b5c2aa1 100644 --- a/tkr/bootstrap.php +++ b/tkr/bootstrap.php @@ -39,6 +39,8 @@ function confirm_setup(): void { username TEXT NOT NULL, display_name TEXT NOT NULL, password_hash TEXT NOT NULL, + about TEXT NULL, + website TEXT NULL, mood TEXT NULL )"); diff --git a/tkr/lib/emoji.php b/tkr/lib/emoji.php index 7cb450f..d91d467 100644 --- a/tkr/lib/emoji.php +++ b/tkr/lib/emoji.php @@ -3,7 +3,6 @@ function get_emojis_with_labels(): array { return [ 'faces' => [ ['๐Ÿ˜€', 'grinning face'], - ['๐Ÿ˜ƒ', 'grinning face with big eyes'], ['๐Ÿ˜„', 'grinning face with smiling eyes'], ['๐Ÿ˜', 'beaming face with smiling eyes'], ['๐Ÿ˜†', 'grinning squinting face'], @@ -26,41 +25,31 @@ function get_emojis_with_labels(): array { ['๐Ÿ˜œ', 'winking face with tongue'], ['๐Ÿ˜', 'squinting face with tongue'], ['๐Ÿคช', 'zany face'], - ['๐Ÿคจ', 'face with raised eyebrow'], + ['๐Ÿฆธ', 'superhero'], + ['๐Ÿฆน', 'supervillain'], + ['๐Ÿง™', 'mage'], + ['๐Ÿง›', 'vampire'], + ['๐ŸงŸ', 'zombie'], + ['๐Ÿงž', 'genie'], ], 'gestures' => [ ['๐Ÿ‘‹', 'waving hand'], - ['๐Ÿคš', 'raised back of hand'], - ['๐Ÿ–๏ธ', 'hand with fingers splayed'], - ['โœ‹', 'raised hand'], ['๐Ÿ––', 'vulcan salute'], ['๐Ÿ‘Œ', 'OK hand'], ['๐ŸคŒ', 'pinched fingers'], - ['๐Ÿค', 'pinching hand'], ['โœŒ๏ธ', 'victory hand'], ['๐Ÿคž', 'crossed fingers'], ['๐ŸคŸ', 'love-you gesture'], ['๐Ÿค˜', 'sign of the horns'], ['๐Ÿค™', 'call me hand'], - ['๐Ÿ‘ˆ', 'backhand index pointing left'], - ['๐Ÿ‘‰', 'backhand index pointing right'], - ['๐Ÿ‘†', 'backhand index pointing up'], - ['๐Ÿ–•', 'middle finger'], - ['๐Ÿ‘‡', 'backhand index pointing down'], - ['โ˜๏ธ', 'index pointing up'], ['๐Ÿ‘', 'thumbs up'], ['๐Ÿ‘Ž', 'thumbs down'], ['โœŠ', 'raised fist'], ['๐Ÿ‘Š', 'oncoming fist'], - ['๐Ÿค›', 'left-facing fist'], - ['๐Ÿคœ', 'right-facing fist'], ], 'nature' => [ ['โ˜€๏ธ', 'sun'], - ['๐ŸŒค๏ธ', 'sun behind small cloud'], ['โ›…', 'sun behind cloud'], - ['๐ŸŒฅ๏ธ', 'sun behind large cloud'], - ['๐ŸŒฆ๏ธ', 'sun behind rain cloud'], ['๐ŸŒง๏ธ', 'cloud with rain'], ['๐ŸŒจ๏ธ', 'cloud with snow'], ['โ„๏ธ', 'snowflake'], @@ -72,14 +61,9 @@ function get_emojis_with_labels(): array { ['๐ŸŒŠ', 'water wave'], ['๐ŸŒซ๏ธ', 'fog'], ['๐ŸŒฌ๏ธ', 'wind face'], - ['๐Ÿƒ', 'leaf fluttering in wind'], ['๐Ÿ‚', 'fallen leaf'], - ['๐Ÿ', 'maple leaf'], - ['๐ŸŒพ', 'sheaf of rice'], ['๐ŸŒต', 'cactus'], ['๐ŸŒด', 'palm tree'], - ['๐ŸŒณ', 'deciduous tree'], - ['๐ŸŒฒ', 'evergreen tree'], ['๐ŸŒธ', 'cherry blossom'], ], 'animals' => [ @@ -98,45 +82,14 @@ function get_emojis_with_labels(): array { ['๐Ÿท', 'pig face'], ['๐Ÿธ', 'frog face'], ['๐Ÿต', 'monkey face'], - ['๐Ÿ™ˆ', 'see-no-evil monkey'], - ['๐Ÿ™‰', 'hear-no-evil monkey'], - ['๐Ÿ™Š', 'speak-no-evil monkey'], ['๐Ÿ”', 'chicken'], ['๐Ÿง', 'penguin'], ['๐Ÿฆ', 'bird'], - ['๐Ÿค', 'baby chick'], ['๐Ÿฃ', 'hatching chick'], ['๐Ÿบ', 'wolf face'], ['๐Ÿฆ„', 'unicorn face'], ], - 'people' => [ - ['๐Ÿง‘', 'person'], - ['๐Ÿ‘ฉ', 'woman'], - ['๐Ÿ‘จ', 'man'], - ['๐Ÿ‘ถ', 'baby'], - ['๐Ÿ‘ง', 'girl'], - ['๐Ÿ‘ฆ', 'boy'], - ['๐Ÿง’', 'child'], - ['๐Ÿ‘ต', 'older woman'], - ['๐Ÿ‘ด', 'older man'], - ['๐Ÿง“', 'older adult'], - ['๐Ÿ‘ฒ', 'person with skullcap'], - ['๐Ÿง•', 'woman with headscarf'], - ['๐Ÿ‘ณ', 'person wearing turban'], - ['๐Ÿ‘ฎ', 'police officer'], - ['๐Ÿ•ต๏ธ', 'detective'], - ['๐Ÿ‘ท', 'construction worker'], - ['๐Ÿ’‚', 'guard'], - ['๐Ÿ‘ธ', 'princess'], - ['๐Ÿคด', 'prince'], - ['๐Ÿฆธ', 'superhero'], - ['๐Ÿฆน', 'supervillain'], - ['๐Ÿง™', 'mage'], - ['๐Ÿง›', 'vampire'], - ['๐ŸงŸ', 'zombie'], - ['๐Ÿงž', 'genie'], - ], - 'emotions' => [ + 'hearts' => [ ['โค๏ธ', 'red heart'], ['๐Ÿงก', 'orange heart'], ['๐Ÿ’›', 'yellow heart'], @@ -155,49 +108,32 @@ function get_emojis_with_labels(): array { ['๐Ÿ’', 'heart with ribbon'], ['๐Ÿ’”', 'broken heart'], ['โฃ๏ธ', 'heart exclamation'], - ['๐Ÿ’Ÿ', 'heart decoration'], - ['๐Ÿ’ค', 'zzz'], - ['๐Ÿคฏ', 'exploding head'], - ['๐Ÿ˜ฑ', 'face screaming in fear'], - ['๐Ÿฅต', 'hot face'], - ['๐Ÿฅถ', 'cold face'], - ['๐Ÿคฌ', 'face with symbols on mouth'], ], 'activities' => [ ['๐Ÿšด', 'person biking'], ['๐Ÿšต', 'person mountain biking'], ['๐Ÿƒ', 'person running'], - ['๐Ÿƒโ€โ™€๏ธ', 'woman running'], ['๐Ÿ‹๏ธ', 'person lifting weights'], ['๐ŸŠ', 'person swimming'], ['๐Ÿ„', 'person surfing'], ['๐Ÿšฃ', 'person rowing boat'], - ['๐Ÿคฝ', 'person playing water polo'], - ['๐Ÿคพ', 'person playing handball'], - ['โ›น๏ธ', 'person bouncing ball'], ['๐Ÿคธ', 'person cartwheeling'], ['๐Ÿง˜', 'person in lotus position'], - ['๐Ÿ‡', 'horse racing'], ['๐Ÿง—', 'person climbing'], ['๐Ÿ•๏ธ', 'camping'], ['๐ŸŽฃ', 'fishing pole'], - ['โ›บ', 'tent'], ['๐ŸŽฟ', 'skis'], ['๐Ÿ‚', 'snowboarder'], ['๐Ÿ›น', 'skateboard'], - ['๐Ÿ›ผ', 'roller skate'], ['๐Ÿงบ', 'basket'], ['๐ŸŽฏ', 'bullseye'], - ['๐ŸŒ๏ธ', 'person golfing'], ], 'hobbies' => [ ['๐Ÿ“š', 'books'], ['๐Ÿ“–', 'open book'], ['๐ŸŽง', 'headphone'], ['๐ŸŽต', 'musical note'], - ['๐ŸŽถ', 'musical notes'], ['๐ŸŽค', 'microphone'], - ['๐ŸŽผ', 'musical score'], ['๐ŸŽท', 'saxophone'], ['๐ŸŽธ', 'guitar'], ['๐ŸŽน', 'musical keyboard'], @@ -205,71 +141,14 @@ function get_emojis_with_labels(): array { ['๐ŸŽป', 'violin'], ['๐Ÿช•', 'banjo'], ['โœ๏ธ', 'writing hand'], - ['๐Ÿ–Š๏ธ', 'pen'], ['๐Ÿ“', 'memo'], ['๐Ÿ“ท', 'camera'], - ['๐Ÿ“ธ', 'camera with flash'], ['๐ŸŽจ', 'artist palette'], ['๐Ÿงต', 'thread'], ['๐Ÿงถ', 'yarn'], ['๐Ÿชก', 'sewing needle'], ['๐Ÿ“น', 'video camera'], ['๐ŸŽฌ', 'clapper board'], - ['๐Ÿงฉ', 'puzzle piece'], - ], - 'tech' => [ - ['๐Ÿ’ป', 'laptop'], - ['๐Ÿ–ฅ๏ธ', 'desktop computer'], - ['๐Ÿ–จ๏ธ', 'printer'], - ['๐Ÿ–ฑ๏ธ', 'computer mouse'], - ['โŒจ๏ธ', 'keyboard'], - ['๐Ÿ“ฑ', 'mobile phone'], - ['๐Ÿ“ฒ', 'mobile phone with arrow'], - ['๐Ÿ“ž', 'telephone receiver'], - ['โ˜Ž๏ธ', 'telephone'], - ['๐Ÿ“Ÿ', 'pager'], - ['๐Ÿ“ ', 'fax machine'], - ['๐Ÿ”‹', 'battery'], - ['๐Ÿ”Œ', 'electric plug'], - ['๐Ÿ’ฝ', 'computer disk'], - ['๐Ÿ’พ', 'floppy disk'], - ['๐Ÿ’ฟ', 'optical disk'], - ['๐Ÿ“€', 'dvd'], - ['๐Ÿงฎ', 'abacus'], - ['๐Ÿ•น๏ธ', 'joystick'], - ['๐Ÿ“ก', 'satellite antenna'], - ['๐Ÿ”', 'magnifying glass tilted left'], - ['๐Ÿ”Ž', 'magnifying glass tilted right'], - ['๐Ÿงญ', 'compass'], - ['๐Ÿ“Š', 'bar chart'], - ['๐Ÿ“ˆ', 'chart increasing'], - ], - 'travel' => [ - ['โœˆ๏ธ', 'airplane'], - ['๐Ÿ›ซ', 'airplane departure'], - ['๐Ÿ›ฌ', 'airplane arrival'], - ['๐Ÿš—', 'automobile'], - ['๐Ÿš•', 'taxi'], - ['๐Ÿš™', 'sport utility vehicle'], - ['๐ŸšŒ', 'bus'], - ['๐ŸšŽ', 'trolleybus'], - ['๐ŸŽ๏ธ', 'racing car'], - ['๐Ÿš“', 'police car'], - ['๐Ÿš‘', 'ambulance'], - ['๐Ÿš’', 'fire engine'], - ['๐Ÿš', 'minibus'], - ['๐Ÿ›ป', 'pickup truck'], - ['๐Ÿšš', 'delivery truck'], - ['๐Ÿš›', 'articulated lorry'], - ['๐Ÿšœ', 'tractor'], - ['๐Ÿšฒ', 'bicycle'], - ['๐Ÿ›ด', 'kick scooter'], - ['๐Ÿšจ', 'police car light'], - ['โ›ต', 'sailboat'], - ['๐Ÿšค', 'speedboat'], - ['๐Ÿ›ณ๏ธ', 'passenger ship'], - ['โ›ด๏ธ', 'ferry'], - ['๐Ÿš', 'helicopter'], ], 'food' => [ ['๐ŸŽ', 'red apple'], @@ -294,10 +173,34 @@ function get_emojis_with_labels(): array { ['๐Ÿ”', 'hamburger'], ['๐ŸŸ', 'french fries'], ['๐ŸŒญ', 'hot dog'], - ['๐Ÿฅช', 'sandwich'], - ['๐ŸŒฎ', 'taco'], ['๐Ÿฃ', 'sushi'], ], + 'vibes' => [ + ['๐Ÿ’ค', 'zzz'], + ['๐Ÿคฏ', 'exploding head'], + ['๐Ÿ˜ฑ', 'face screaming in fear'], + ['๐Ÿฅต', 'hot face'], + ['๐Ÿฅถ', 'cold face'], + ['๐Ÿคฌ', 'face with symbols on mouth'], + ['๐Ÿคจ', 'face with raised eyebrow'], + ], + 'tech' => [ + ['๐Ÿ’ป', 'laptop'], + ['๐Ÿ“ž', 'telephone receiver'], + ['๐Ÿ”‹', 'battery'], + ['๐Ÿ’ฟ', 'optical disk'], + ['๐Ÿ•น๏ธ', 'joystick'], + ['๐Ÿ”', 'magnifying glass tilted left'], + ['๐Ÿ“ˆ', 'chart increasing'], + ], + 'travel' => [ + ['โœˆ๏ธ', 'airplane'], + ['๐Ÿš—', 'automobile'], + ['๐Ÿš•', 'taxi'], + ['๐Ÿšฒ', 'bicycle'], + ['๐Ÿ›ด', 'kick scooter'], + ['โ›ต', 'sailboat'], + ], //'custom' => get_user_emojis($db), ]; diff --git a/tkr/lib/mood.php b/tkr/lib/mood.php index 193b5d2..3b13b1e 100644 --- a/tkr/lib/mood.php +++ b/tkr/lib/mood.php @@ -1,34 +1,28 @@ prepare("SELECT mood FROM user WHERE username=?"); - $stmt->execute([$_SESSION['username']]); - $row = $stmt->fetch(); - - return $row['mood']; -} - function save_mood(string $mood): void { $config = Config::load(); - $db = get_db(); + $user = User::load(); + //$db = get_db(); - $stmt = $db->prepare("UPDATE user SET mood=? WHERE username=?"); - $stmt->execute([$mood, $_SESSION['username']]); + //$stmt = $db->prepare("UPDATE user SET mood=? WHERE username=?"); + //$stmt->execute([$mood, $_SESSION['username']]); + $user->mood = $mood; + $user = $user->save(); header("Location: $config->basePath"); exit; } function render_emoji_tabs(): string { + $user = User::load(); $emoji_groups = get_emojis_with_labels(); - $selected_emoji = get_mood(); + $selected_emoji = $user->mood; ob_start(); ?> diff --git a/tkr/lib/ticks.php b/tkr/lib/ticks.php index d7b8886..5e84713 100644 --- a/tkr/lib/ticks.php +++ b/tkr/lib/ticks.php @@ -1,20 +1,6 @@ "\'()]+)~i', - fn($matches) => '' . $matches[1] . '', - $safe - ); - - return $safe; -} - function save_tick(string $tick): void { // build the tick path and filename from the current time $date = new DateTime(); diff --git a/tkr/lib/user.php b/tkr/lib/user.php index cfa0379..e490dd0 100644 --- a/tkr/lib/user.php +++ b/tkr/lib/user.php @@ -9,6 +9,8 @@ class User { // properties public string $username; public string $displayName; + public string $about; + public string $website; public string $mood; // load user settings from sqlite database @@ -16,13 +18,15 @@ class User { $db = get_db(); // There's only ever one user. I'm just leaning into that. - $stmt = $db->query("SELECT username, display_name, mood FROM user WHERE id=1"); + $stmt = $db->query("SELECT username, display_name, about, website, mood FROM user WHERE id=1"); $row = $stmt->fetch(PDO::FETCH_ASSOC); $u = new self(); if ($row) { $u->username = $row['username']; $u->displayName = $row['display_name']; + $u->about = $row['about'] ?? ''; + $u->website = $row['website'] ?? ''; $u->mood = $row['mood']; } @@ -32,8 +36,8 @@ class User { public function save(): self { $db = get_db(); - $stmt = $db->prepare("UPDATE user SET username=?, display_name=?, mood=? WHERE id=1"); - $stmt->execute([$this->username, $this->displayName, $this->mood]); + $stmt = $db->prepare("UPDATE user SET username=?, display_name=?, about=?, website=?, mood=? WHERE id=1"); + $stmt->execute([$this->username, $this->displayName, $this->about, $this->website, $this->mood]); return self::load(); } diff --git a/tkr/lib/util.php b/tkr/lib/util.php new file mode 100644 index 0000000..db95f59 --- /dev/null +++ b/tkr/lib/util.php @@ -0,0 +1,15 @@ +"\'()]+)~i', + fn($matches) => '' . $matches[1] . '', + $safe + ); + + return $safe; +} diff --git a/tkr/public/admin.php b/tkr/public/admin.php index 6ad61de..1f8c011 100644 --- a/tkr/public/admin.php +++ b/tkr/public/admin.php @@ -22,6 +22,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // User profile $username = trim($_POST['username'] ?? ''); $displayName = trim($_POST['display_name'] ?? ''); + $about = trim($_POST['about'] ?? ''); + $website = trim($_POST['website'] ?? ''); + // Site settings $siteTitle = trim($_POST['site_title']) ?? ''; $siteDescription = trim($_POST['site_description']) ?? ''; @@ -40,6 +43,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!$displayName) { $errors[] = "Display name is required."; } + // Make sure the website looks like a URL and starts with a protocol + if ($website) { + if (!filter_var($website, FILTER_VALIDATE_URL)) { + $errors[] = "Please enter a valid URL (including http:// or https://)."; + } elseif (!preg_match('/^https?:\/\//i', $website)) { + $errors[] = "URL must start with http:// or https://."; + } + } + // Validate site settings if (!$siteTitle) { @@ -71,6 +83,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Update user profile $user->username = $username; $user->displayName = $displayName; + $user->about = $about; + $user->website = $website; // Save user profile and reload user from database $user = $user->save(); @@ -101,6 +115,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { User settings

+
+
Site settings diff --git a/tkr/public/css/tkr.css b/tkr/public/css/tkr.css index a5c61e3..d4bea56 100644 --- a/tkr/public/css/tkr.css +++ b/tkr/public/css/tkr.css @@ -2,6 +2,28 @@ body { font-family: sans-serif; margin: 2em; } +.flex-container { + display: flex; +} + +/* Responsive layout - makes a one column layout instead of a two-column layout */ +@media (max-width: 800px) { + .flex-container { + flex-direction: column; + } +} + +.profile { + flex-grow: 0; + flex-shrink: 0; + flex-basis: 200px; + order: 1; +} + +.ticks { + order: 2; +} + .tick { margin-bottom: 1em; } .ticktime { color: gray; font-size: 0.9em; } diff --git a/tkr/public/index.php b/tkr/public/index.php index 735a943..794779a 100644 --- a/tkr/public/index.php +++ b/tkr/public/index.php @@ -4,11 +4,14 @@ require_once __DIR__ . '/../bootstrap.php'; confirm_setup(); require_once LIB_ROOT . '/config.php'; +require_once LIB_ROOT . '/user.php'; require LIB_ROOT . '/session.php'; require LIB_ROOT . '/ticks.php'; -require LIB_ROOT . '/mood.php'; +require LIB_ROOT . '/util.php'; $config = Config::load(); +// I can get away with this before login because there's only one user. +$user = User::load(); $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; $limit = $config->itemsPerPage; @@ -20,18 +23,43 @@ $ticks = iterator_to_array(stream_ticks($limit, $offset)); <?= $config->siteTitle ?> - + + +

siteDescription ?>

+
+
+ +
+ + + + +
+ +

Hi, I'm displayName ?>

+

about ?>

+

Website: website) ?>

+

Current mood: mood ?>

+ + Set your mood

+

Admin

+

Logout username) ?>

+ +

Login

+ +
+
-
- - -
+
+ + +
- +
-
- -

Login

- -
- - - - -
-

Current mood: | Set your mood

-

Admin | Logout

diff --git a/tkr/public/save_tick.php b/tkr/public/save_tick.php index 937cfdb..70cc26f 100644 --- a/tkr/public/save_tick.php +++ b/tkr/public/save_tick.php @@ -4,6 +4,7 @@ require_once __DIR__ . '/../bootstrap.php'; require LIB_ROOT . '/config.php'; require LIB_ROOT . '/session.php'; require LIB_ROOT . '/ticks.php'; +require LIB_ROOT . '/util.php'; confirm_setup();