Skip to content

Commit

Permalink
integrations overview
Browse files Browse the repository at this point in the history
  • Loading branch information
RemcoSimonides committed Dec 29, 2024
1 parent d970d12 commit 490a4fe
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 17 deletions.
31 changes: 16 additions & 15 deletions apps/journal/src/app/pages/goal/goal.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { PagenotfoundComponent } from '@strive/ui/404/404.component'
import { HeaderRootComponent } from '@strive/ui/header-root/header-root.component'
import { StravaActivityTypesComponent } from '@strive/goal/components/strava-activity-types/strava-activity-types.component'
import { StravaCardComponent } from '@strive/strava/components/strava-card/strava-card.component'
import { IntegrationsComponent } from '@strive/goal/modals/integrations/integrations.component'
// Strive Services
import { GoalService } from '@strive/goal/goal.service'
import { GoalStakeholderService } from '@strive/stakeholder/stakeholder.service'
Expand Down Expand Up @@ -103,30 +104,17 @@ function stakeholderChanged(before: GoalStakeholder | undefined, after: GoalStak
imports: [
CommonModule,
RouterModule,
GoalOptionsPopoverComponent,
GoalSharePopoverComponent,
GoalUpdateModalComponent,
AchieversModalComponent,
SpectatorsModalComponent,
SupportersModalComponent,
ImageDirective,
ImageZoomModalComponent,
CompactPipe,
RoadmapComponent,
StoryComponent,
DescriptionComponent,
JoinButtonComponent,
SupportListComponent,
AddSupportComponent,
ChatModalComponent,
PageLoadingComponent,
PagenotfoundComponent,
HeaderRootComponent,
AddOthersModalComponent,
DeadlinePopoverComponent,
UpsertPostModalComponent,
CollectiveGoalsModalComponent,
SuggestionModalComponent,
StravaCardComponent,
IonFab,
IonFabButton,
Expand Down Expand Up @@ -394,8 +382,8 @@ export class GoalPageComponent implements OnDestroy {
case enumGoalOptions.deleteGoal:
this.deleteGoal()
break
case enumGoalOptions.integrateStrava:
this.integrateStrava()
case enumGoalOptions.integrations:
this.openIntegrations()
break
case enumGoalOptions.editReminders:
this.editReminders()
Expand Down Expand Up @@ -670,6 +658,19 @@ export class GoalPageComponent implements OnDestroy {
}).then(modal => modal.present())
}

async openIntegrations() {
const modal = await this.modalCtrl.create({
component: IntegrationsComponent,
componentProps: { goalId: this.goal?.id }
})
modal.present()
modal.onDidDismiss().then(({ data }) => {
if (data === 'strava') {
this.integrateStrava()
}
})
}

async integrateStrava(stravaAuthParams?: StravaAuthParams) {
if (!this.goal?.id) {
await firstValueFrom(this.goal$)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ion-item (click)="dismiss(enumGoalOptions.editReminders)" button>Edit Reminders</ion-item>
}
@if (stakeholder.isAdmin) {
<ion-item (click)="dismiss(enumGoalOptions.integrateStrava)" button>Strava integration</ion-item>
<ion-item (click)="dismiss(enumGoalOptions.integrations)" button>Integrations</ion-item>
}
@if (stakeholder.isAdmin || stakeholder.isAchiever) {
<ion-item (click)="dismiss(enumGoalOptions.editGoal)" button>Edit Goal</ion-item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export enum enumGoalOptions {
finishGoal,
editGoal,
deleteGoal,
integrateStrava,
integrations,
editReminders
}

Expand Down
Binary file added assets/images/dark/strava.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/light/strava.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions libs/goal/src/lib/modals/integrations/integrations.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<strive-header-modal (dismiss)="dismiss()">
<ion-title>Integrations</ion-title>
</strive-header-modal>

<ion-content class="ion-padding">
@if (view$ | async; as view) {

<h5>Active Integrations</h5>
<ul>
@for (integration of activeIntegrations; track integration) {
<li (click)="dismiss(integration.id)">
<img [asset]="integration.image" [alt]="integration.name" />
<span>{{ integration.name }}</span>
<small>{{ view.strava | activities }}</small>
</li>
}
</ul>

@if (availableIntegrations.length) {
<h5>Available Integrations</h5>
<ul>
@for (integration of availableIntegrations; track integration) {
<li (click)="dismiss(integration.id)">
<img [asset]="integration.image" [alt]="integration.name" />
<span>{{ integration.name }}</span>
</li>
}
</ul>
}
} @else {
<strive-page-loading />
}

</ion-content>
17 changes: 17 additions & 0 deletions libs/goal/src/lib/modals/integrations/integrations.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ul {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 150px));
grid-gap: 20px;

li {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;

small {
color: var(--ion-color-medium);
}
}
}
94 changes: 94 additions & 0 deletions libs/goal/src/lib/modals/integrations/integrations.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { CommonModule, Location } from '@angular/common'
import { ChangeDetectionStrategy, Component, Input, OnInit, Pipe, PipeTransform } from '@angular/core'

import { IonContent, IonTitle, ModalController } from '@ionic/angular/standalone'
import { BehaviorSubject, combineLatest, filter, map, switchMap, tap } from 'rxjs'
import { where } from 'firebase/firestore'

import { HeaderModalComponent } from '@strive/ui/header-modal/header-modal.component'
import { PageLoadingComponent } from '@strive/ui/page-loading/page-loading.component'
import { ModalDirective } from '@strive/utils/directives/modal.directive'
import { ImageDirective } from '@strive/media/directives/image.directive'
import { StravaIntegration } from '@strive/model'
import { StravaService } from '@strive/strava/strava.service'
import { AuthService } from '@strive/auth/auth.service'
import { capitalizeFirstLetter } from '@strive/utils/helpers'

interface Integration {
id: string
name: string
image: string
}

@Pipe({ name: 'activities', standalone: true })
export class StravaActivitiesPipe implements PipeTransform {
transform(strava: StravaIntegration | undefined) {
return strava?.activityTypes.map(type => capitalizeFirstLetter(type)).join(', ')
}
}

@Component({
standalone: true,
selector: 'strive-integrations',
templateUrl: './integrations.component.html',
styleUrls: ['./integrations.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
StravaActivitiesPipe,
HeaderModalComponent,
PageLoadingComponent,
ImageDirective,
IonContent,
IonTitle
]
})
export class IntegrationsComponent extends ModalDirective implements OnInit {

@Input() goalId?: string

activeIntegrations: Integration[] = []
availableIntegrations: Integration[] = []

private goalId$ = new BehaviorSubject(this.goalId)
private strava$ = this.goalId$.pipe(
filter(goalId => !!goalId),
switchMap(goalId => this.stravaService.valueChanges([
where('goalId', '==', goalId),
where('userId', '==', this.auth.uid)]
)),
map(integrations => integrations.length ? integrations[0] : undefined),
tap(strava => {
const integration: Integration = {
id: 'strava',
name: 'Strava',
image: 'strava.png'
}

if (strava) {
this.activeIntegrations.push(integration)
} else {
this.availableIntegrations.push(integration)
}
})
)

view$ = combineLatest([
this.strava$
]).pipe(
map(([strava]) => ({ strava }))
)

constructor(
private auth: AuthService,
protected override location: Location,
protected override modalCtrl: ModalController,
private stravaService: StravaService
) {
super(location, modalCtrl)
}

ngOnInit() {
this.goalId$.next(this.goalId)
}
}

0 comments on commit 490a4fe

Please sign in to comment.