fixes strava integration
This commit is contained in:
@@ -29,6 +29,7 @@ type StravaIntegration struct {
|
|||||||
AccessToken string `json:"accessToken,omitempty"`
|
AccessToken string `json:"accessToken,omitempty"`
|
||||||
RefreshToken string `json:"refreshToken,omitempty"`
|
RefreshToken string `json:"refreshToken,omitempty"`
|
||||||
ExpiresAt int64 `json:"expiresAt,omitempty"`
|
ExpiresAt int64 `json:"expiresAt,omitempty"`
|
||||||
|
After string `json:"after,omitempty"`
|
||||||
}
|
}
|
||||||
type StravaRoute struct {
|
type StravaRoute struct {
|
||||||
Athlete Athlete `json:"athlete"`
|
Athlete Athlete `json:"athlete"`
|
||||||
|
|||||||
@@ -89,22 +89,12 @@ func SyncStrava(app core.App) error {
|
|||||||
stravaIntegration.ExpiresAt = r.ExpiresAt
|
stravaIntegration.ExpiresAt = r.ExpiresAt
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(stravaIntegration)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.Set("strava", string(b))
|
|
||||||
err = app.Save(i)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if stravaIntegration.Routes {
|
if stravaIntegration.Routes {
|
||||||
page := 1
|
page := 1
|
||||||
hasNewRoutes := true
|
hasMore := true
|
||||||
for hasNewRoutes {
|
for hasMore {
|
||||||
|
|
||||||
routes, err := fetchStravaRoutes(r.AccessToken, page)
|
routes, err := fetchStravaRoutes(r.AccessToken, page)
|
||||||
|
hasMore = len(routes) > 0
|
||||||
page += 1
|
page += 1
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warning := fmt.Sprintf("error fetching routes from strava: %v\n", err)
|
warning := fmt.Sprintf("error fetching routes from strava: %v\n", err)
|
||||||
@@ -112,7 +102,7 @@ func SyncStrava(app core.App) error {
|
|||||||
app.Logger().Warn(warning)
|
app.Logger().Warn(warning)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
hasNewRoutes, err = syncTrailsWithRoutes(app, r.AccessToken, userId, actorId, routes)
|
err = syncTrailsWithRoutes(app, r.AccessToken, userId, actorId, routes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warning := fmt.Sprintf("error syncing strava routes with trails: %v\n", err)
|
warning := fmt.Sprintf("error syncing strava routes with trails: %v\n", err)
|
||||||
fmt.Print(warning)
|
fmt.Print(warning)
|
||||||
@@ -123,9 +113,20 @@ func SyncStrava(app core.App) error {
|
|||||||
}
|
}
|
||||||
if stravaIntegration.Activities {
|
if stravaIntegration.Activities {
|
||||||
page := 1
|
page := 1
|
||||||
hasNewActivities := true
|
hasMore := true
|
||||||
for hasNewActivities {
|
for hasMore {
|
||||||
activities, err := fetchStravaActivities(r.AccessToken, page)
|
var after int64 = 0
|
||||||
|
if stravaIntegration.After != "" {
|
||||||
|
t, err := time.Parse("2006-01-02", stravaIntegration.After)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t = t.UTC()
|
||||||
|
|
||||||
|
after = t.Unix()
|
||||||
|
}
|
||||||
|
activities, err := fetchStravaActivities(r.AccessToken, page, after)
|
||||||
|
hasMore = len(activities) > 0
|
||||||
page += 1
|
page += 1
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warning := fmt.Sprintf("error fetching activities from strava: %v", err)
|
warning := fmt.Sprintf("error fetching activities from strava: %v", err)
|
||||||
@@ -133,7 +134,8 @@ func SyncStrava(app core.App) error {
|
|||||||
app.Logger().Warn(warning)
|
app.Logger().Warn(warning)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
hasNewActivities, err = syncTrailsWithActivities(app, r.AccessToken, userId, actorId, activities)
|
err = syncTrailsWithActivities(app, r.AccessToken, actorId, activities)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
warning := fmt.Sprintf("error syncing strava activities with trails: %v", err)
|
warning := fmt.Sprintf("error syncing strava activities with trails: %v", err)
|
||||||
fmt.Print(warning)
|
fmt.Print(warning)
|
||||||
@@ -141,6 +143,17 @@ func SyncStrava(app core.App) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(stravaIntegration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Set("strava", string(b))
|
||||||
|
err = app.Save(i)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,8 +221,8 @@ func fetchStravaRoutes(accessToken string, page int) ([]StravaRoute, error) {
|
|||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchStravaActivities(accessToken string, page int) ([]StravaActivity, error) {
|
func fetchStravaActivities(accessToken string, page int, after int64) ([]StravaActivity, error) {
|
||||||
stravaRoutesURL := fmt.Sprintf("https://www.strava.com/api/v3/athlete/activities?page=%d", page)
|
stravaRoutesURL := fmt.Sprintf("https://www.strava.com/api/v3/athlete/activities?page=%d&after=%d", page, after)
|
||||||
req, err := http.NewRequest("GET", stravaRoutesURL, nil)
|
req, err := http.NewRequest("GET", stravaRoutesURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -235,17 +248,15 @@ func fetchStravaActivities(accessToken string, page int) ([]StravaActivity, erro
|
|||||||
return activities, nil
|
return activities, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncTrailsWithRoutes(app core.App, accessToken string, user string, actor string, routes []StravaRoute) (bool, error) {
|
func syncTrailsWithRoutes(app core.App, accessToken string, user string, actor string, routes []StravaRoute) error {
|
||||||
hasNewRoutes := false
|
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
trails, err := app.FindRecordsByFilter("trails", "external_id = {:id}", "", 1, 0, dbx.Params{"id": route.IDStr})
|
trails, err := app.FindRecordsByFilter("trails", "external_id = {:id}", "", 1, 0, dbx.Params{"id": route.IDStr})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hasNewRoutes, err
|
return err
|
||||||
}
|
}
|
||||||
if len(trails) != 0 {
|
if len(trails) != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hasNewRoutes = true
|
|
||||||
gpx, err := fetchRouteGPX(route, accessToken)
|
gpx, err := fetchRouteGPX(route, accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.Logger().Warn(fmt.Sprintf("Unable to fetch GPX for route '%s': %v", route.Name, err))
|
app.Logger().Warn(fmt.Sprintf("Unable to fetch GPX for route '%s': %v", route.Name, err))
|
||||||
@@ -263,7 +274,7 @@ func syncTrailsWithRoutes(app core.App, accessToken string, user string, actor s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasNewRoutes, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchRouteGPX(route StravaRoute, accessToken string) (*filesystem.File, error) {
|
func fetchRouteGPX(route StravaRoute, accessToken string) (*filesystem.File, error) {
|
||||||
@@ -393,17 +404,15 @@ func createWaypointsFromRoute(app core.App, route StravaRoute, user string, trai
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncTrailsWithActivities(app core.App, accessToken string, user string, actor string, activities []StravaActivity) (bool, error) {
|
func syncTrailsWithActivities(app core.App, accessToken string, actor string, activities []StravaActivity) error {
|
||||||
hasNewActivites := false
|
|
||||||
for _, activity := range activities {
|
for _, activity := range activities {
|
||||||
trails, err := app.FindRecordsByFilter("trails", "external_id = {:id}", "", 1, 0, dbx.Params{"id": strconv.Itoa(int(activity.ID))})
|
trails, err := app.FindRecordsByFilter("trails", "external_id = {:id}", "", 1, 0, dbx.Params{"id": strconv.Itoa(int(activity.ID))})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hasNewActivites, err
|
return err
|
||||||
}
|
}
|
||||||
if len(trails) != 0 {
|
if len(trails) != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hasNewActivites = true
|
|
||||||
detailedActivity, err := fetchDetailedActivity(activity, accessToken)
|
detailedActivity, err := fetchDetailedActivity(activity, accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.Logger().Warn(fmt.Sprintf("Unable to fetch detailed activity '%s': %v", activity.Name, err))
|
app.Logger().Warn(fmt.Sprintf("Unable to fetch detailed activity '%s': %v", activity.Name, err))
|
||||||
@@ -419,10 +428,9 @@ func syncTrailsWithActivities(app core.App, accessToken string, user string, act
|
|||||||
app.Logger().Warn(fmt.Sprintf("Unable to create trail from activity '%s': %v", activity.Name, err))
|
app.Logger().Warn(fmt.Sprintf("Unable to create trail from activity '%s': %v", activity.Name, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasNewActivites, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchDetailedActivity(activity StravaActivity, accessToken string) (*DetailedStravaActivity, error) {
|
func fetchDetailedActivity(activity StravaActivity, accessToken string) (*DetailedStravaActivity, error) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import Datepicker from "$lib/components/base/datepicker.svelte";
|
||||||
import Modal from "$lib/components/base/modal.svelte";
|
import Modal from "$lib/components/base/modal.svelte";
|
||||||
import TextField from "$lib/components/base/text_field.svelte";
|
import TextField from "$lib/components/base/text_field.svelte";
|
||||||
import Toggle from "$lib/components/base/toggle.svelte";
|
import Toggle from "$lib/components/base/toggle.svelte";
|
||||||
@@ -25,23 +26,32 @@
|
|||||||
modal.openModal();
|
modal.openModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { form, errors } = createForm({
|
const {
|
||||||
|
form,
|
||||||
|
errors,
|
||||||
|
data: formData,
|
||||||
|
} = createForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
clientId: integration?.strava?.clientId ?? "",
|
clientId: integration?.strava?.clientId ?? "",
|
||||||
clientSecret: integration?.strava?.clientSecret ?? "",
|
clientSecret: integration?.strava?.clientSecret ?? "",
|
||||||
routes: integration?.strava?.routes ?? true,
|
routes: integration?.strava?.routes ?? true,
|
||||||
activities: integration?.strava?.activities ?? true,
|
activities: integration?.strava?.activities ?? true,
|
||||||
active: integration?.strava?.active ?? false,
|
active: integration?.strava?.active ?? false,
|
||||||
|
after: integration?.strava?.after,
|
||||||
},
|
},
|
||||||
extend: validator({
|
extend: validator({
|
||||||
schema: StravaSchema,
|
schema: StravaSchema,
|
||||||
}),
|
}),
|
||||||
onSubmit: async (form) => {
|
onSubmit: async (form) => {
|
||||||
form.active = integration?.strava?.active ?? form.active
|
form.active = integration?.strava?.active ?? form.active;
|
||||||
onsave?.(form);
|
onsave?.(form);
|
||||||
modal.closeModal();
|
modal.closeModal();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function clearAfterDate() {
|
||||||
|
($formData as any).after = undefined;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -60,7 +70,9 @@
|
|||||||
></TextField>
|
></TextField>
|
||||||
<TextField
|
<TextField
|
||||||
label="Client Secret"
|
label="Client Secret"
|
||||||
placeholder={integration?.strava ? `(${$_("unchanged")})` : "de8b3789bd7116d..."}
|
placeholder={integration?.strava
|
||||||
|
? `(${$_("unchanged")})`
|
||||||
|
: "de8b3789bd7116d..."}
|
||||||
name="clientSecret"
|
name="clientSecret"
|
||||||
type="password"
|
type="password"
|
||||||
error={$errors.clientSecret}
|
error={$errors.clientSecret}
|
||||||
@@ -73,6 +85,25 @@
|
|||||||
label={$_("activity", { values: { n: 2 } })}
|
label={$_("activity", { values: { n: 2 } })}
|
||||||
></Toggle>
|
></Toggle>
|
||||||
</div>
|
</div>
|
||||||
|
<p
|
||||||
|
class="text-xs text-gray-500 max-w-lg pt-4 pb-1 border-t border-input-border"
|
||||||
|
>
|
||||||
|
{$_("strava-integration-after-date-hint")}
|
||||||
|
</p>
|
||||||
|
<div class="flex items-end relative gap-x-2">
|
||||||
|
<Datepicker
|
||||||
|
error={$errors.after}
|
||||||
|
label={$_("after")}
|
||||||
|
bind:value={$formData.after}
|
||||||
|
></Datepicker>
|
||||||
|
<button
|
||||||
|
class="btn-icon mb-[10px]"
|
||||||
|
type="button"
|
||||||
|
onclick={clearAfterDate}
|
||||||
|
aria-label="Clear 'after' date"
|
||||||
|
><i class="fa fa-close"></i></button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet footer()}
|
{#snippet footer()}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bäckerei",
|
"bakery": "Bäckerei",
|
||||||
"barrier": "Barriere",
|
"barrier": "Barriere",
|
||||||
"basic-info": "Basisinformation",
|
"basic-info": "Basisinformation",
|
||||||
|
"basque": "Baskisch",
|
||||||
"before": "Vor",
|
"before": "Vor",
|
||||||
"bicycle-parking": "Fahrrad Parkplatz",
|
"bicycle-parking": "Fahrrad Parkplatz",
|
||||||
"bicycle-rental": "Fahrradverleih",
|
"bicycle-rental": "Fahrradverleih",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Auto",
|
"car": "Auto",
|
||||||
"car-motorcycle": "Auto/Motorrad",
|
"car-motorcycle": "Auto/Motorrad",
|
||||||
"card": "{n, plural, =1 {Karte} other {Karten}}",
|
"card": "{n, plural, =1 {Karte} other {Karten}}",
|
||||||
"basque": "Baskisch",
|
|
||||||
"categories": "Kategorien",
|
"categories": "Kategorien",
|
||||||
"category": "Kategorie",
|
"category": "Kategorie",
|
||||||
"change": "Ändern",
|
"change": "Ändern",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistiken",
|
"statistics": "Statistiken",
|
||||||
"stop-drawing": "Zeichnen beenden",
|
"stop-drawing": "Zeichnen beenden",
|
||||||
"stop-editing": "Bearbeiten beenden",
|
"stop-editing": "Bearbeiten beenden",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "U-Bahn Eingang",
|
"subway-stop": "U-Bahn Eingang",
|
||||||
"summit": "Gipfel",
|
"summit": "Gipfel",
|
||||||
"summit-book": "Gipfelbuch",
|
"summit-book": "Gipfelbuch",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Basic Info",
|
"basic-info": "Basic Info",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Before",
|
"before": "Before",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Car",
|
"car": "Car",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Card} other {Cards}}",
|
"card": "{n, plural, =1 {Card} other {Cards}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Categories",
|
"categories": "Categories",
|
||||||
"category": "Category",
|
"category": "Category",
|
||||||
"change": "Change",
|
"change": "Change",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistics",
|
"statistics": "Statistics",
|
||||||
"stop-drawing": "Stop drawing",
|
"stop-drawing": "Stop drawing",
|
||||||
"stop-editing": "Stop editing",
|
"stop-editing": "Stop editing",
|
||||||
|
"strava-integration-after-date-hint": "If your account has a large amount of acitivities you may run into Strava's API rate limit preventing you from syncing all activities at once. To mitigate this issue you can set an \"After\" date below so that only activities that were recorded after this date are synced.",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Summit Book",
|
"summit-book": "Summit Book",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Información básica",
|
"basic-info": "Información básica",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Antes",
|
"before": "Antes",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Coche",
|
"car": "Coche",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, one {}=1 {Ficha} other {Fichas}}",
|
"card": "{n, plural, one {}=1 {Ficha} other {Fichas}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Categorías",
|
"categories": "Categorías",
|
||||||
"category": "Categoría",
|
"category": "Categoría",
|
||||||
"change": "Modificar",
|
"change": "Modificar",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Estadísticas",
|
"statistics": "Estadísticas",
|
||||||
"stop-drawing": "Parar de diseñar",
|
"stop-drawing": "Parar de diseñar",
|
||||||
"stop-editing": "Parar de editar",
|
"stop-editing": "Parar de editar",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Libro de ascensos",
|
"summit-book": "Libro de ascensos",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Okindegia",
|
"bakery": "Okindegia",
|
||||||
"barrier": "Oztopoa",
|
"barrier": "Oztopoa",
|
||||||
"basic-info": "Oinarrizko informazioa",
|
"basic-info": "Oinarrizko informazioa",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Aurretik",
|
"before": "Aurretik",
|
||||||
"bicycle-parking": "Bizikleta-parkina",
|
"bicycle-parking": "Bizikleta-parkina",
|
||||||
"bicycle-rental": "Bizikleta-alokairua",
|
"bicycle-rental": "Bizikleta-alokairua",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Kotxea",
|
"car": "Kotxea",
|
||||||
"car-motorcycle": "Kotxea/Motozikleta",
|
"car-motorcycle": "Kotxea/Motozikleta",
|
||||||
"card": "{n, plural, one {}=1 {Txartea} other {Txartelak}}",
|
"card": "{n, plural, one {}=1 {Txartea} other {Txartelak}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Kategoriak",
|
"categories": "Kategoriak",
|
||||||
"category": "Kategoria",
|
"category": "Kategoria",
|
||||||
"change": "Aldatu",
|
"change": "Aldatu",
|
||||||
@@ -338,7 +338,6 @@
|
|||||||
"route-point": "Ibilbideko puntua",
|
"route-point": "Ibilbideko puntua",
|
||||||
"russian": "Russian",
|
"russian": "Russian",
|
||||||
"save": "Gorde",
|
"save": "Gorde",
|
||||||
"russian": "Russian",
|
|
||||||
"save-list": "Gorde zerrenda",
|
"save-list": "Gorde zerrenda",
|
||||||
"save-trail": "Gorde ibilaldia",
|
"save-trail": "Gorde ibilaldia",
|
||||||
"save-your-trail-first": "Gorde zure ibilaldia lehenengo",
|
"save-your-trail-first": "Gorde zure ibilaldia lehenengo",
|
||||||
@@ -391,6 +390,7 @@
|
|||||||
"statistics": "Estatistikak",
|
"statistics": "Estatistikak",
|
||||||
"stop-drawing": "Utzi marrazteari",
|
"stop-drawing": "Utzi marrazteari",
|
||||||
"stop-editing": "Utzi editatzeari",
|
"stop-editing": "Utzi editatzeari",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Metro sarbidea",
|
"subway-stop": "Metro sarbidea",
|
||||||
"summit": "Gailurra",
|
"summit": "Gailurra",
|
||||||
"summit-book": "Igoeren liburua",
|
"summit-book": "Igoeren liburua",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Informations de base",
|
"basic-info": "Informations de base",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Avant le",
|
"before": "Avant le",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Voiture",
|
"car": "Voiture",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Tuile} other {Tuiles}}",
|
"card": "{n, plural, =1 {Tuile} other {Tuiles}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Catégories",
|
"categories": "Catégories",
|
||||||
"category": "Catégorie",
|
"category": "Catégorie",
|
||||||
"change": "Changement",
|
"change": "Changement",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistiques",
|
"statistics": "Statistiques",
|
||||||
"stop-drawing": "Arrêter de tracer",
|
"stop-drawing": "Arrêter de tracer",
|
||||||
"stop-editing": "Arrêter la modification",
|
"stop-editing": "Arrêter la modification",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Liste des ascensions",
|
"summit-book": "Liste des ascensions",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Alap információk",
|
"basic-info": "Alap információk",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Before",
|
"before": "Before",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Car",
|
"car": "Car",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Kártya} other {Kártyák}}",
|
"card": "{n, plural, =1 {Kártya} other {Kártyák}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Kategóriák",
|
"categories": "Kategóriák",
|
||||||
"category": "Kategória",
|
"category": "Kategória",
|
||||||
"change": "Változás",
|
"change": "Változás",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistics",
|
"statistics": "Statistics",
|
||||||
"stop-drawing": "Stop drawing",
|
"stop-drawing": "Stop drawing",
|
||||||
"stop-editing": "Stop editing",
|
"stop-editing": "Stop editing",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Csúcspont könyv",
|
"summit-book": "Csúcspont könyv",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Informazioni di base",
|
"basic-info": "Informazioni di base",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Prima",
|
"before": "Prima",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Car",
|
"car": "Car",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Carta} other {Carte}}",
|
"card": "{n, plural, =1 {Carta} other {Carte}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Categorie",
|
"categories": "Categorie",
|
||||||
"category": "Categoria",
|
"category": "Categoria",
|
||||||
"change": "Modifica",
|
"change": "Modifica",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistiche",
|
"statistics": "Statistiche",
|
||||||
"stop-drawing": "Smettere di disegnare",
|
"stop-drawing": "Smettere di disegnare",
|
||||||
"stop-editing": "Stop editing",
|
"stop-editing": "Stop editing",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Libro di vetta",
|
"summit-book": "Libro di vetta",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Algemene informatie",
|
"basic-info": "Algemene informatie",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Voor",
|
"before": "Voor",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Wagen",
|
"car": "Wagen",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Kaart} other {Kaarten}}",
|
"card": "{n, plural, =1 {Kaart} other {Kaarten}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Categorieën",
|
"categories": "Categorieën",
|
||||||
"category": "Categorie",
|
"category": "Categorie",
|
||||||
"change": "Wijzigen",
|
"change": "Wijzigen",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistieken",
|
"statistics": "Statistieken",
|
||||||
"stop-drawing": "Stop met tekenen",
|
"stop-drawing": "Stop met tekenen",
|
||||||
"stop-editing": "Stop met bewerken",
|
"stop-editing": "Stop met bewerken",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Bergtopboek",
|
"summit-book": "Bergtopboek",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Podstawowe informacje",
|
"basic-info": "Podstawowe informacje",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Przed",
|
"before": "Przed",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Samochód",
|
"car": "Samochód",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Karta} other {Karty}}",
|
"card": "{n, plural, =1 {Karta} other {Karty}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Kategorie",
|
"categories": "Kategorie",
|
||||||
"category": "Kategoria",
|
"category": "Kategoria",
|
||||||
"change": "Zmień",
|
"change": "Zmień",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statystyki",
|
"statistics": "Statystyki",
|
||||||
"stop-drawing": "Przestań rysować",
|
"stop-drawing": "Przestań rysować",
|
||||||
"stop-editing": "Zakończ edycję",
|
"stop-editing": "Zakończ edycję",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Logbook",
|
"summit-book": "Logbook",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Informações básicas",
|
"basic-info": "Informações básicas",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "Antes",
|
"before": "Antes",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Car",
|
"car": "Car",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Cartão} other {Cartões}}",
|
"card": "{n, plural, =1 {Cartão} other {Cartões}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Categorias",
|
"categories": "Categorias",
|
||||||
"category": "Categoria",
|
"category": "Categoria",
|
||||||
"change": "Alterar",
|
"change": "Alterar",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "Statistics",
|
"statistics": "Statistics",
|
||||||
"stop-drawing": "Parar desenho",
|
"stop-drawing": "Parar desenho",
|
||||||
"stop-editing": "Stop editing",
|
"stop-editing": "Stop editing",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "Livro da cimeira",
|
"summit-book": "Livro da cimeira",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "Bakery",
|
"bakery": "Bakery",
|
||||||
"barrier": "Barrier",
|
"barrier": "Barrier",
|
||||||
"basic-info": "Основная информация",
|
"basic-info": "Основная информация",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "До",
|
"before": "До",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "Автомобиль:",
|
"car": "Автомобиль:",
|
||||||
"car-motorcycle": "Car/Motorcycle",
|
"car-motorcycle": "Car/Motorcycle",
|
||||||
"card": "{n, plural, =1 {Карточка} other {Карточки}}",
|
"card": "{n, plural, =1 {Карточка} other {Карточки}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "Категории",
|
"categories": "Категории",
|
||||||
"category": "Категория",
|
"category": "Категория",
|
||||||
"change": "Изменить",
|
"change": "Изменить",
|
||||||
@@ -339,7 +339,6 @@
|
|||||||
"russian": "Russian",
|
"russian": "Russian",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"save-list": "Сохранить список",
|
"save-list": "Сохранить список",
|
||||||
"russian": "Russian",
|
|
||||||
"save-trail": "Сохранить трек",
|
"save-trail": "Сохранить трек",
|
||||||
"save-your-trail-first": "Сначала сохраните трек",
|
"save-your-trail-first": "Сначала сохраните трек",
|
||||||
"search-cities": "Поиск по городам",
|
"search-cities": "Поиск по городам",
|
||||||
@@ -391,6 +390,7 @@
|
|||||||
"statistics": "Статистика",
|
"statistics": "Статистика",
|
||||||
"stop-drawing": "Закончить рисование",
|
"stop-drawing": "Закончить рисование",
|
||||||
"stop-editing": "Закончить редактирование",
|
"stop-editing": "Закончить редактирование",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "Subway entrance",
|
"subway-stop": "Subway entrance",
|
||||||
"summit": "Summit",
|
"summit": "Summit",
|
||||||
"summit-book": "История поездок",
|
"summit-book": "История поездок",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"bakery": "面包店",
|
"bakery": "面包店",
|
||||||
"barrier": "障碍",
|
"barrier": "障碍",
|
||||||
"basic-info": "基本信息",
|
"basic-info": "基本信息",
|
||||||
|
"basque": "Basque",
|
||||||
"before": "之前",
|
"before": "之前",
|
||||||
"bicycle-parking": "自行车停车场",
|
"bicycle-parking": "自行车停车场",
|
||||||
"bicycle-rental": "自行车租车",
|
"bicycle-rental": "自行车租车",
|
||||||
@@ -56,7 +57,6 @@
|
|||||||
"car": "汽车",
|
"car": "汽车",
|
||||||
"car-motorcycle": "汽车/摩托车",
|
"car-motorcycle": "汽车/摩托车",
|
||||||
"card": "{n, plural, =1 {卡片} other {卡片}}",
|
"card": "{n, plural, =1 {卡片} other {卡片}}",
|
||||||
"basque": "Basque",
|
|
||||||
"categories": "分类",
|
"categories": "分类",
|
||||||
"category": "分类",
|
"category": "分类",
|
||||||
"change": "更换",
|
"change": "更换",
|
||||||
@@ -390,6 +390,7 @@
|
|||||||
"statistics": "统计",
|
"statistics": "统计",
|
||||||
"stop-drawing": "停止绘制",
|
"stop-drawing": "停止绘制",
|
||||||
"stop-editing": "停止编辑",
|
"stop-editing": "停止编辑",
|
||||||
|
"strava-integration-after-date-hint": "",
|
||||||
"subway-stop": "地铁入口",
|
"subway-stop": "地铁入口",
|
||||||
"summit": "山峰",
|
"summit": "山峰",
|
||||||
"summit-book": "详细日程",
|
"summit-book": "详细日程",
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ const StravaSchema = z.object({
|
|||||||
clientSecret: z.string().length(40).optional().or(z.literal('')),
|
clientSecret: z.string().length(40).optional().or(z.literal('')),
|
||||||
routes: z.boolean(),
|
routes: z.boolean(),
|
||||||
activities: z.boolean(),
|
activities: z.boolean(),
|
||||||
active: z.boolean()
|
active: z.boolean(),
|
||||||
|
after: z.string().date().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const KomootSchema = z.object({
|
const KomootSchema = z.object({
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface StravaIntegration extends BaseIntegration {
|
|||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
expiresAt?: number;
|
expiresAt?: number;
|
||||||
|
after?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KomootIntegration extends BaseIntegration {
|
export interface KomootIntegration extends BaseIntegration {
|
||||||
|
|||||||
Reference in New Issue
Block a user