in Mongolian language month names too long for example (нэгдүгээр сар, хоёрдугаар сар) etc. i want (1-р сар, 2-р сар) etc
1 post - 1 participant
in Mongolian language month names too long for example (нэгдүгээр сар, хоёрдугаар сар) etc. i want (1-р сар, 2-р сар) etc
1 post - 1 participant
I am probably doing it wrong but since upgrading to Angular Ionic 8 autofill password no longer working when username entered since converting to the new format. How would i get this to work? Thanks in advance.
My Code
<ion-input class="login-input" label="Username" label-placement="floating" fill="outline" autocomplete="true" [(ngModel)]="username"></ion-input>
<ion-input class="login-input" label="Password" label-placement="floating" fill="outline" autocomplete="true" [disabled]="isAuthenticating||isInit" type="string" [(ngModel)]="password">
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
</ion-input>
2 posts - 2 participants
Hi All
I never previously had andriod studio issue related to my Ionic project but after update to 7, i am getting these DebugAndriodTestManifest errors.
When migrating the Ionic Project, is there a correct way to generate the Android project? I mean, as clean re-creation, but still getting my project settings, etc?
t seems that the migration from Ionic 6 to 7 has somehow affected my Andriod Project.
2 posts - 2 participants
Hello
I am using the ionicframework online tool builder. So I upload to Github a minimal amount of files, basically some config files and all files inside /src.
As I am not using XCode or Visual Studio, I don´t know where should I place PrivacyInfo.xcprivacy file.
I have tried at root and in /ios folder but when I download the xcarchive, I don´t find it inside, so I think that the system is not recognizing it when gets the code from GitHub.
How should I proceed?
Thanks.
2 posts - 2 participants
Hi there,
I wanted to intigrate Signaturepad in my project. I tried some different versions but nothing is working. Is there a working one? Please help
1 post - 1 participant
If an Ionic app is built with the default theme, that is, without touching its theming code (scss); and it is being viewed on (say) an Android platform in the default ‘md’, ‘Light’ mode.
The UI will contain the 9 colors (primary, secondary, tertiary, and so on), AND shades of gray applied to some elements and controls.
I understand that I can customize those 9 colors (primary, secondary, tertiary, and so on), by changing the values of the variables in the file varialbles.scss. I don’t understand from where the gray shades in the UI are being defined, and how I can customize or override those as per my requirements.
1 post - 1 participant
I have my angular.json set to use multiple projects, and my mobile project has a namespace of “app-mobile”.
When I try and build with
ionic build --configuration production
which runs the command
ng.cmd run app:build:production
I get the error: Project “app” does not exist.
How can I change this to run
ng.cmd run app-mobile:build:production
1 post - 1 participant
I have an IonButton component with the fill
set to “clear” and the class isLink
on it.
In my css I’m trying to override the default hover background color, but nothing is working. I’ve tried the following approaches:
Doesn’t work:
.isLink {
--background-hover: none;
}
Doesn’t work:
.isLink::part(native):hover {
background: none;
}
Doesn’t work:
.isLink::part(native):hover {
--background-hover: none;
}
If I set the background to “red” instead of “none” or “transparent”, it works fine like this:
.isLink::part(native):hover {
background: red;
}
What’s going on here? What’s the correct way to override this style?
1 post - 1 participant
I am using Ionic 8 and Capacitor 6.
My (still PWA but soon to be) app is set up based on ion-tabs with list items in each tab view.
When scrolling any of those lists and tapping on the status bar my expected behaviour would be the list to scroll to the top. However, when tapping the status bar neither in Safari nor installed as PWA (still Webkit) would scroll to the top. Nothing happens at all.
What I have tried:
Added statusBar: false option to IonicVue config - unfortunately just like swipeBackEnabled: false none of these have any effect.
Added Capacitor StatusBar, Style plugins and added event listener statusTap. This didn’t work either.
I don’t know if the above settings are supposed to work when the vue app is deployed as ios app. Howewer, something seems to prevent tapping of the status bar to be fired.
If you need sample code, I’ll be happy to provide it, however - as I mentioned - it’s pretty much the ion-tabs code sample with list items in each tab view.
I have tried on an iPhone XS, an iPad Pro (Gen. 1) both with iOS 17.4.1 and on Simulator app iPhone 15 Pro Max also on iOS 17.4.1.
Edit: I just tried it on two of the examples from docs and tapping the status bar there wouldn’t do anything as well (opened the iframe urls directly):
https://ionicframework.com/docs/usage/v8/infinite-scroll/basic/demo.html
https://ionicframework.com/docs/usage/v8/header/translucent/demo.html
1 post - 1 participant
Hello… I’m Rod. This happend when I try to create a new project with: ionic start myApp in blank
ERROR MESSAGE!!!:
\ Downloading and extracting blank starter Error: read ECONNRESET
** at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {**
** errno: -4077,**
** code: ‘ECONNRESET’,**
** syscall: ‘read’,**
** response: undefined**
}
Hope helps you
SOLUTION: provided by hauthorn
Follow up: I had Killer Network OEM software installed. Disabling all related services fixed the issue.
1 post - 1 participant
i build an android application using ionic framework using angular and capacitor, when i call my api with http its work fine and i get data, but when i use https i get canceled status in the debugger, the server work fine with https, i try to call the api with postman and its work fine, so if there is any configuration or plugin to add i would be glad to be informed;
1 post - 1 participant
The RegExp
constructor was called with a non-literal value. If an adversary were able to supply a malicious regex, they could cause a Regular Expression Denial of Service (ReDoS) against the application. In Node applications, this could cause the entire application to no longer be responsive to other users’ requests.
To remediate this issue, never allow user-supplied regular expressions. Instead, the regular expression should be hardcoded. If this is not possible, consider using an alternative regular expression engine such as node-re2. RE2 is a safe alternative that does not support backtracking, which is what leads to ReDoS.
Example using re2 which does not support backtracking (Note: it is still recommended to never use user-supplied input):
// Import the re2 module
const RE2 = require('re2');
function match(userSuppliedRegex, userInput) {
// Create a RE2 object with the user supplied regex, this is relatively safe
// due to RE2 not supporting backtracking which can be abused to cause long running
// queries
var re = new RE2(userSuppliedRegex);
// Execute the regular expression against some userInput
var result = re.exec(userInput);
// Work with the result
}
For more information on Regular Expression DoS see:
Medium
1 post - 1 participant
Hi.
My name is Fabian, and I have built an Ionic Capacitor React app for both Android and iOS. However, there is an issue that occurs on the iOS version, where the IonPage occasionally fails to render. This issue doesn’t seem to appear on Android.
There are no errors printed to the console when this issue happens, and the url of the failing page is correct, according to the Sarari inspector, so I don’t think it has to do with the router.
Here are some screenshots:
Page transition starting point (user presses the ‘Browse all videos’ button):
What the next page SHOULD look like:
What the next page occassionally looks like, when the error occurs (~10% of the time):
Tapping one of the tab buttons will fix it, and the correct content will then be rendered
Please help me solve this. I’ve tried several things, like: removing everything within the ion-content tags, checking the state at certain points of execution, reworking the sorting, but I am baffled.
Here is the relevant component code that should renders the IonPage:
import {
IonBackButton,
IonButtons,
IonContent,
IonHeader,
IonPage,
IonSearchbar,
IonTitle,
IonToolbar,
useIonViewDidEnter,
useIonViewWillEnter,
useIonViewWillLeave,
} from "@ionic/react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useLocation, useParams } from "react-router";
import { Virtuoso } from "react-virtuoso";
import { useAppSelector } from "../../lib/custom-hooks/useAppSelector";
import { useHomeSubRouting } from "../../lib/custom-hooks/useHomeSubRouting";
import { selectAllCpdVideos } from "../../lib/slices/appContentSlice";
import { CPDListLinkOrigin, CPDVideo, FilterListModalType, SortMode } from "../../lib/types";
import { Utils } from "../../lib/utils";
import { selectAppActivity } from "../../lib/slices/appActivitySlice";
import { Keyboard } from "@capacitor/keyboard";
import VideoFilterButton from "../../shared-components/VideoFilterButton";
import VideoFilterModal from "./VideoFilterModal";
import CPDSortModal from "./CPDSortModal";
import VideoCard from "./VideoCard";
import imgDropdownGreen from "../../assets/images/dropdown_green_n.png";
function CPDHubVideoList() {
console.log("CPDHubVideoList rendering...");
const homeSubRoute = useHomeSubRouting();
const location = useLocation();
const searchInputRef = useRef<HTMLIonSearchbarElement>(null);
const filtersContainerRef = useRef<HTMLDivElement | null>(null);
const prevScrollOffset = useRef(0);
const params = useParams<{ linkOrigin: CPDListLinkOrigin; optionalParam?: string }>(); // TODO optionalParam will be an event id or category
const decodedOptionalParam = params.optionalParam ? decodeURIComponent(params.optionalParam) : null;
const appActivity = useAppSelector(selectAppActivity);
const allVideos = useAppSelector(selectAllCpdVideos);
const allVideosForCurrentRoute = useMemo(() => filterAllVideosFromLinkOrigin(), [location.pathname]);
const [filtersContainerOffset, setFiltersContainerOffset] = useState(0);
const [searchText, setSearchText] = useState("");
// TODO use generics here instead of selectedCategories, selectedPresenters etc
const [selectedCategories, setSelectedCategories] = useState<string[]>(
decodedOptionalParam ? [decodedOptionalParam] : []
);
const [selectedPresenters, setSelectedPresenters] = useState<string[]>([]);
const [selectedDates, setSelectedDates] = useState<string[]>([]);
const [sortMode, setSortMode] = useState<SortMode>("Last added");
const [showSortModal, setShowSortModal] = useState(false);
const [filterListModal, setFilterListModal] = useState<{
show: boolean;
type: FilterListModalType;
allItems: string[];
}>({
show: false,
type: "",
allItems: [],
});
const uniqueCategories = useMemo(() => Utils.getUniqueCategoriesForVideos(allVideos), []);
const uniquePresenters = useMemo(() => getUniquePresentersForVideos(), []);
const uniqueDates = useMemo(() => getUniqueDatesForVideos(), []);
const filteredVideos = filterVideos();
const pageTitle = useMemo(() => {
switch (params.linkOrigin) {
case "bookmarks":
return "Bookmarked";
case "history":
return "History";
case "recently-added":
return "Recently added CPD";
case "top-rated":
return "Top Rated";
case "event":
return decodedOptionalParam;
default:
return "";
}
}, []);
const showSearchInput =
params.linkOrigin === "search" || params.linkOrigin === "browse" || params.linkOrigin === "category";
const showFilters =
params.linkOrigin === "search" ||
params.linkOrigin === "browse" ||
params.linkOrigin === "top-rated" ||
params.linkOrigin === "category" ||
params.linkOrigin === "event";
useIonViewDidEnter(() => {
if (params.linkOrigin === "search") {
searchInputRef.current?.setFocus();
}
}, []);
useIonViewWillEnter(() => {
if (filtersContainerRef.current) {
setFiltersContainerOffset(-filtersContainerRef.current.offsetHeight);
}
}, []);
useIonViewWillLeave(() => {
setFiltersContainerOffset(filtersContainerRef.current?.offsetHeight!);
}, []);
function filterAllVideosFromLinkOrigin() {
switch (params.linkOrigin) {
case "bookmarks":
return [...allVideos].filter((vid) => vid.isBookmarked);
case "history":
return [...allVideos].filter((vid) => appActivity.map((it) => it.id).includes(vid.id));
case "recently-added":
return [...allVideos].sort((a, b) => b.publishedOrder - a.publishedOrder).slice(0, 10);
case "event":
return [...allVideos]; // TODO
case "top-rated":
return [...allVideos].sort((a, b) => b.likeCount - a.likeCount).slice(0, 10);
case "category":
return [...allVideos].filter((vid) => vid.eventCategory === decodedOptionalParam);
default:
return [...allVideos];
}
}
function filterVideos() {
let videosCopy = [...allVideosForCurrentRoute];
// sort
if (sortMode === "Last added") {
videosCopy = sortVideosByLastAdded(videosCopy);
} else if (sortMode === "Oldest first") {
videosCopy = sortVideosOldestFirst(videosCopy);
} else {
videosCopy = sortVideosAlphabetically(videosCopy);
}
videosCopy = applySearch(videosCopy);
videosCopy = applyCategoryFiltering(videosCopy);
videosCopy = applyPresenterFiltering(videosCopy);
videosCopy = applyDatesFiltering(videosCopy);
return videosCopy;
}
function sortVideosByLastAdded(videos: CPDVideo[]) {
return [...videos].sort((a, b) => b.publishedOrder - a.publishedOrder);
}
function sortVideosOldestFirst(videos: CPDVideo[]) {
return [...videos].sort((a, b) => a.publishedOrder - b.publishedOrder);
}
function sortVideosAlphabetically(videos: CPDVideo[]) {
return [...videos].sort((a, b) => {
return a.title.localeCompare(b.title);
});
}
function applySearch(videos: CPDVideo[]) {
const trimmedText = searchText.toLowerCase().trim();
if (trimmedText === "") {
return videos;
}
const searched = videos.filter((vid) => {
return (
vid.title.toLowerCase().includes(trimmedText) ||
vid.eventType.toLowerCase().includes(trimmedText) ||
vid.eventCategory.toLowerCase().includes(trimmedText) ||
vid.shortDescription.toLowerCase().includes(trimmedText) ||
vid.presenters.join("").toLowerCase().includes(trimmedText)
);
});
return searched;
}
function applyCategoryFiltering(videos: CPDVideo[]) {
if (!selectedCategories.length) {
return videos;
}
return videos.filter((vid) => selectedCategories.includes(vid.eventCategory));
}
function applyPresenterFiltering(videos: CPDVideo[]) {
if (!selectedPresenters.length) {
return videos;
}
return videos.filter((vid) => {
return vid.presenters.some((presenterOfVideo) => selectedPresenters.includes(presenterOfVideo));
});
}
function applyDatesFiltering(videos: CPDVideo[]) {
if (!selectedDates.length) {
return videos;
}
return videos.filter((vid) => selectedDates.includes(vid.eventDate));
}
// TODO use generics instead?
function determineSelectedItemsForFilterModal() {
switch (filterListModal.type) {
case "Categories":
return selectedCategories;
case "Presenters":
return selectedPresenters;
case "Dates":
return selectedDates;
default:
return [];
}
}
function clearSelectedItems() {
switch (filterListModal.type) {
case "Categories":
return setSelectedCategories([]);
case "Presenters":
return setSelectedPresenters([]);
case "Dates":
return setSelectedDates([]);
}
}
function getUniquePresentersForVideos() {
const uniqueValues = allVideos
.flatMap((vid) => vid.presenters)
.filter((item, index, arr) => arr.indexOf(item) === index);
return uniqueValues.sort((a, b) => a.localeCompare(b)).filter((it) => it !== "");
}
// TODO
function getUniqueDatesForVideos() {
const uniqueValues = allVideos
.map((vid) => vid.eventDate)
.filter((item, index, arr) => arr.indexOf(item) === index);
return uniqueValues;
}
// show filters container if scrolling up, or scroll position is near top (to work well with iOS bounce). otherwise, hide the filters container
function handleContentScroll(scrollTop: number) {
if (filteredVideos.length < 4) return; // don't do anything if there's only a couple of videos to show
var currentScrollPos = scrollTop;
if (prevScrollOffset.current > currentScrollPos || currentScrollPos < 10) {
setFiltersContainerOffset(-filtersContainerRef.current?.offsetHeight!);
} else {
setFiltersContainerOffset(filtersContainerRef.current?.offsetHeight!);
}
prevScrollOffset.current = currentScrollPos;
}
return (
<IonPage>
<IonHeader className={"ion-no-border"}>
<IonToolbar className="t-border">
<IonButtons
slot="start"
class="mr-6"
style={{
// Ionic docs recommend using ion-searchbar as the only child to ion-toolbar, but we don't want to do that - which causes back buton misalignment.
// Add these margins so that the back button is vertically aligned to the centre, like other back buttons. This fix should only be implemented when using a search bar.
marginTop: showSearchInput ? "var(--padding-top)" : 0,
marginBottom: showSearchInput ? "var(--padding-bottom)" : 0,
}}
>
<IonBackButton defaultHref={homeSubRoute ? "/app/home/cpd-hub" : "/app/cpd-hub"} />
</IonButtons>
{/* TODO fix slight shift in the back button when using this searchbar in the toolbar */}
{showSearchInput ? (
<IonSearchbar
id="video-list-searchbar"
ref={searchInputRef}
value={searchText}
placeholder="Search CPD Hub"
onIonInput={(e) => setSearchText(e.detail.value!)}
className="font-normal text-black text-17 leading-27"
/>
) : (
<IonTitle>{pageTitle}</IonTitle>
)}
</IonToolbar>
<div className="relative">
<div
className="bg-grey-80 w-full absolute"
style={{
height: 300, // number doesn't matter much, just make it high so the background is covered
bottom: filtersContainerOffset,
}}
/>
<div
ref={filtersContainerRef}
className="absolute w-full"
style={{ transition: "bottom 0.2s", bottom: filtersContainerOffset }}
>
{showFilters && (
<div className="t-border bg-grey-80 flex t-border p-2 gap-3 overflow-scroll">
<VideoFilterButton
label="Category"
handleClick={() => setFilterListModal({ show: true, type: "Categories", allItems: uniqueCategories })}
selectedItemsInFilter={selectedCategories.length}
/>
<VideoFilterButton
label="Presenter"
handleClick={() => setFilterListModal({ show: true, type: "Presenters", allItems: uniquePresenters })}
selectedItemsInFilter={selectedPresenters.length}
/>
<VideoFilterButton
label="Date"
handleClick={() => setFilterListModal({ show: true, type: "Dates", allItems: uniqueDates })}
selectedItemsInFilter={selectedDates.length}
/>
</div>
)}
<div
className="flex justify-between items-center p-3 bg-white"
style={{ borderBottom: "solid 2px var(--grey-30)" }}
>
<div className="flex items-center">
<div className="text-13 font-semibold text-grey-70">Sort by: </div>
<button onClick={() => setShowSortModal(true)} className="flex items-center">
<div className="text-13 font-semibold text-green-30">{sortMode}</div>
<img width={24} height={24} src={imgDropdownGreen} style={{ marginTop: "-4px" }} />
</button>
</div>
<div className="text-13 font-semibold text-grey-70">{`${filteredVideos.length} result${
filteredVideos.length > 1 ? "s" : ""
}`}</div>
</div>
</div>
</div>
</IonHeader>
<IonContent style={{ "--background": "var(--grey-20)" }} onTouchStart={(e) => Keyboard.hide()}>
<Virtuoso
className="t-border"
data={filteredVideos}
onScroll={(e) => handleContentScroll(e.currentTarget.scrollTop)}
itemContent={(index, video) => {
return (
// applying padding directly to the virtuoso style prop won't work, so add padding for first item here
<div style={{ paddingTop: index === 0 ? filtersContainerRef.current?.clientHeight! : "" }}>
<VideoCard video={video} guestVersion={false} searchText={searchText} />
</div>
);
}}
/>
<CPDSortModal
showModal={showSortModal}
handleClose={() => setShowSortModal(false)}
handleSortChange={(newSortMode) => setSortMode(newSortMode)}
currentSortMode={sortMode}
/>
<VideoFilterModal
show={filterListModal.show}
type={filterListModal.type}
allItems={filterListModal.allItems}
selectedItems={determineSelectedItemsForFilterModal()}
resultsCount={filteredVideos.length}
handleReset={() => clearSelectedItems()}
handleClose={() => setFilterListModal({ show: false, type: "", allItems: [] })}
handleSelection={(selectedItem) => {
switch (filterListModal.type) {
case "Categories":
if (selectedCategories.includes(selectedItem)) {
// remove from cat list
const newItems = [...selectedCategories].filter((cat) => cat !== selectedItem);
setSelectedCategories(newItems);
} else {
// add item to cat list
setSelectedCategories([...selectedCategories, selectedItem]);
}
break;
case "Presenters":
if (selectedPresenters.includes(selectedItem)) {
const newItems = [...selectedPresenters].filter((cat) => cat !== selectedItem);
setSelectedPresenters(newItems);
} else {
setSelectedPresenters([...selectedPresenters, selectedItem]);
}
break;
case "Dates":
if (selectedDates.includes(selectedItem)) {
const newItems = [...selectedDates].filter((cat) => cat !== selectedItem);
setSelectedDates(newItems);
} else {
setSelectedDates([...selectedDates, selectedItem]);
}
break;
default:
throw new Error("Filter type not found");
}
}}
/>
</IonContent>
</IonPage>
);
}
export default CPDHubVideoList;
Thanks for any help!
Fabian
4 posts - 2 participants
I have a page that uses tabindex for ion-inputs. It works properly on a web browser, but in iOS the tabindex is completely ignored, and the “Next” key advances to the next input in the html file. I am on ionic angular 6.10.1. How can this be resolved?
1 post - 1 participant
Hello,
I have created a Android app with using the IONIC version 5 Cordova plugin and used Android version 9
when I uploaded the build on the Google Play Store, it required setting up Android SDK 33 so we made changes accordingly in the config xml file.
Now the build is uploaded in Google Console but our camera functionality is not working in that build, The normal APK camera works fine on Android devices. for the camera, we have used Cordova-plugin-camera plugins.
so any help would be greatly appreciated.
Thanks,
Vipul Jadvani
1 post - 1 participant
Howdy folks! I was reaching out to see if anyone can provide some insight on how to approach a project I’m working on. My team and I are building a headless WC mobile app with react, the ionic framework, jwt and graphql. The web version will remain a standard WC store. Our approach for checkout is use the WooGQL checkoutUrl and let wordpress handle checkout (which uses checkoutWC plugin) then redirect the user back to the app. Currently I have the app loading the checkout page via webview and basic 301 redirect that occurs after checkout. Which kind of works but once the user returns to the app they’re logged out. My assumption is that I need to pass the session back to the app though I’m not sure how to approach that. I’ve also tried opening the checkout page in capacitor’s in-app broswer but once GraphQL goes to validate the transfer-session, WP thinks the user isn’t authenticated so they just get directed to the homepage of the desktop site
If anyone has some recommendations or have a better alternative I would love to hear it as this is my first time working on an app.
2 posts - 2 participants
This just started happening to me when I used a new device (iPhone 15, ios 17.4).
It works fine on: iPhone 8, iPhone SE, web browser, Android (have not tested on a large Android device).
I have not changed any code. It may have always had this issue on larger iPhones but I don’t think so.
I am running @ionic/angular": “^6.4.2”
1 post - 1 participant
Currently I’m using ionic 6 where I’m using the ionics inAppBrowser plugin to open the webView.
But recently there’s a notification on InAppBrowser Plugin | Launch an In-App Web Browser on Ionic which says that inAppBrowser has reached it’s limit and its EOL will be July 1, 2024.
I’m using folliwing inAppBrowser :
import { InAppBrowser } from ‘@ionic-native/in-app-browser/ngx’;
According to notification given in the link InAppBrowser Plugin | Launch an In-App Web Browser on Ionic,
2 posts - 2 participants
Ionic 8 and Capacitor 6 with VueJS
I’ve seen several posts around this issue, but unfortunately none of the proposed fixes work for me, or seem “the right way to do it”.
As I understand it, safe areas should be applied automatically to each device, ensuring for e. g. ion-header and ion-footer or ion-tabs to be displayed correctly.
However, in my app this isn’t the case at all. Maybe I’m missing something as I don’t find the docs to be very concise on how and when to use the application variables --ion-safe-area (or if I’m actually supposed to use them - unless I want to change them deliberately for my purposes).
Anywho: I’d really appreciate someone shedding some light on this for me as I’m currently really lost.
Feel free though, to check out the Github repo: GitHub - socialmedialabs/ionic-sample-tabs-app
1 post - 1 participant