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