Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.esl.ionic"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1003009
versionName "10.3.9"
versionCode 1003010
versionName "10.3.10"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Each dictation in a list now shows a short preview under the title, so you can pick the right practice without opening it.
Search by title, words, or ID, then press Enter. Age, type, and date are chips you can tap to change, and each list row now shows its preview at the bottom.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
列表里每份练习的标题下会显示一行预览,方便你不用打开就能选对听写练习。
可以用标题、生字或 ID 寻找练习,按回车开始。年龄、类型和日期点一下就能改,列表里每份练习的预览改到最下方。
Original file line number Diff line number Diff line change
@@ -1 +1 @@
列表裡每份練習的標題下會顯示一行預覽,方便你不用打開就能選對默書。
可以用標題、生字或 ID 尋找練習,按 Enter 開始。年齡、類型和日期點一下就能改,列表裡每份練習的預覽改到最下方。
2 changes: 1 addition & 1 deletion e2e/search-dictation-open-by-id.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test.describe('search dictation opens by id', () => {
test('view the first search result on /dictation-view/:id', async ({ page }) => {
await page.goto('/search-dictation');

const keyword = page.locator('ion-searchbar input');
const keyword = page.locator('ion-input[formcontrolname="keyword"] input');
await keyword.click();
await keyword.fill('apple');

Expand Down
8 changes: 4 additions & 4 deletions ios/App/App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,12 @@
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1003009;
CURRENT_PROJECT_VERSION = 1003010;
DEVELOPMENT_TEAM = DU483K8F48;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 10.3.9;
MARKETING_VERSION = 10.3.10;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = com.esl.ionic;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -378,12 +378,12 @@
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1003009;
CURRENT_PROJECT_VERSION = 1003010;
DEVELOPMENT_TEAM = DU483K8F48;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 10.3.9;
MARKETING_VERSION = 10.3.10;
PRODUCT_BUNDLE_IDENTIFIER = com.esl.ionic;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
4 changes: 2 additions & 2 deletions ios/App/App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>10.3.9</string>
<string>10.3.10</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand All @@ -34,7 +34,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1003009</string>
<string>1003010</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSRequiresIPhoneOS</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/App/fastlane/metadata/en-US/release_notes.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Each dictation in a list now shows a short preview under the title, so you can pick the right practice without opening it.
Search by title, words, or ID, then press Enter. Age, type, and date are chips you can tap to change, and each list row now shows its preview at the bottom.
2 changes: 1 addition & 1 deletion ios/App/fastlane/metadata/zh-Hans/release_notes.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
列表里每份练习的标题下会显示一行预览,方便你不用打开就能选对听写练习。
可以用标题、生字或 ID 寻找练习,按回车开始。年龄、类型和日期点一下就能改,列表里每份练习的预览改到最下方。
2 changes: 1 addition & 1 deletion ios/App/fastlane/metadata/zh-Hant/release_notes.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
列表裡每份練習的標題下會顯示一行預覽,方便你不用打開就能選對默書。
可以用標題、生字或 ID 尋找練習,按 Enter 開始。年齡、類型和日期點一下就能改,列表裡每份練習的預覽改到最下方。
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "esl-ionic",
"version": "10.3.9",
"version": "10.3.10",
"author": "thcathy",
"homepage": "https://www.funfunspell.com",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion release_notes/en.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Each dictation in a list now shows a short preview under the title, so you can pick the right practice without opening it.
Search by title, words, or ID, then press Enter. Age, type, and date are chips you can tap to change, and each list row now shows its preview at the bottom.
2 changes: 1 addition & 1 deletion release_notes/zh-Hans.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
列表里每份练习的标题下会显示一行预览,方便你不用打开就能选对听写练习。
可以用标题、生字或 ID 寻找练习,按回车开始。年龄、类型和日期点一下就能改,列表里每份练习的预览改到最下方。
2 changes: 1 addition & 1 deletion release_notes/zh-Hant.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
列表裡每份練習的標題下會顯示一行預覽,方便你不用打開就能選對默書。
可以用標題、生字或 ID 尋找練習,按 Enter 開始。年齡、類型和日期點一下就能改,列表裡每份練習的預覽改到最下方。
12 changes: 6 additions & 6 deletions src/app/components/dictation-list/dictation-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@
<ion-col class="heading">{{dictation.title}}</ion-col>
<ion-col class="ion-text-right">{{ dictation.createdDate | date: 'yyyy-MM-dd' }}</ion-col>
</ion-row>
@let line = previewLine(dictation);
<ion-row class="preview-row">
<ion-col>
<div class="preview-line" [title]="line">{{ line }}</div>
</ion-col>
</ion-row>
<ion-row>
<ion-col style="min-width: 120px;">
<fa-icon [icon]="['fas', 'list-ol']"></fa-icon> {{ dictation | dictationQuestions }}
Expand All @@ -49,6 +43,12 @@
<ion-col class="col-label">{{ 'Suitable' | translate }}: {{ 'SuitableStudent.' + dictation.suitableStudent | translate }}</ion-col>
</ion-row>
}
@let line = previewLine(dictation);
<ion-row class="preview-row">
<ion-col>
<div class="preview-line" [title]="line">{{ line }}</div>
</ion-col>
</ion-row>
</ion-label>
<ion-button fill="outline" slot="end" (click)="viewDictation(dictation)">{{ 'ViewDictation' | translate }}</ion-button>
</ion-item>
Expand Down
3 changes: 2 additions & 1 deletion src/app/components/dictation-list/dictation-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('DictationListComponent', () => {
expect(component.navService.openDictationById).not.toHaveBeenCalled();
});

it('renders one preview line under the heading on every row', () => {
it('renders one preview line at the bottom of every row', () => {
const described = TestData.fillInDictation();
described.description = 'Fruit words';
described.suitableStudent = 'Kindergarten';
Expand All @@ -67,6 +67,7 @@ describe('DictationListComponent', () => {
expect(previews.length).toBe(1);
const heading = label.querySelector('.heading');
expect(heading.compareDocumentPosition(previews[0]) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
expect(label.lastElementChild.classList.contains('preview-row')).toBeTrue();
});
expect(labels[0].querySelector('.preview-line').textContent.trim()).toBe('Fruit words');
expect(labels[0].querySelector('.preview-line').getAttribute('title')).toBe('Fruit words');
Expand Down
92 changes: 51 additions & 41 deletions src/app/pages/search-dictation/search-dictation.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
<ion-card class="card">
<ion-card-content>
<form class="input-form" [formGroup]="inputForm" novalidate>
<ion-searchbar (ionInput)="filterHistory($event)" formControlName="keyword" placeholder="{{'ID or part of title / description' | translate}}" (ionBlur)="stopShowHistory()" (keydown.enter)="search()"></ion-searchbar>
@if (filteredHistory && showHistory) {
<ion-item>
<ion-input formControlName="keyword" label="{{'Search'|translate}}" [clearInput]="true" placeholder="{{'Title, words, or ID' | translate}}" (ionInput)="filterHistory($event)" (ionBlur)="stopShowHistory()" (keydown.enter)="searchOnEnter($event)"></ion-input>
</ion-item>
@if (showHistory && filteredHistory.length) {
<ion-list class="autocomplete">
@for (h of filteredHistory; track h) {
<ion-item (mousedown)="setKeyword(h)">{{h}} <fa-icon item-end [icon]="['fas', 'history']"></fa-icon></ion-item>
Expand All @@ -20,19 +22,17 @@
}
@if (keyword.invalid && (keyword.dirty || keyword.touched)) {
<div class="error-text">
@if (keyword.errors.minlength) {
@if (keyword.errors.pattern) {
<div>{{'CharactersTooShort'|translate:{length: '3'} }}</div>
}
@if (keyword.errors.maxlength) {
<div>{{'CharactersTooLong'|translate:{length: '50'} }}</div>
}
</div>
}
@if (moreOptions) {
<ion-item>
<ion-input type="text" label="{{'Created By'|translate}}" formControlName="creator"></ion-input>
</ion-item>
}
<ion-item>
<ion-input type="text" label="{{'Created By'|translate}}" formControlName="creator"></ion-input>
</ion-item>
@if (creator.invalid && (creator.dirty || creator.touched)) {
<div class="error-text">
@if (creator.errors.minlength) {
Expand All @@ -43,42 +43,52 @@
}
</div>
}
@if (moreOptions) {
<ion-item>
<ion-select formControlName="suitableStudent" interface="popover"
label="{{'Suitable'|translate}} ({{'Age'|translate}})">
<div class="filters">
<button type="button" id="suitable-filter" class="filter-chip" data-filter="suitable">
<span class="chip-title">{{'Suitable'|translate}} ({{'Age'|translate}}):&nbsp;</span>
<span class="chip-value">{{ suitableChipLabel }}</span>
</button>
<button type="button" id="type-filter" class="filter-chip" data-filter="type">
<span class="chip-title">{{'Type'|translate}}:&nbsp;</span>
<span class="chip-value">{{ typeChipLabel }}</span>
</button>
<button type="button" id="date-filter" class="filter-chip" data-filter="date">
<span class="chip-title">{{'Date'|translate}}:&nbsp;</span>
<span class="chip-value">{{ dateChipLabel }}</span>
</button>
</div>
<ion-popover trigger="suitable-filter" triggerAction="click" [dismissOnSelect]="true" alignment="start">
<ng-template>
<ion-list>
@for (state of suitableStudentOptions; track state) {
<ion-select-option [value]="state">{{'SuitableStudent.' + state|translate}}</ion-select-option>
<ion-item button (click)="chooseSuitable(state)"><ion-label class="ion-text-nowrap">{{'SuitableStudent.' + state|translate}}</ion-label></ion-item>
}
</ion-select>
</ion-item>
}
@if (moreOptions) {
<ion-item>
<ion-select label="{{'Type'|translate}}" formControlName="type" interface="popover">
<ion-select-option value="Any">{{'Any'|translate}}</ion-select-option>
<ion-select-option value="Vocab">{{'Word'|translate}}</ion-select-option>
<ion-select-option value="Article">{{'Sentence'|translate}}</ion-select-option>
</ion-select>
</ion-item>
}
@if (moreOptions) {
<ion-item>
<ion-select label="{{'Date'|translate}}" formControlName="minDate" interface="popover">
@for (option of dateOptions; track option) {
<ion-select-option [value]="option">{{option.option|translate}}</ion-select-option>
</ion-list>
</ng-template>
</ion-popover>
<ion-popover trigger="type-filter" triggerAction="click" [dismissOnSelect]="true" alignment="start">
<ng-template>
<ion-list>
@for (option of typeOptions; track option.value) {
<ion-item button (click)="chooseType(option.value)"><ion-label class="ion-text-nowrap">{{option.label|translate}}</ion-label></ion-item>
}
</ion-select>
</ion-item>
}

<ion-button fill="outline" class="ion-float-left" (click)="moreOptions = !moreOptions">
{{'More Options'|translate}}
</ion-button>

<ion-button class="ion-float-right" (click)="search()" [disabled]="inputForm.invalid">
<fa-icon icon="search"></fa-icon>&nbsp;{{'Search'|translate}}
</ion-button>
</ion-list>
</ng-template>
</ion-popover>
<ion-popover trigger="date-filter" triggerAction="click" [dismissOnSelect]="true" alignment="start">
<ng-template>
<ion-list>
@for (option of dateOptions; track option.option) {
<ion-item button (click)="chooseDate(option)"><ion-label class="ion-text-nowrap">{{option.option|translate}}</ion-label></ion-item>
}
</ion-list>
</ng-template>
</ion-popover>
<div class="form-actions">
<ion-button (click)="search()" [disabled]="inputForm.invalid">
<fa-icon icon="search"></fa-icon>&nbsp;{{'Search'|translate}}
</ion-button>
</div>

</form>
</ion-card-content>
Expand Down
48 changes: 47 additions & 1 deletion src/app/pages/search-dictation/search-dictation.page.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
ion-input {
--placeholder-color: #a0a0a0;
}

.autocomplete {
width: calc(100% - 48px);
width: 100%;
position: absolute;
z-index: 999;
background: #f1f1f1;
Expand Down Expand Up @@ -34,3 +38,45 @@ ion-button {
.error-text {
padding-left: 16px;
}

.filters {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
margin: 12px 16px;
}

.filter-chip {
display: inline-flex;
align-items: center;
flex: 0 0 auto;
border: 0;
border-radius: 16px;
margin: 0;
padding: 0 10px;
height: 32px;
background: var(--ion-color-primary);
color: var(--ion-color-primary-contrast, #fff);
font-size: 14px;
white-space: nowrap;
cursor: pointer;
}

.chip-title {
font-weight: 600;
}

.chip-value {
font-weight: 400;
}

ion-popover {
--width: max-content;
--max-width: calc(100vw - 32px);
}

.form-actions {
display: flex;
justify-content: flex-end;
}
Loading
Loading