diff --git a/app/Http/Controllers/SubscriptionController.php b/app/Http/Controllers/SubscriptionController.php index dda683e..ff77724 100644 --- a/app/Http/Controllers/SubscriptionController.php +++ b/app/Http/Controllers/SubscriptionController.php @@ -28,6 +28,7 @@ class SubscriptionController extends Controller public function store(Request $request) { + /* validate the submission */ $data = $request->validate([ 'source' => 'required|url', 'displayname' => 'nullable|string|max:255', @@ -35,25 +36,26 @@ class SubscriptionController extends Controller 'refreshrate' => 'nullable|string|max:10', ]); - $principalUri = $request->user()->uri; + /* normalize hard to check against this url */ $source = rtrim(trim($data['source']), '/'); - $desc = 'Remote feed: '.$source; - /* if they already subscribed to this exact feed, don’t create duplicates */ - if (Subscription::where('principaluri', $principalUri)->where('source', $source)->exists()) { + /* set the principal URI */ + $principalUri = $request->user()->uri; + + /* check for existing subscriptions */ + $already = Subscription::query() + ->where('principaluri', $principalUri) + ->where('source', $source) + ->exists(); + + if ($already) { return Redirect::route('calendar.index')->with('toast', [ 'message' => __('You are already subscribed to that calendar.'), 'type' => 'info', ]); } - $sub = DB::transaction(function () use ($request, $data, $principalUri, $source, $desc) { - - /* check if a mirror instance already exists */ - $existingInstance = CalendarInstance::where('principaluri', $principalUri) - ->where('description', $desc) - ->first(); - + $sub = DB::transaction(function () use ($data, $principalUri, $source) { /* create the calendarsubscriptions record */ $sub = Subscription::create([ @@ -66,36 +68,24 @@ class SubscriptionController extends Controller 'lastmodified' => now()->timestamp, ]); - // choose the calendar container - if ($existingInstance) { - $calId = $existingInstance->calendarid; + // create new empty calendar container + $calId = Calendar::create([ + 'synctoken' => 1, + 'components' => 'VEVENT', + ])->id; - // keep the mirror instance’s user-facing bits up to date - $existingInstance->update([ - 'displayname' => $sub->displayname, - 'calendarcolor' => $sub->calendarcolor, - 'timezone' => config('app.timezone', 'UTC'), - ]); - } else { - // create new empty calendar container - $calId = Calendar::create([ - 'synctoken' => 1, - 'components' => 'VEVENT', - ])->id; + // create mirror calendarinstance row (description can be user-editable, not relied on) + CalendarInstance::create([ + 'calendarid' => $calId, + 'principaluri' => $principalUri, + 'uri' => (string) Str::uuid(), + 'displayname' => $sub->displayname, + 'description' => 'Remote feed: '.$source, // informational only + 'calendarcolor' => $sub->calendarcolor, + 'timezone' => config('app.timezone', 'UTC'), + ]); - // create mirror calendarinstance row - CalendarInstance::create([ - 'calendarid' => $calId, - 'principaluri' => $sub->principaluri, - 'uri' => Str::uuid(), - 'displayname' => $sub->displayname, - 'description' => $desc, - 'calendarcolor' => $sub->calendarcolor, - 'timezone' => config('app.timezone', 'UTC'), - ]); - } - - // upsert our calendar_meta entry by calendar_id (since that’s your pk) + // meta entry CalendarMeta::updateOrCreate( ['calendar_id' => $calId], [ @@ -114,12 +104,10 @@ class SubscriptionController extends Controller // sync immediately so events appear without waiting for the */10 dispatcher SyncSubscription::dispatch($sub)->afterCommit(); - return redirect() - ->route('calendar.index') - ->with('toast', [ - 'message' => __('Subscription added! Syncing events now...'), - 'type' => 'success', - ]); + return Redirect::route('calendar.index')->with('toast', [ + 'message' => __('Subscription added! Syncing events now...'), + 'type' => 'success', + ]); } public function edit(Subscription $subscription) diff --git a/app/Jobs/SyncSubscription.php b/app/Jobs/SyncSubscription.php index 0a96eb7..55a493f 100644 --- a/app/Jobs/SyncSubscription.php +++ b/app/Jobs/SyncSubscription.php @@ -188,27 +188,19 @@ class SyncSubscription implements ShouldQueue return (int) $meta->calendar_id; } - $desc = $this->mirrorDescription($source); - - $existing = CalendarInstance::where('principaluri', $this->subscription->principaluri) - ->where('description', $desc) - ->first(); - - if ($existing) { - return (int) $existing->calendarid; - } - + // create a new master calendar in `calendars` $calendar = Calendar::create([ 'synctoken' => 1, 'components' => 'VEVENT', ]); + // create the per-user instance (description is display-only; never used for lookup) CalendarInstance::create([ 'calendarid' => $calendar->id, 'principaluri' => $this->subscription->principaluri, 'uri' => (string) Str::uuid(), 'displayname' => $this->subscription->displayname, - 'description' => $desc, + 'description' => $this->mirrorDescription($source), 'calendarcolor' => $meta->color ?? '#1a1a1a', 'timezone' => config('app.timezone', 'UTC'), ]); diff --git a/lang/en/calendar.php b/lang/en/calendar.php index 59d9052..7434722 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -26,7 +26,7 @@ return [ ], 'language_region' => [ 'title' => 'Language and region', - 'subtitle' => 'Choose your default language, region, and formatting preferences for calendars. These affect how dates and times are displayed throughout Kithkin.', + 'subtitle' => 'Choose your default language, region, and formatting preferences. These affect how dates and times are displayed in your calendars and events.', ], 'my_calendars' => 'Settings for my calendars', 'subscribe' => [ diff --git a/lang/en/common.php b/lang/en/common.php index 5e3945e..cb56168 100644 --- a/lang/en/common.php +++ b/lang/en/common.php @@ -17,11 +17,23 @@ return [ 'calendars' => 'Calendars', 'cancel' => 'Cancel', 'cancel_funny' => 'Get me out of here', + 'date' => 'Date', + 'date_select' => 'Select a date', + 'date_format' => 'Date format', + 'date_format_select' => 'Select a date format', 'event' => 'Event', 'events' => 'Events', + 'language' => 'Language', + 'language_select' => 'Select a language', 'password' => 'Password', + 'region' => 'Region', + 'region_select' => 'Select a region', 'save_changes' => 'Save changes', 'settings' => 'Settings', + 'time' => 'Time', + 'time_select' => 'Select a time', + 'time_format' => 'Time format', + 'time_format_select' => 'Select a time format', 'timezone' => 'Time zone', 'timezone_select' => 'Select a time zone', diff --git a/resources/css/etc/layout.css b/resources/css/etc/layout.css index 69d7d8a..be47b82 100644 --- a/resources/css/etc/layout.css +++ b/resources/css/etc/layout.css @@ -125,6 +125,23 @@ main { @apply flex items-center h-20 min-h-20 px-6 2xl:px-8; @apply backdrop-blur-xs sticky top-0 z-1 shrink-0; background-color: rgba(255, 255, 255, 0.9); + + a.app-return { + @apply flex flex-row gap-2 items-center; + + > svg { + @apply opacity-0 invisible -translate-x-1 text-white mt-1; + transition: + color 150ms ease-in, + opacity 150ms ease-in, + visibility 150ms ease-in, + translate 150ms ease-in; + } + + &:hover > svg { + @apply opacity-100 visible translate-x-0 text-secondary; + } + } } > .aside-inset { @@ -248,11 +265,11 @@ main { @keyframes title-drop { from { opacity: 0; - transform: translateY(-1rem); + transform: translateX(-0.5rem); } to { opacity: 1; - transform: translateY(0); + transform: translateX(0); } } diff --git a/resources/css/etc/theme.css b/resources/css/etc/theme.css index 8515977..8be6b5d 100644 --- a/resources/css/etc/theme.css +++ b/resources/css/etc/theme.css @@ -18,6 +18,7 @@ --color-primary-hover: #000000; --color-secondary: #555; --color-secondary-hover: #444; + --color-cyan-50: oklch(98.97% 0.015 196.79); --color-cyan-100: oklch(97.92% 0.03 196.61); --color-cyan-200: oklch(95.79% 0.063 196.12); @@ -30,6 +31,19 @@ --color-cyan-800: oklch(59.15% 0.101 194.76); --color-cyan-900: oklch(49.05% 0.084 194.76); --color-cyan-950: oklch(43.96% 0.075 194.76); + + --color-green-50: oklch(0.975 0.014 162.33); + --color-green-100: oklch(0.953 0.029 163.16); + --color-green-200: oklch(0.907 0.058 162.25); + --color-green-300: oklch(0.864 0.085 160.81); + --color-green-400: oklch(0.821 0.112 159.55); + --color-green-500: oklch(0.782 0.136 157.79); /* #61d296 as 500 */ + --color-green-600: oklch(0.734 0.161 155.16); + --color-green-700: oklch(0.624 0.137 155.13); + --color-green-800: oklch(0.507 0.108 155.72); + --color-green-900: oklch(0.382 0.078 156.05); + --color-green-950: oklch(0.319 0.063 156.43); + --color-magenta-50: oklch(96.93% 0.027 325.87); --color-magenta-100: oklch(93.76% 0.056 326.06); --color-magenta-200: oklch(87.58% 0.117 326.54); @@ -42,6 +56,7 @@ --color-magenta-800: oklch(47.69% 0.219 328.37); --color-magenta-900: oklch(40.42% 0.186 328.37); --color-magenta-950: oklch(36.79% 0.169 328.37); + --color-red-50: oklch(0.975 0.012 23.84); --color-red-100: oklch(0.951 0.024 20.79); --color-red-200: oklch(0.895 0.055 23.81); @@ -56,6 +71,8 @@ --border-width-md: 1.5px; + --outline-width-md: 1.5px; + --radius-xs: 0.25rem; --radius-sm: 0.375rem; --radius-md: 0.6667rem; diff --git a/resources/css/lib/button.css b/resources/css/lib/button.css index df6ec30..08a2f6b 100644 --- a/resources/css/lib/button.css +++ b/resources/css/lib/button.css @@ -2,6 +2,7 @@ button, .button { @apply relative inline-flex items-center cursor-pointer gap-2 rounded-md h-11 px-4 text-lg font-medium; /*transition: background-color 125ms ease-in-out; */ + @apply focus:outline-md focus:outline-offset-2 focus:outline-secondary; --button-border: var(--color-primary); --button-accent: var(--color-primary-hover); @@ -16,7 +17,11 @@ button, } &:focus { - box-shadow: none; + @apply shadow-none; + } + + &:active { + @apply shadow-none outline-none; left: 2.5px; top: 2.5px; } diff --git a/resources/css/lib/toast.css b/resources/css/lib/toast.css index a3bbfe4..de396fb 100644 --- a/resources/css/lib/toast.css +++ b/resources/css/lib/toast.css @@ -7,7 +7,7 @@ dl.toasts { @apply h-0 invisible overflow-hidden; &.success + dd { - @apply bg-green-500 text-white; + @apply bg-green-500 text-primary; } &.error + dd { diff --git a/resources/svg/icons/return.svg b/resources/svg/icons/return.svg new file mode 100644 index 0000000..4d5e65a --- /dev/null +++ b/resources/svg/icons/return.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/views/calendar/settings/index.blade.php b/resources/views/calendar/settings/index.blade.php index 460ea1d..223c763 100644 --- a/resources/views/calendar/settings/index.blade.php +++ b/resources/views/calendar/settings/index.blade.php @@ -2,7 +2,10 @@

- {{ __('common.calendar') }} + + {{ __('common.calendar') }} + +

diff --git a/resources/views/calendar/settings/language.blade.php b/resources/views/calendar/settings/language.blade.php index 37b0c3e..88796d9 100644 --- a/resources/views/calendar/settings/language.blade.php +++ b/resources/views/calendar/settings/language.blade.php @@ -9,70 +9,67 @@

-
+ @csrf -
- - - @error('language') -
{{ $message }}
- @enderror +
+
+ + + +
+
+ + + +
-
- - - @error('region') -
{{ $message }}
- @enderror +
+
+ + + +
+
+ + + +
-
- - - @error('date_format') -
{{ $message }}
- @enderror -
- -
- - - @error('time_format') -
{{ $message }}
- @enderror -
- -
- {{ __('Save') }} - {{ __('Cancel and go back') }} +
+ {{ __('common.save_changes') }} + {{ __('common.cancel') }}