Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ecfb08fb | ||
|
|
099bbebe38 | ||
|
|
a34b6c5114 | ||
|
|
7353b04dd1 | ||
|
|
a2c1a82ebb | ||
|
|
ccfa68c530 | ||
|
|
6885ae6216 |
160
CHANGELOG.md
Normal file
160
CHANGELOG.md
Normal file
@@ -0,0 +1,160 @@
|
||||
## [2.0.3](https://github.com/leinelissen/jellyfin-audio-player/compare/v2.0.2...v2.0.3) (2023-02-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve album list scrolling performance ([099bbeb](https://github.com/leinelissen/jellyfin-audio-player/commit/099bbebe38942f2c72782e6c34ad3cea0876b291))
|
||||
* prevent track indexes from overflowing ([a34b6c5](https://github.com/leinelissen/jellyfin-audio-player/commit/a34b6c51141cb3cd6058733ccb3323d75f40bbd5))
|
||||
|
||||
|
||||
|
||||
## [2.0.2](https://github.com/leinelissen/jellyfin-audio-player/compare/v2.0.1...v2.0.2) (2023-01-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow user-supplied CA certificates on Android ([ccfa68c](https://github.com/leinelissen/jellyfin-audio-player/commit/ccfa68c53045dfc1a7071d282da477a3ec6c9f60)), closes [#110](https://github.com/leinelissen/jellyfin-audio-player/issues/110)
|
||||
* font colour for dark mode on input ([6885ae6](https://github.com/leinelissen/jellyfin-audio-player/commit/6885ae6216119155e86146c39ca502fa8a18183f))
|
||||
|
||||
|
||||
|
||||
## [2.0.1](https://github.com/leinelissen/jellyfin-audio-player/compare/v2.0.0...v2.0.1) (2022-11-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* android and ios builds ([845b379](https://github.com/leinelissen/jellyfin-audio-player/commit/845b379e0983f012a2eda65350748307d4b74dca))
|
||||
* Blur obscuring buttons on Android ([e0493c4](https://github.com/leinelissen/jellyfin-audio-player/commit/e0493c4a55157abff8fbb1eddeab331ac856feff))
|
||||
* BlurView on Android ([b2bd211](https://github.com/leinelissen/jellyfin-audio-player/commit/b2bd211758f13a789294b98b5a129b07519ec3f8))
|
||||
* Depcreated createReducer calls ([d072292](https://github.com/leinelissen/jellyfin-audio-player/commit/d072292008929aa53738bf69e91eb6925686687a))
|
||||
* Ensure proper spacing in downloads screen ([cd10ddd](https://github.com/leinelissen/jellyfin-audio-player/commit/cd10ddd260c0a8d2b967248fe6dc0aeb09983e32))
|
||||
* Input icon alignment on Android ([0ffc5b6](https://github.com/leinelissen/jellyfin-audio-player/commit/0ffc5b64894099d761451483fa7cd35e76446054))
|
||||
* jumpy progress animations ([9807b0e](https://github.com/leinelissen/jellyfin-audio-player/commit/9807b0e920379ea646f6940d814cd2ed239a2054))
|
||||
* margin on connection notice ([68de2ca](https://github.com/leinelissen/jellyfin-audio-player/commit/68de2ca80e3ba55489a34d9464af4f891093ffe6))
|
||||
* Only show single line for tracks without artists or albums ([7ed389e](https://github.com/leinelissen/jellyfin-audio-player/commit/7ed389ead647c299be229b15fab47a8cc97be8c7))
|
||||
* Remove any restrictions on bitrate and samplerate ([b41031e](https://github.com/leinelissen/jellyfin-audio-player/commit/b41031eeac9b5a9976b10a93d620bfd108c8d97c))
|
||||
* Rename Jellyfin Audio Player to Fintunes in translation files ([0a7f6ab](https://github.com/leinelissen/jellyfin-audio-player/commit/0a7f6abf3e6af6f5684b63b0005868f250e687a2))
|
||||
* screenshotting logic ([d4570b6](https://github.com/leinelissen/jellyfin-audio-player/commit/d4570b60aecdeae4ce8dedb63c511f359e9760cb))
|
||||
* switch album id to demo instance ([9a1defb](https://github.com/leinelissen/jellyfin-audio-player/commit/9a1defbeef61a79addec4f71e0363e0b0271a111))
|
||||
* use entire input box as touch area for focus ([87f992d](https://github.com/leinelissen/jellyfin-audio-player/commit/87f992d912f0846773a85d67b6f67a90fe1ac293))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Save App metadata in the repo ([9c8e474](https://github.com/leinelissen/jellyfin-audio-player/commit/9c8e474d51402f5e6fa24ab683cc86aa3e131552))
|
||||
|
||||
|
||||
|
||||
## [1.2.7](https://github.com/leinelissen/jellyfin-audio-player/compare/v1.2.6...v1.2.7) (2022-08-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Allow FLAC playback ([5b54760](https://github.com/leinelissen/jellyfin-audio-player/commit/5b54760e4ee6620062ce0cc4c79daf81753f00ae))
|
||||
|
||||
|
||||
|
||||
## [1.2.6](https://github.com/leinelissen/jellyfin-audio-player/compare/v1.2.6-beta.1...v1.2.6) (2022-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Peer dependency chain ([63bbbf2](https://github.com/leinelissen/jellyfin-audio-player/commit/63bbbf2719aa5d296a6ec99774f9bf1a1aa068d0))
|
||||
* Remove unused imports ([c7f0d46](https://github.com/leinelissen/jellyfin-audio-player/commit/c7f0d46b410825765ab5d074469ec23d32ffd45d))
|
||||
|
||||
|
||||
|
||||
## [1.2.6-beta.1](https://github.com/leinelissen/jellyfin-audio-player/compare/v1.2.5...v1.2.6-beta.1) (2022-06-09)
|
||||
|
||||
|
||||
|
||||
## [1.2.5](https://github.com/leinelissen/jellyfin-audio-player/compare/v1.2.4...v1.2.5) (2022-05-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Only pull Exoplayer from jcenter ([89d2984](https://github.com/leinelissen/jellyfin-audio-player/commit/89d29844b9821e1a42b3b60c43dc4c3078231d56))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Apply default text styles to ReText ([37ead0e](https://github.com/leinelissen/jellyfin-audio-player/commit/37ead0ec989a8b714fde1bcb6dd36e568c6e7e8c))
|
||||
* Create new progress slider from scratch ([6efc8e7](https://github.com/leinelissen/jellyfin-audio-player/commit/6efc8e757c10c66019914f7561d075c3ecaf2f69))
|
||||
* Implement colored blur backgrounds ([f48d248](https://github.com/leinelissen/jellyfin-audio-player/commit/f48d2481443850888a0bd1a1cf2604420e633b26))
|
||||
* Tweak progress bar gestures ([b0961d3](https://github.com/leinelissen/jellyfin-audio-player/commit/b0961d3263d5f4ef3978fde748a6a277059cb0cb))
|
||||
|
||||
|
||||
|
||||
## [1.2.4](https://github.com/leinelissen/jellyfin-audio-player/compare/v1.2.3...v1.2.4) (2022-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* No interaction on Android webview ([#59](https://github.com/leinelissen/jellyfin-audio-player/issues/59)) ([91eaa1d](https://github.com/leinelissen/jellyfin-audio-player/commit/91eaa1d864f66e1a6597809bd46c17907acc99ee))
|
||||
|
||||
|
||||
|
||||
## [1.2.3](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.2.3...v1.2.3) (2022-01-16)
|
||||
|
||||
|
||||
|
||||
## [0.2.3](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.2.2...v0.2.3) (2022-01-15)
|
||||
|
||||
|
||||
|
||||
## [0.2.2](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.2.1...v0.2.2) (2022-01-03)
|
||||
|
||||
|
||||
|
||||
## [0.2.1](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.2.0...v0.2.1) (2022-01-02)
|
||||
|
||||
|
||||
|
||||
# [0.2.0](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.7...v0.2.0) (2022-01-02)
|
||||
|
||||
|
||||
|
||||
## [0.1.7](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.6...v0.1.7) (2021-10-25)
|
||||
|
||||
|
||||
|
||||
## [0.1.6](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.5...v0.1.6) (2021-04-25)
|
||||
|
||||
|
||||
|
||||
## [0.1.5](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.4...v0.1.5) (2021-04-24)
|
||||
|
||||
|
||||
|
||||
## [0.1.4](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.3...v0.1.4) (2021-04-03)
|
||||
|
||||
|
||||
|
||||
## [0.1.3](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.2...v0.1.3) (2021-03-21)
|
||||
|
||||
|
||||
|
||||
## [0.1.2](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.1...v0.1.2) (2021-03-09)
|
||||
|
||||
|
||||
|
||||
## [0.1.1](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.1.0...v0.1.1) (2021-02-13)
|
||||
|
||||
|
||||
|
||||
# [0.1.0](https://github.com/leinelissen/jellyfin-audio-player/compare/v1.0.0-beta3...v0.1.0) (2021-02-07)
|
||||
|
||||
|
||||
|
||||
# [1.0.0-beta3](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.0.1-alpha1...v1.0.0-beta3) (2020-08-25)
|
||||
|
||||
|
||||
|
||||
## [0.0.1-alpha1](https://github.com/leinelissen/jellyfin-audio-player/compare/v0.0.1-alpha0...v0.0.1-alpha1) (2020-07-26)
|
||||
|
||||
|
||||
|
||||
## 0.0.1-alpha0 (2020-07-10)
|
||||
|
||||
|
||||
|
||||
@@ -138,8 +138,8 @@ android {
|
||||
applicationId "nl.moeilijkedingen.jellyfinaudioplayer"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 13
|
||||
versionName "2.0.1"
|
||||
versionCode 15
|
||||
versionName "2.0.3"
|
||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/react_native_config"
|
||||
tools:targetApi="28"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity
|
||||
|
||||
9
android/app/src/debug/res/xml/react_native_config.xml
Normal file
9
android/app/src/debug/res/xml/react_native_config.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="user"/>
|
||||
<certificates src="system"/>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
@@ -9,7 +9,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/react_native_config"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
9
android/app/src/main/res/xml/react_native_config.xml
Normal file
9
android/app/src/main/res/xml/react_native_config.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="user"/>
|
||||
<certificates src="system"/>
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
@@ -10,7 +10,7 @@ platform :ios do
|
||||
get_provisioning_profile(
|
||||
output_path: 'certificates/',
|
||||
filename: "provisioning.mobileprovision",
|
||||
fail_on_name_taken: true,
|
||||
fail_on_name_taken: false,
|
||||
)
|
||||
update_code_signing_settings(
|
||||
use_automatic_signing: true,
|
||||
@@ -42,7 +42,7 @@ platform :ios do
|
||||
workspace: "ios/Fintunes.xcworkspace",
|
||||
export_method: "app-store",
|
||||
)
|
||||
upload_to_testflight
|
||||
upload_to_testflight()
|
||||
end
|
||||
|
||||
after_all do
|
||||
|
||||
@@ -606,7 +606,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 49;
|
||||
CURRENT_PROJECT_VERSION = 53;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -643,7 +643,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 49;
|
||||
CURRENT_PROJECT_VERSION = 53;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
INFOPLIST_FILE = Fintunes/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -799,7 +799,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 49;
|
||||
CURRENT_PROJECT_VERSION = 53;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
@@ -832,7 +832,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 49;
|
||||
CURRENT_PROJECT_VERSION = 53;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 238P3C58WC;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.1</string>
|
||||
<string>2.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>49</string>
|
||||
<string>53</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "fintunes",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "fintunes",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"dependencies": {
|
||||
"@react-native-async-storage/async-storage": "^1.17.11",
|
||||
"@react-native-community/blur": "^4.3.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fintunes",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"main": "src/index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -35,7 +35,12 @@ function Input({ icon = null, style, testID, ...rest }: InputProps) {
|
||||
<Gap size={8} />
|
||||
</>
|
||||
)}
|
||||
<TextInput {...rest} style={{ margin: 0, padding: 0 }} ref={inputRef} testID={`${testID}-textinput`} />
|
||||
<TextInput
|
||||
{...rest}
|
||||
style={[defaultStyles.text, { margin: 0, padding: 0 }]}
|
||||
ref={inputRef}
|
||||
testID={`${testID}-textinput`}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useRef, ReactText } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, ReactText, useMemo } from 'react';
|
||||
import { useGetImage } from 'utility/JellyfinApi';
|
||||
import { MusicNavigationProp } from '../types';
|
||||
import { SafeAreaView, SectionList, View } from 'react-native';
|
||||
@@ -20,22 +20,6 @@ import { ShadowWrapper } from 'components/Shadow';
|
||||
|
||||
const HeadingHeight = 50;
|
||||
|
||||
interface VirtualizedItemInfo {
|
||||
section: SectionedId,
|
||||
// Key of the section or combined key for section + item
|
||||
key: string,
|
||||
// Relative index within the section
|
||||
index: number,
|
||||
// True if this is the section header
|
||||
header?: boolean,
|
||||
leadingItem?: EntityId,
|
||||
leadingSection?: SectionedId,
|
||||
trailingItem?: EntityId,
|
||||
trailingSection?: SectionedId,
|
||||
}
|
||||
|
||||
type VirtualizedSectionList = { _subExtractor: (index: number) => VirtualizedItemInfo };
|
||||
|
||||
function generateSection({ section }: { section: SectionedId }) {
|
||||
return (
|
||||
<SectionHeading label={section.label} key={section.label} />
|
||||
@@ -105,42 +89,52 @@ const Albums: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigation = useNavigation<MusicNavigationProp>();
|
||||
const getImage = useGetImage();
|
||||
const listRef = useRef<SectionList<EntityId>>(null);
|
||||
const listRef = useRef<SectionList<EntityId[]>>(null);
|
||||
|
||||
const getItemLayout = useCallback((data: SectionedId[] | null, index: number): { offset: number, length: number, index: number } => {
|
||||
// We must wait for the ref to become available before we can use the
|
||||
// native item retriever in VirtualizedSectionList
|
||||
if (!listRef.current) {
|
||||
return { offset: 0, length: 0, index };
|
||||
}
|
||||
// Create an array that computes all the height data for the entire list in
|
||||
// advance. We can then use this pre-computed data to respond to
|
||||
// `getItemLayout` calls, without having to compute things in place (and
|
||||
// fail horribly).
|
||||
// This approach was inspired by https://gist.github.com/RaphBlanchet/472ed013e05398c083caae6216b598b5
|
||||
const itemLayouts = useMemo(() => {
|
||||
// Create an array in which we will store all possible outputs for
|
||||
// `getItemLayout`. We will loop through each potential album and add
|
||||
// items that will be in the list
|
||||
const layouts: Array<{ length: number; offset: number; index: number }> = [];
|
||||
|
||||
// Keep track of both the index of items and the offset (in pixels) from
|
||||
// the top
|
||||
let index = 0;
|
||||
let offset = 0;
|
||||
|
||||
// Retrieve the right item info
|
||||
// @ts-ignore
|
||||
const wrapperListRef = (listRef.current?._wrapperListRef) as VirtualizedSectionList;
|
||||
const info: VirtualizedItemInfo = wrapperListRef._subExtractor(index);
|
||||
const { index: itemIndex, header, key } = info;
|
||||
const sectionIndex = parseInt(key.split(':')[0]);
|
||||
// Loop through each individual section (i.e. alphabet letter) and add
|
||||
// all items in that particular section.
|
||||
sections.forEach((section) => {
|
||||
// Each section starts with a header, so we'll need to add the item,
|
||||
// as well as the offset.
|
||||
layouts[index] = ({ length: HeadingHeight, offset, index });
|
||||
index++;
|
||||
offset += HeadingHeight;
|
||||
|
||||
// We can then determine the "length" (=height) of this item. Header items
|
||||
// end up with an itemIndex of -1, thus are easy to identify.
|
||||
const length = header ? 50 : (itemIndex % 2 === 0 ? AlbumHeight : 0);
|
||||
|
||||
// We'll also need to account for any unevenly-ended lists up until the
|
||||
// current item.
|
||||
const previousRows = data?.filter((row, i) => i < sectionIndex)
|
||||
.reduce((sum, row) => sum + Math.ceil(row.data.length / 2), 0) || 0;
|
||||
// Then, loop through all the rows (sets of two albums) and add
|
||||
// items for those as well.
|
||||
section.data.forEach(() => {
|
||||
layouts[index] = ({ length: AlbumHeight, offset, index });
|
||||
index++;
|
||||
offset += AlbumHeight;
|
||||
});
|
||||
|
||||
// We must also calcuate the offset, total distance from the top of the
|
||||
// screen. First off, we'll account for each sectionIndex that is shown up
|
||||
// until now. This only includes the heading for the current section if the
|
||||
// item is not the section header
|
||||
const headingOffset = HeadingHeight * (header ? sectionIndex : sectionIndex + 1);
|
||||
const currentRows = itemIndex > 1 ? Math.ceil((itemIndex + 1) / 2) : 0;
|
||||
const itemOffset = AlbumHeight * (previousRows + currentRows);
|
||||
const offset = headingOffset + itemOffset;
|
||||
|
||||
return { index, length, offset };
|
||||
}, [listRef]);
|
||||
// The way SectionList works is that you get an item for a
|
||||
// SectionHeader and a SectionFooter, no matter if you've specified
|
||||
// whether you want them or not. Thus, we will need to add an empty
|
||||
// footer as an item, so that we don't mismatch our indexes
|
||||
layouts[index] = { length: 0, offset, index };
|
||||
index++;
|
||||
});
|
||||
|
||||
// Then, store and memoize the output
|
||||
return layouts;
|
||||
}, [sections]);
|
||||
|
||||
// Set callbacks
|
||||
const retrieveData = useCallback(() => dispatch(fetchAllAlbums()), [dispatch]);
|
||||
@@ -148,30 +142,19 @@ const Albums: React.FC = () => {
|
||||
const selectLetter = useCallback((sectionIndex: number) => {
|
||||
listRef.current?.scrollToLocation({ sectionIndex, itemIndex: 0, animated: false, });
|
||||
}, [listRef]);
|
||||
const generateItem = useCallback(({ item, index, section }: { item: EntityId, index: number, section: SectionedId }) => {
|
||||
if (index % 2 === 1) {
|
||||
return <View key={item} />;
|
||||
}
|
||||
const nextItem = section.data[index + 1];
|
||||
|
||||
const generateItem = useCallback(({ item }: { item: EntityId[] }) => {
|
||||
return (
|
||||
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item}>
|
||||
<GeneratedAlbumItem
|
||||
id={item}
|
||||
imageUrl={getImage(item as string)}
|
||||
name={albums[item]?.Name || ''}
|
||||
artist={albums[item]?.AlbumArtist || ''}
|
||||
onPress={selectAlbum}
|
||||
/>
|
||||
{albums[nextItem] &&
|
||||
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
|
||||
{item.map((id) => (
|
||||
<GeneratedAlbumItem
|
||||
id={nextItem}
|
||||
imageUrl={getImage(nextItem as string)}
|
||||
name={albums[nextItem]?.Name || ''}
|
||||
artist={albums[nextItem]?.AlbumArtist || ''}
|
||||
key={id}
|
||||
id={id}
|
||||
imageUrl={getImage(id as string)}
|
||||
name={albums[id]?.Name || ''}
|
||||
artist={albums[id]?.AlbumArtist || ''}
|
||||
onPress={selectAlbum}
|
||||
/>
|
||||
}
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}, [albums, getImage, selectAlbum]);
|
||||
@@ -191,9 +174,9 @@ const Albums: React.FC = () => {
|
||||
sections={sections}
|
||||
refreshing={isLoading}
|
||||
onRefresh={retrieveData}
|
||||
getItemLayout={getItemLayout}
|
||||
getItemLayout={(_, i) => itemLayouts[i] ?? { length: 0, offset: 0, index: i }}
|
||||
ref={listRef}
|
||||
keyExtractor={(item) => item as string}
|
||||
keyExtractor={(item) => item.join('-')}
|
||||
renderSectionHeader={generateSection}
|
||||
renderItem={generateItem}
|
||||
/>
|
||||
|
||||
@@ -28,8 +28,7 @@ import ticksToDuration from 'utility/ticksToDuration';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
index: {
|
||||
width: 16,
|
||||
marginRight: 8
|
||||
marginRight: 12
|
||||
},
|
||||
activeText: {
|
||||
color: THEME_COLOR,
|
||||
|
||||
@@ -50,7 +50,7 @@ export const selectAlbumsByArtist = createSelector(
|
||||
albumsByArtist,
|
||||
);
|
||||
|
||||
export type SectionedId = SectionListData<EntityId>;
|
||||
export type SectionedId = SectionListData<EntityId[]>;
|
||||
|
||||
/**
|
||||
* Splits a set of albums into a list that is split by alphabet letters
|
||||
@@ -58,13 +58,26 @@ export type SectionedId = SectionListData<EntityId>;
|
||||
function splitAlbumsByAlphabet(state: AppState['music']['albums']): SectionedId[] {
|
||||
const { entities: albums } = state;
|
||||
const albumIds = albumsByArtist(state);
|
||||
const sections: SectionedId[] = ALPHABET_LETTERS.split('').map((l) => ({ label: l, data: [] }));
|
||||
const sections: SectionedId[] = ALPHABET_LETTERS.split('').map((l) => ({ label: l, data: [[]] }));
|
||||
|
||||
albumIds.forEach((id) => {
|
||||
// Retrieve the album letter and corresponding letter index
|
||||
const album = albums[id];
|
||||
const letter = album?.AlbumArtist?.toUpperCase().charAt(0);
|
||||
const index = letter ? ALPHABET_LETTERS.indexOf(letter) : 26;
|
||||
(sections[index >= 0 ? index : 26].data as Array<EntityId>).push(id);
|
||||
|
||||
// Then find the current row in this section (note that albums are
|
||||
// grouped in pairs so we can render them more easily).
|
||||
const section = sections[index >= 0 ? index : 26];
|
||||
const row = section.data.length - 1;
|
||||
|
||||
// Add the album to the row
|
||||
section.data[row].push(id);
|
||||
|
||||
// GUARD: Check if the row is overflowing. If so, add a new row.
|
||||
if (section.data[row].length >= 2) {
|
||||
(section.data as EntityId[][]).push([]);
|
||||
}
|
||||
});
|
||||
|
||||
return sections;
|
||||
|
||||
Reference in New Issue
Block a user