Compare commits

...

4 Commits

Author SHA1 Message Date
Егор Мартынов
27126e8bef
Merge 8349ea8c9f into f67c544e16 2026-02-02 21:28:59 +11:00
Aaron Cunliffe
f67c544e16
fix: webserver folder creation regex change (#653)
Some checks failed
CI / build (push) Has been cancelled
## Summary

Resolves #562 

Implements regex change to support valid characters discussed by
@daveallie in issue
[here](https://github.com/crosspoint-reader/crosspoint-reader/issues/562#issuecomment-3830809156).

Also rejects `.` and `..` as folder names which are invalid in FAT32 and
exFAT filesystems

## Additional Context
- Unsure on the wording for the alert, it feels overly explicit, but
that might be a good thing. Happy to change.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< PARTIALLY >**_
2026-02-02 21:27:02 +11:00
mrtnvgr
8349ea8c9f
feat(settings): add "Cover + Custom" sleep screen mode 2026-01-28 14:00:37 +07:00
mrtnvgr
17aa6c8de9 chore(SleepActivity): use switch-case statement 2026-01-28 10:55:38 +07:00
6 changed files with 45 additions and 27 deletions

View File

@ -102,6 +102,7 @@ The Settings screen allows you to configure the device's behavior. There are a f
- "Custom" - Custom images from the SD card; see [Sleep Screen](#36-sleep-screen) below for more information
- "Cover" - The book cover image (Note: this is experimental and may not work as expected)
- "None" - A blank screen
- "Cover + Custom" - The book cover image, fallbacks to "Custom" behavior
- **Sleep Screen Cover Mode**: How to display the book cover when "Cover" sleep screen is selected:
- "Fit" (default) - Scale the image down to fit centered on the screen, padding with white borders as necessary
- "Crop" - Scale the image down and crop as necessary to try to to fill the screen (Note: this is experimental and may not work as expected)

View File

@ -153,7 +153,7 @@ Click **File Manager** to access file management features.
1. Click the **+ Add** button in the top-right corner
2. Select **New Folder** from the dropdown menu
3. Enter a folder name (letters, numbers, underscores, and hyphens only)
3. Enter a folder name (must not contain characters \" * : < > ? / \\ | and must not be . or ..)
4. Click **Create Folder**
This is useful for organizing your ebooks by genre, author, or series.

View File

@ -15,7 +15,15 @@ class CrossPointSettings {
CrossPointSettings(const CrossPointSettings&) = delete;
CrossPointSettings& operator=(const CrossPointSettings&) = delete;
enum SLEEP_SCREEN_MODE { DARK = 0, LIGHT = 1, CUSTOM = 2, COVER = 3, BLANK = 4, SLEEP_SCREEN_MODE_COUNT };
enum SLEEP_SCREEN_MODE {
DARK = 0,
LIGHT = 1,
CUSTOM = 2,
COVER = 3,
BLANK = 4,
COVER_CUSTOM = 5,
SLEEP_SCREEN_MODE_COUNT
};
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT };
enum SLEEP_SCREEN_COVER_FILTER {
NO_FILTER = 0,

View File

@ -18,19 +18,17 @@ void SleepActivity::onEnter() {
ScreenComponents::drawPopup(renderer, "Entering Sleep...");
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::BLANK) {
return renderBlankSleepScreen();
switch (SETTINGS.sleepScreen) {
case (CrossPointSettings::SLEEP_SCREEN_MODE::BLANK):
return renderBlankSleepScreen();
case (CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM):
return renderCustomSleepScreen();
case (CrossPointSettings::SLEEP_SCREEN_MODE::COVER):
case (CrossPointSettings::SLEEP_SCREEN_MODE::COVER_CUSTOM):
return renderCoverSleepScreen();
default:
return renderDefaultSleepScreen();
}
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) {
return renderCustomSleepScreen();
}
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::COVER) {
return renderCoverSleepScreen();
}
renderDefaultSleepScreen();
}
void SleepActivity::renderCustomSleepScreen() const {
@ -198,8 +196,18 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
}
void SleepActivity::renderCoverSleepScreen() const {
void (SleepActivity::*renderNoCoverSleepScreen)() const;
switch (SETTINGS.sleepScreen) {
case (CrossPointSettings::SLEEP_SCREEN_MODE::COVER_CUSTOM):
renderNoCoverSleepScreen = &SleepActivity::renderCustomSleepScreen;
break;
default:
renderNoCoverSleepScreen = &SleepActivity::renderDefaultSleepScreen;
break;
}
if (APP_STATE.openEpubPath.empty()) {
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
std::string coverBmpPath;
@ -212,12 +220,12 @@ void SleepActivity::renderCoverSleepScreen() const {
Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastXtc.load()) {
Serial.println("[SLP] Failed to load last XTC");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
if (!lastXtc.generateCoverBmp()) {
Serial.println("[SLP] Failed to generate XTC cover bmp");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
coverBmpPath = lastXtc.getCoverBmpPath();
@ -226,12 +234,12 @@ void SleepActivity::renderCoverSleepScreen() const {
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastTxt.load()) {
Serial.println("[SLP] Failed to load last TXT");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
if (!lastTxt.generateCoverBmp()) {
Serial.println("[SLP] No cover image found for TXT file");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
coverBmpPath = lastTxt.getCoverBmpPath();
@ -240,17 +248,17 @@ void SleepActivity::renderCoverSleepScreen() const {
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastEpub.load()) {
Serial.println("[SLP] Failed to load last epub");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
if (!lastEpub.generateCoverBmp(cropped)) {
Serial.println("[SLP] Failed to generate cover bmp");
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
coverBmpPath = lastEpub.getCoverBmpPath(cropped);
} else {
return renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
FsFile file;
@ -263,7 +271,7 @@ void SleepActivity::renderCoverSleepScreen() const {
}
}
renderDefaultSleepScreen();
return (this->*renderNoCoverSleepScreen)();
}
void SleepActivity::renderBlankSleepScreen() const {

View File

@ -14,7 +14,8 @@ namespace {
constexpr int displaySettingsCount = 6;
const SettingInfo displaySettings[displaySettingsCount] = {
// Should match with SLEEP_SCREEN_MODE
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen,
{"Dark", "Light", "Custom", "Cover", "None", "Cover + Custom"}),
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
SettingInfo::Enum("Sleep Screen Cover Filter", &CrossPointSettings::sleepScreenCoverFilter,
{"None", "Contrast", "Inverted"}),

View File

@ -1146,10 +1146,10 @@ function retryAllFailedUploads() {
return;
}
// Validate folder name (no special characters except underscore and hyphen)
const validName = /^[a-zA-Z0-9_\-]+$/.test(folderName);
// Validate folder name
const validName = /^(?!\.{1,2}$)[^"*:<>?\/\\|]+$/.test(folderName);
if (!validName) {
alert('Folder name can only contain letters, numbers, underscores, and hyphens.');
alert('Folder name cannot contain \" * : < > ? / \\ | and must not be . or ..');
return;
}