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) {