Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ush 1190 - Meaningful error messages on mobile around offline posts being uploaded #1152

Merged
merged 2 commits into from
Jul 8, 2024
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
2 changes: 0 additions & 2 deletions apps/mobile-mzima-client/android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ dependencies {
implementation project(':capacitor-dialog')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-geolocation')
implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-network')
implementation project(':capacitor-share')
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar')
implementation project(':capacitor-native-settings')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 220235003,
"versionName": "2.202350.3",
"versionCode": 220241000,
"versionName": "2.202410.0-alpha.0",
"outputFile": "app-release.apk"
}
],
Expand Down
32 changes: 13 additions & 19 deletions apps/mobile-mzima-client/android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
project(':capacitor-android').projectDir = new File('../../../node_modules/@capacitor/android/capacitor')

include ':capacitor-community-intercom'
project(':capacitor-community-intercom').projectDir = new File('../node_modules/@capacitor-community/intercom/android')
project(':capacitor-community-intercom').projectDir = new File('../../../node_modules/@capacitor-community/intercom/android')

include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
project(':capacitor-app').projectDir = new File('../../../node_modules/@capacitor/app/android')

include ':capacitor-camera'
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
project(':capacitor-camera').projectDir = new File('../../../node_modules/@capacitor/camera/android')

include ':capacitor-device'
project(':capacitor-device').projectDir = new File('../node_modules/@capacitor/device/android')
project(':capacitor-device').projectDir = new File('../../../node_modules/@capacitor/device/android')

include ':capacitor-dialog'
project(':capacitor-dialog').projectDir = new File('../node_modules/@capacitor/dialog/android')
project(':capacitor-dialog').projectDir = new File('../../../node_modules/@capacitor/dialog/android')

include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')
project(':capacitor-filesystem').projectDir = new File('../../../node_modules/@capacitor/filesystem/android')

include ':capacitor-geolocation'
project(':capacitor-geolocation').projectDir = new File('../node_modules/@capacitor/geolocation/android')

include ':capacitor-haptics'
project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android')
project(':capacitor-geolocation').projectDir = new File('../../../node_modules/@capacitor/geolocation/android')

include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
project(':capacitor-keyboard').projectDir = new File('../../../node_modules/@capacitor/keyboard/android')

include ':capacitor-network'
project(':capacitor-network').projectDir = new File('../node_modules/@capacitor/network/android')
project(':capacitor-network').projectDir = new File('../../../node_modules/@capacitor/network/android')

include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')

include ':capacitor-splash-screen'
project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
project(':capacitor-share').projectDir = new File('../../../node_modules/@capacitor/share/android')

include ':capacitor-status-bar'
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
project(':capacitor-status-bar').projectDir = new File('../../../node_modules/@capacitor/status-bar/android')

include ':capacitor-native-settings'
project(':capacitor-native-settings').projectDir = new File('../node_modules/capacitor-native-settings/android')
project(':capacitor-native-settings').projectDir = new File('../../../node_modules/capacitor-native-settings/android')
19 changes: 14 additions & 5 deletions apps/mobile-mzima-client/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { BaseComponent } from './base.component';
import { UploadFileHelper } from './post/helpers';
import { Location } from '@angular/common';
import { LanguageInterface } from '@mzima-client/sdk';
import { TranslateService } from '@ngx-translate/core';

