diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 2d7cdc153cab8..241cb8ecd99df 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -401,7 +401,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 175; + CURRENT_PROJECT_VERSION = 176; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -543,7 +543,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 175; + CURRENT_PROJECT_VERSION = 176; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -571,7 +571,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 175; + CURRENT_PROJECT_VERSION = 176; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 1831798a4288e..14fc27b56dd61 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -58,11 +58,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.115.0 + 1.116.0 CFBundleSignature ???? CFBundleVersion - 175 + 176 FLTEnableImpeller ITSAppUsesNonExemptEncryption diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index 729b59ded5911..8bfb8c8bb9bf9 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -51,107 +51,109 @@ class CropImagePage extends HookWidget { ], ), backgroundColor: context.scaffoldBackgroundColor, - body: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Column( - children: [ - Container( - padding: const EdgeInsets.only(top: 20), - width: constraints.maxWidth * 0.9, - height: constraints.maxHeight * 0.6, - child: CropImage( - controller: cropController, - image: image, - gridColor: Colors.white, + body: SafeArea( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 20), + width: constraints.maxWidth * 0.9, + height: constraints.maxHeight * 0.6, + child: CropImage( + controller: cropController, + image: image, + gridColor: Colors.white, + ), ), - ), - Expanded( - child: Container( - width: double.infinity, - decoration: BoxDecoration( - color: context.scaffoldBackgroundColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), + Expanded( + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), ), - ), - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only( - left: 20, - right: 20, - bottom: 10, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - IconButton( - icon: Icon( - Icons.rotate_left, - color: Theme.of(context).iconTheme.color, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 20, + right: 20, + bottom: 10, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Icon( + Icons.rotate_left, + color: Theme.of(context).iconTheme.color, + ), + onPressed: () { + cropController.rotateLeft(); + }, ), - onPressed: () { - cropController.rotateLeft(); - }, - ), - IconButton( - icon: Icon( - Icons.rotate_right, - color: Theme.of(context).iconTheme.color, + IconButton( + icon: Icon( + Icons.rotate_right, + color: Theme.of(context).iconTheme.color, + ), + onPressed: () { + cropController.rotateRight(); + }, ), - onPressed: () { - cropController.rotateRight(); - }, + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: null, + label: 'Free', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 1.0, + label: '1:1', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 16.0 / 9.0, + label: '16:9', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 3.0 / 2.0, + label: '3:2', + ), + _AspectRatioButton( + cropController: cropController, + aspectRatio: aspectRatio, + ratio: 7.0 / 5.0, + label: '7:5', ), ], ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _AspectRatioButton( - cropController: cropController, - aspectRatio: aspectRatio, - ratio: null, - label: 'Free', - ), - _AspectRatioButton( - cropController: cropController, - aspectRatio: aspectRatio, - ratio: 1.0, - label: '1:1', - ), - _AspectRatioButton( - cropController: cropController, - aspectRatio: aspectRatio, - ratio: 16.0 / 9.0, - label: '16:9', - ), - _AspectRatioButton( - cropController: cropController, - aspectRatio: aspectRatio, - ratio: 3.0 / 2.0, - label: '3:2', - ), - _AspectRatioButton( - cropController: cropController, - aspectRatio: aspectRatio, - ratio: 7.0 / 5.0, - label: '7:5', - ), - ], - ), - ], + ], + ), ), ), ), - ), - ], - ); - }, + ], + ); + }, + ), ), ); } diff --git a/web/src/lib/actions/click-outside.ts b/web/src/lib/actions/click-outside.ts index bbcb0c405b718..1a421f1f5625e 100644 --- a/web/src/lib/actions/click-outside.ts +++ b/web/src/lib/actions/click-outside.ts @@ -6,6 +6,12 @@ interface Options { onEscape?: () => void; } +/** + * Calls a function when a click occurs outside of the element, or when the escape key is pressed. + * @param node + * @param options Object containing onOutclick and onEscape functions + * @returns + */ export function clickOutside(node: HTMLElement, options: Options = {}): ActionReturn { const { onOutclick, onEscape } = options; diff --git a/web/src/lib/actions/focus-outside.ts b/web/src/lib/actions/focus-outside.ts index 2266ea8f0ff83..c302e33d4ca2c 100644 --- a/web/src/lib/actions/focus-outside.ts +++ b/web/src/lib/actions/focus-outside.ts @@ -2,6 +2,11 @@ interface Options { onFocusOut?: (event: FocusEvent) => void; } +/** + * Calls a function when focus leaves the element. + * @param node + * @param options Object containing onFocusOut function + */ export function focusOutside(node: HTMLElement, options: Options = {}) { const { onFocusOut } = options; diff --git a/web/src/lib/actions/focus.ts b/web/src/lib/actions/focus.ts index 81185625f7332..3b6049f24732f 100644 --- a/web/src/lib/actions/focus.ts +++ b/web/src/lib/actions/focus.ts @@ -1,3 +1,4 @@ +/** Focus the given element when it is mounted. */ export const initInput = (element: HTMLInputElement) => { element.focus(); }; diff --git a/web/src/lib/actions/intersection-observer.ts b/web/src/lib/actions/intersection-observer.ts index 700ae0c3733b4..edbc07e5c1f09 100644 --- a/web/src/lib/actions/intersection-observer.ts +++ b/web/src/lib/actions/intersection-observer.ts @@ -13,7 +13,9 @@ type OnIntersectCallback = (entryOrElement: IntersectionObserverEntry | HTMLElem type OnSeparateCallback = (element: HTMLElement) => unknown; type IntersectionObserverActionProperties = { key?: string; + /** Function to execute when the element leaves the viewport */ onSeparate?: OnSeparateCallback; + /** Function to execute when the element enters the viewport */ onIntersect?: OnIntersectCallback; root?: Element | Document | null; @@ -112,6 +114,12 @@ function _intersectionObserver( }; } +/** + * Monitors an element's visibility in the viewport and calls functions when it enters or leaves (based on a threshold). + * @param element + * @param properties One or multiple configurations for the IntersectionObserver(s) + * @returns + */ export function intersectionObserver( element: HTMLElement, properties: IntersectionObserverActionProperties | IntersectionObserverActionProperties[], diff --git a/web/src/lib/actions/list-navigation.ts b/web/src/lib/actions/list-navigation.ts index b981f675214fe..8f8ed62ed009e 100644 --- a/web/src/lib/actions/list-navigation.ts +++ b/web/src/lib/actions/list-navigation.ts @@ -1,6 +1,11 @@ import { shortcuts } from '$lib/actions/shortcut'; import type { Action } from 'svelte/action'; +/** + * Enables keyboard navigation (up and down arrows) for a list of elements. + * @param node Element which listens for keyboard events + * @param container Element containing the list of elements + */ export const listNavigation: Action = (node, container: HTMLElement) => { const moveFocus = (direction: 'up' | 'down') => { const children = Array.from(container?.children); diff --git a/web/src/lib/actions/shortcut.ts b/web/src/lib/actions/shortcut.ts index df155ea821ad0..6348257c40496 100644 --- a/web/src/lib/actions/shortcut.ts +++ b/web/src/lib/actions/shortcut.ts @@ -10,11 +10,16 @@ export type Shortcut = { export type ShortcutOptions = { shortcut: Shortcut; + /** If true, the event handler will not execute if the event comes from an input field */ ignoreInputFields?: boolean; onShortcut: (event: KeyboardEvent & { currentTarget: T }) => unknown; preventDefault?: boolean; }; +/** Determines whether an event should be ignored. The event will be ignored if: + * - The element dispatching the event is not the same as the element which the event listener is attached to + * - The element dispatching the event is an input field + */ export const shouldIgnoreEvent = (event: KeyboardEvent | ClipboardEvent): boolean => { if (event.target === event.currentTarget) { return false; @@ -33,6 +38,7 @@ export const matchesShortcut = (event: KeyboardEvent, shortcut: Shortcut) => { ); }; +/** Bind a single keyboard shortcut to node. */ export const shortcut = ( node: T, option: ShortcutOptions, @@ -47,6 +53,7 @@ export const shortcut = ( }; }; +/** Binds multiple keyboard shortcuts to node */ export const shortcuts = ( node: T, options: ShortcutOptions[], diff --git a/web/src/lib/actions/thumbhash.ts b/web/src/lib/actions/thumbhash.ts index ab9d28ffc9b4d..e49f04dbee546 100644 --- a/web/src/lib/actions/thumbhash.ts +++ b/web/src/lib/actions/thumbhash.ts @@ -1,6 +1,11 @@ import { decodeBase64 } from '$lib/utils'; import { thumbHashToRGBA } from 'thumbhash'; +/** + * Renders a thumbnail onto a canvas from a base64 encoded hash. + * @param canvas + * @param param1 object containing the base64 encoded hash (base64Thumbhash: yourString) + */ export function thumbhash(canvas: HTMLCanvasElement, { base64ThumbHash }: { base64ThumbHash: string }) { const ctx = canvas.getContext('2d'); if (ctx) {