@UntilDestroy()
@Component({
Expand All @@ -52,6 +53,7 @@ export class AppComponent extends BaseComponent {
private surveysService: SurveysService,
private listenerService: ListenerService,
private languageService: LanguageService,
private translateService: TranslateService,

@Optional() override routerOutlet?: IonRouterOutlet,
) {
Expand Down Expand Up @@ -149,12 +151,19 @@ export class AppComponent extends BaseComponent {

async uploadPendingPosts() {
const posts: any[] = await this.dataBaseService.get(STORAGE_KEYS.PENDING_POST_KEY);
for (let post of posts) {
if (post?.file?.upload)
post = await new UploadFileHelper(this.mediaService).uploadFile(post, post.file);
await firstValueFrom(this.postsService.post(post));
if (posts) {
this.toastMessage$.next(
this.translateService.instant('app.info.posts_uploading', { num_posts: posts.length }),
);
for (let post of posts) {
if (post?.file?.upload)
post = await new UploadFileHelper(this.mediaService).uploadFile(post, post.file);
await firstValueFrom(this.postsService.post(post));
}
this.toastMessage$.next(
this.translateService.instant('app.info.posts_uploaded', { num_posts: posts.length }),
);
}
this.toastMessage$.next('All pending posts uploaded to the server');
await this.dataBaseService.set(STORAGE_KEYS.PENDING_POST_KEY, []);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Injectable } from '@angular/core';
import { Network } from '@capacitor/network';
import { Subject } from 'rxjs';
import { BehaviorSubject } from 'rxjs';

const CONNECTION_TYPES = ['wifi', 'cellular'];

@Injectable({
providedIn: 'root',
})
export class NetworkService {
private readonly _networkStatus = new Subject<boolean>();
private readonly _networkStatus = new BehaviorSubject<boolean>(false);
readonly networkStatus$ = this._networkStatus.asObservable();

constructor() {
Expand All @@ -27,4 +27,8 @@ export class NetworkService {
const { connected, connectionType } = await Network.getStatus();
return connected && CONNECTION_TYPES.includes(connectionType);
}

getCurrentNetworkStatus(): boolean {
return this._networkStatus.getValue();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, OnInit, Output, forwardRef } from '@angular/core';
import { FilterControl, FilterControlOption } from '@models';
import { CategoriesService, CategoryInterface, SavedsearchesService } from '@mzima-client/sdk';
import { AlertService, SessionService, ToastService } from '@services';
import { AlertService, NetworkService, SessionService, ToastService } from '@services';
import { searchFormHelper } from '@helpers';
import _ from 'lodash';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
Expand Down Expand Up @@ -53,6 +53,7 @@ export class FilterComponent implements ControlValueAccessor, OnInit {
private categoriesService: CategoriesService,
private sessionService: SessionService,
private toastService: ToastService,
private networkService: NetworkService,
) {
this.sessionService.currentUserData$.subscribe({
next: () => {
Expand Down Expand Up @@ -144,28 +145,30 @@ export class FilterComponent implements ControlValueAccessor, OnInit {

private getSavedFilters(): void {
this.isOptionsLoading = true;
this.savedsearchesService.get().subscribe({
next: (response) => {
this.options = response.results.map((filter) => ({
value: filter.id,
label: filter.name,
checked: filter.id === this.filter.value,
info: `Applied filters: ${this.getObjectKeysCount(filter.filter)} of 24`,
}));
this.isOptionsLoading = false;
},
error: ({ message, status }) => {
this.isOptionsLoading = false;
this.toastService.presentToast({
message,
layout: 'stacked',
duration: 3000,
});
if (message.match(/Http failure response for/) && status !== 404) {
setTimeout(() => this.getSavedFilters(), 5000);
}
},
});
if (this.networkService.getCurrentNetworkStatus()) {
this.savedsearchesService.get().subscribe({
next: (response) => {
this.options = response.results.map((filter) => ({
value: filter.id,
label: filter.name,
checked: filter.id === this.filter.value,
info: `Applied filters: ${this.getObjectKeysCount(filter.filter)} of 24`,
}));
this.isOptionsLoading = false;
},
error: ({ message, status }) => {
this.isOptionsLoading = false;
this.toastService.presentToast({
message: 'Saved Filters: ' + message,
layout: 'stacked',
duration: 3000,
});
if (message.match(/Http failure response for/) && status !== 404) {
setTimeout(() => this.getSavedFilters(), 5000);
}
},
});
}
}

private getDataSources(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,28 +244,30 @@ export class MapViewComponent implements AfterViewInit {
// }

public getPostsGeoJson() {
this.postsService
.getGeojson({ limit: 500, offset: 0, page: 1, currentView: 'map' })
.pipe(untilDestroyed(this))
.subscribe({
next: async (postsResponse) => {
await this.databaseService.set(STORAGE_KEYS.GEOJSONPOSTS, postsResponse);
this.geoJsonDataProcessor(postsResponse);
},
error: async ({ message }) => {
this.toastService.presentToast({
message,
layout: 'stacked',
duration: 3000,
});
if (message.match(/Http failure response for/)) {
const posts = await this.databaseService.get(STORAGE_KEYS.GEOJSONPOSTS);
if (posts) {
this.geoJsonDataProcessor(posts);
if (this.isConnection) {
this.postsService
.getGeojson({ limit: 500, offset: 0, page: 1, currentView: 'map' })
.pipe(untilDestroyed(this))
.subscribe({
next: async (postsResponse) => {
await this.databaseService.set(STORAGE_KEYS.GEOJSONPOSTS, postsResponse);
this.geoJsonDataProcessor(postsResponse);
},
error: async ({ message }) => {
this.toastService.presentToast({
message: 'GeoJson: ' + message,
layout: 'stacked',
duration: 3000,
});
if (message.match(/Http failure response for/)) {
const posts = await this.databaseService.get(STORAGE_KEYS.GEOJSONPOSTS);
if (posts) {
this.geoJsonDataProcessor(posts);
}
}
}
},
});
},
});
}
}

private geoJsonDataProcessor(posts: GeoJsonPostsResponse) {
Expand Down
42 changes: 20 additions & 22 deletions apps/mobile-mzima-client/src/app/post/post-edit/post-edit.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,18 @@ import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { DomSanitizer } from '@angular/platform-browser';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { STORAGE_KEYS } from '@constants';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import {
BehaviorSubject,
EMPTY,
from,
lastValueFrom,
map,
Observable,
of,
switchMap,
tap,
} from 'rxjs';
import {
generalHelpers,
GeoJsonFilter,
MediaService,
PostContent,
postHelpers,
PostResult,
PostsService,
SurveyItem,
SurveysService,
generalHelpers,
postHelpers,
} from '@mzima-client/sdk';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import {
AlertService,
DatabaseService,
Expand All @@ -41,11 +30,23 @@ import {
ToastService,
} from '@services';
import { FormValidator, preparingVideoUrl } from '@validators';
import { PostEditForm, prepareRelationConfig, UploadFileProgressHelper } from '../helpers';
import {
BehaviorSubject,
EMPTY,
Observable,
from,
lastValueFrom,
map,
of,
switchMap,
tap,
} from 'rxjs';
import { PostEditForm, UploadFileProgressHelper, prepareRelationConfig } from '../helpers';

import { dateHelper, objectHelpers } from '@helpers';
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import { objectHelpers, dateHelper } from '@helpers';
import { TranslateService } from '@ngx-translate/core';

dayjs.extend(timezone);

Expand Down Expand Up @@ -115,6 +116,7 @@ export class PostEditPage {
private dataBaseService: DatabaseService,
private cdr: ChangeDetectorRef,
private sanitizer: DomSanitizer,
private translateService: TranslateService,
) {
this.route.queryParams.subscribe({
next: (queryParams) => {
Expand Down Expand Up @@ -622,9 +624,7 @@ export class PostEditPage {
if (this.isConnection) {
await this.uploadPost();
} else {
await this.postComplete(
'Thank you for your report. A message will be sent when the connection is restored.',
);
await this.postComplete(this.translateService.instant('app.info.post_submitted_offline'));
this.isSubmitting = 'complete';
this.backNavigation();
}
Expand Down Expand Up @@ -740,9 +740,7 @@ export class PostEditPage {
}
},
complete: async () => {
await this.postComplete(
'Thank you for submitting your report. The post is being reviewed by our team and soon will appear on the platform.',
);
await this.postComplete(this.translateService.instant('app.info.post_submitted_online'));
this.backNavigation();
},
});
Expand Down
9 changes: 8 additions & 1 deletion apps/mobile-mzima-client/src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@
"edit": "Edit",
"toc_text":"<p>These Terms of Service (the “Terms”) and our Privacy Policy apply to and govern your use of any websites owned or operated by Ushahidi Inc. (“Ushahidi”) that post a link to these Terms (such websites, the “Sites”), the Ushahidi services accessible via the Sites (including any cloud-based servers or platforms) and the Ushahidi mobile device applications (the “Apps”).</p><p>To make these Terms easier to read, the Sites, our services, platforms, and Apps are collectively called the “Services.” Please note that these Terms do not cover any of our products you download under an open source license. Additionally, these Terms may not apply if you have entered into a separate agreement with Ushahidi that governs your relationship with us, such as a separately-negotiated enterprise agreement.</p><h2>Agreement To Terms</h2><p>these Terms, do not use the Services. If you are accessing and using the Services on behalf of a company (such as your employer) or other legal entity, you represent and warrant that you have the authority to bind that company or other legal entity to these Terms.</p><p>In that case, “you” and “your” will refer to that company or other legal entity.</p>",
"skip": "Skip",
"loading":"Getting data..."
"loading":"Getting data...",
"info" : {
"post_submitted_online" : "Thank you for submitting your report. The post is being reviewed by our team and soon will appear on the platform.",
"post_submitted_offline" : "Thank you for your report. A message will be sent when the connection is restored.",
"posts_not_uploaded" : "<b>{num_posts} Posts not Uploaded!</b><br/>The posts were not uploaded due to bad internet connection or you were offline. They will be automatically uploaded as soon as you connect to the internet",
"posts_uploading" : "<b>Uploading {num_posts} Posts</b><br/>We are currently uploading your posts",
"posts_uploaded" : "<b>{num_posts} Posts Uploaded Successfully!</b><br/>Your posts were successfully uploaded. You can see them under \"My Posts\""
}
}
}
Loading