diff --git a/graphql/documents/data/image-slim.graphql b/graphql/documents/data/image-slim.graphql index b9f891fa8..4f787d36e 100644 --- a/graphql/documents/data/image-slim.graphql +++ b/graphql/documents/data/image-slim.graphql @@ -1,6 +1,8 @@ fragment SlimImageData on Image { id title + date + url rating100 organized o_counter diff --git a/graphql/documents/data/image.graphql b/graphql/documents/data/image.graphql index 8142d9d49..f9adb5515 100644 --- a/graphql/documents/data/image.graphql +++ b/graphql/documents/data/image.graphql @@ -2,6 +2,8 @@ fragment ImageData on Image { id title rating100 + date + url organized o_counter created_at diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index ae64f8526..2a0a7f860 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -425,6 +425,10 @@ input ImageFilterType { rating: IntCriterionInput @deprecated(reason: "Use 1-100 range with rating100") # rating expressed as 1-100 rating100: IntCriterionInput + """Filter by date""" + date: DateCriterionInput + """Filter by url""" + url: StringCriterionInput """Filter by organized""" organized: Boolean """Filter by o-counter""" diff --git a/graphql/schema/types/image.graphql b/graphql/schema/types/image.graphql index 3eed1ee85..6832cab24 100644 --- a/graphql/schema/types/image.graphql +++ b/graphql/schema/types/image.graphql @@ -6,6 +6,8 @@ type Image { rating: Int @deprecated(reason: "Use 1-100 range with rating100") # rating expressed as 1-100 rating100: Int + url: String + date: String o_counter: Int organized: Boolean! path: String! @deprecated(reason: "Use files.path") @@ -45,6 +47,8 @@ input ImageUpdateInput { # rating expressed as 1-100 rating100: Int organized: Boolean + url: String + date: String studio_id: ID performer_ids: [ID!] @@ -63,6 +67,8 @@ input BulkImageUpdateInput { # rating expressed as 1-100 rating100: Int organized: Boolean + url: String + date: String studio_id: ID performer_ids: BulkUpdateIds diff --git a/internal/api/resolver_model_image.go b/internal/api/resolver_model_image.go index c7fdb8c5f..2a1965c4e 100644 --- a/internal/api/resolver_model_image.go +++ b/internal/api/resolver_model_image.go @@ -75,6 +75,14 @@ func (r *imageResolver) File(ctx context.Context, obj *models.Image) (*ImageFile }, nil } +func (r *imageResolver) Date(ctx context.Context, obj *models.Image) (*string, error) { + if obj.Date != nil { + result := obj.Date.String() + return &result, nil + } + return nil, nil +} + func (r *imageResolver) Files(ctx context.Context, obj *models.Image) ([]*ImageFile, error) { files, err := r.getFiles(ctx, obj) if err != nil { diff --git a/internal/api/resolver_mutation_image.go b/internal/api/resolver_mutation_image.go index 135888f8f..6a482ff04 100644 --- a/internal/api/resolver_mutation_image.go +++ b/internal/api/resolver_mutation_image.go @@ -104,6 +104,8 @@ func (r *mutationResolver) imageUpdate(ctx context.Context, input ImageUpdateInp updatedImage := models.NewImagePartial() updatedImage.Title = translator.optionalString(input.Title, "title") updatedImage.Rating = translator.ratingConversionOptional(input.Rating, input.Rating100) + updatedImage.URL = translator.optionalString(input.URL, "url") + updatedImage.Date = translator.optionalDate(input.Date, "date") updatedImage.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id") if err != nil { return nil, fmt.Errorf("converting studio id: %w", err) @@ -190,6 +192,8 @@ func (r *mutationResolver) BulkImageUpdate(ctx context.Context, input BulkImageU updatedImage.Title = translator.optionalString(input.Title, "title") updatedImage.Rating = translator.ratingConversionOptional(input.Rating, input.Rating100) + updatedImage.URL = translator.optionalString(input.URL, "url") + updatedImage.Date = translator.optionalDate(input.Date, "date") updatedImage.StudioID, err = translator.optionalIntFromString(input.StudioID, "studio_id") if err != nil { return nil, fmt.Errorf("converting studio id: %w", err) diff --git a/pkg/image/export.go b/pkg/image/export.go index afb811a80..fb6ad0fa0 100644 --- a/pkg/image/export.go +++ b/pkg/image/export.go @@ -15,6 +15,7 @@ import ( func ToBasicJSON(image *models.Image) *jsonschema.Image { newImageJSON := jsonschema.Image{ Title: image.Title, + URL: image.URL, CreatedAt: json.JSONTime{Time: image.CreatedAt}, UpdatedAt: json.JSONTime{Time: image.UpdatedAt}, } @@ -23,6 +24,10 @@ func ToBasicJSON(image *models.Image) *jsonschema.Image { newImageJSON.Rating = *image.Rating } + if image.Date != nil { + newImageJSON.Date = image.Date.String() + } + newImageJSON.Organized = image.Organized newImageJSON.OCounter = image.OCounter diff --git a/pkg/image/export_test.go b/pkg/image/export_test.go index 6350b7302..7f3393d6f 100644 --- a/pkg/image/export_test.go +++ b/pkg/image/export_test.go @@ -25,6 +25,9 @@ const ( var ( title = "title" rating = 5 + url = "http://a.com" + date = "2001-01-01" + dateObj = models.NewDate(date) organized = true ocounter = 2 ) @@ -52,6 +55,8 @@ func createFullImage(id int) models.Image { Title: title, OCounter: ocounter, Rating: &rating, + Date: &dateObj, + URL: url, Organized: organized, CreatedAt: createTime, UpdatedAt: updateTime, @@ -63,6 +68,8 @@ func createFullJSONImage() *jsonschema.Image { Title: title, OCounter: ocounter, Rating: rating, + Date: date, + URL: url, Organized: organized, Files: []string{path}, CreatedAt: json.JSONTime{ diff --git a/pkg/image/import.go b/pkg/image/import.go index 018dbf9fb..b5e54e594 100644 --- a/pkg/image/import.go +++ b/pkg/image/import.go @@ -85,6 +85,13 @@ func (i *Importer) imageJSONToImage(imageJSON jsonschema.Image) models.Image { if imageJSON.Rating != 0 { newImage.Rating = &imageJSON.Rating } + if imageJSON.URL != "" { + newImage.URL = imageJSON.URL + } + if imageJSON.Date != "" { + d := models.NewDate(imageJSON.Date) + newImage.Date = &d + } return newImage } diff --git a/pkg/models/image.go b/pkg/models/image.go index 2b908dcb6..774e0536a 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -18,6 +18,10 @@ type ImageFilterType struct { Rating *IntCriterionInput `json:"rating"` // Filter by rating expressed as 1-100 Rating100 *IntCriterionInput `json:"rating100"` + // Filter by date + Date *DateCriterionInput `json:"date"` + // Filter by url + URL *StringCriterionInput `json:"url"` // Filter by organized Organized *bool `json:"organized"` // Filter by o-counter diff --git a/pkg/models/jsonschema/image.go b/pkg/models/jsonschema/image.go index 364daa0cf..1862ffc82 100644 --- a/pkg/models/jsonschema/image.go +++ b/pkg/models/jsonschema/image.go @@ -13,6 +13,8 @@ type Image struct { Title string `json:"title,omitempty"` Studio string `json:"studio,omitempty"` Rating int `json:"rating,omitempty"` + URL string `json:"url,omitempty"` + Date string `json:"date,omitempty"` Organized bool `json:"organized,omitempty"` OCounter int `json:"o_counter,omitempty"` Galleries []GalleryRef `json:"galleries,omitempty"` diff --git a/pkg/models/model_image.go b/pkg/models/model_image.go index dcece55bb..42425c455 100644 --- a/pkg/models/model_image.go +++ b/pkg/models/model_image.go @@ -16,10 +16,12 @@ type Image struct { Title string `json:"title"` // Rating expressed in 1-100 scale - Rating *int `json:"rating"` - Organized bool `json:"organized"` - OCounter int `json:"o_counter"` - StudioID *int `json:"studio_id"` + Rating *int `json:"rating"` + Organized bool `json:"organized"` + OCounter int `json:"o_counter"` + StudioID *int `json:"studio_id"` + URL string `json:"url"` + Date *Date `json:"date"` // transient - not persisted Files RelatedImageFiles @@ -117,6 +119,8 @@ type ImagePartial struct { Title OptionalString // Rating expressed in 1-100 scale Rating OptionalInt + URL OptionalString + Date OptionalDate Organized OptionalBool OCounter OptionalInt StudioID OptionalInt diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 9c1b46073..63a427ed8 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -21,7 +21,7 @@ import ( "github.com/stashapp/stash/pkg/logger" ) -var appSchemaVersion uint = 42 +var appSchemaVersion uint = 43 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/image.go b/pkg/sqlite/image.go index 7c3cd6c9d..4e8e1b3ac 100644 --- a/pkg/sqlite/image.go +++ b/pkg/sqlite/image.go @@ -31,6 +31,8 @@ type imageRow struct { Title zero.String `db:"title"` // expressed as 1-100 Rating null.Int `db:"rating"` + URL zero.String `db:"url"` + Date models.SQLiteDate `db:"date"` Organized bool `db:"organized"` OCounter int `db:"o_counter"` StudioID null.Int `db:"studio_id,omitempty"` @@ -42,6 +44,10 @@ func (r *imageRow) fromImage(i models.Image) { r.ID = i.ID r.Title = zero.StringFrom(i.Title) r.Rating = intFromPtr(i.Rating) + r.URL = zero.StringFrom(i.URL) + if i.Date != nil { + _ = r.Date.Scan(i.Date.Time) + } r.Organized = i.Organized r.OCounter = i.OCounter r.StudioID = intFromPtr(i.StudioID) @@ -62,6 +68,8 @@ func (r *imageQueryRow) resolve() *models.Image { ID: r.ID, Title: r.Title.String, Rating: nullIntPtr(r.Rating), + URL: r.URL.String, + Date: r.Date.DatePtr(), Organized: r.Organized, OCounter: r.OCounter, StudioID: nullIntPtr(r.StudioID), @@ -87,6 +95,8 @@ type imageRowRecord struct { func (r *imageRowRecord) fromPartial(i models.ImagePartial) { r.setNullString("title", i.Title) r.setNullInt("rating", i.Rating) + r.setNullString("url", i.URL) + r.setSQLiteDate("date", i.Date) r.setBool("organized", i.Organized) r.setInt("o_counter", i.OCounter) r.setNullInt("studio_id", i.StudioID) @@ -638,6 +648,8 @@ func (qb *ImageStore) makeFilter(ctx context.Context, imageFilter *models.ImageF query.handleCriterion(ctx, rating5CriterionHandler(imageFilter.Rating, "images.rating", nil)) query.handleCriterion(ctx, intCriterionHandler(imageFilter.OCounter, "images.o_counter", nil)) query.handleCriterion(ctx, boolCriterionHandler(imageFilter.Organized, "images.organized", nil)) + query.handleCriterion(ctx, dateCriterionHandler(imageFilter.Date, "images.date")) + query.handleCriterion(ctx, stringCriterionHandler(imageFilter.URL, "images.url")) query.handleCriterion(ctx, resolutionCriterionHandler(imageFilter.Resolution, "image_files.height", "image_files.width", qb.addImageFilesTable)) query.handleCriterion(ctx, imageIsMissingCriterionHandler(qb, imageFilter.IsMissing)) diff --git a/pkg/sqlite/image_test.go b/pkg/sqlite/image_test.go index d40859de9..31f6d4876 100644 --- a/pkg/sqlite/image_test.go +++ b/pkg/sqlite/image_test.go @@ -56,6 +56,8 @@ func Test_imageQueryBuilder_Create(t *testing.T) { title = "title" rating = 60 ocounter = 5 + url = "url" + date = models.NewDate("2003-02-01") createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) @@ -72,6 +74,8 @@ func Test_imageQueryBuilder_Create(t *testing.T) { models.Image{ Title: title, Rating: &rating, + Date: &date, + URL: url, Organized: true, OCounter: ocounter, StudioID: &studioIDs[studioIdxWithImage], @@ -88,6 +92,8 @@ func Test_imageQueryBuilder_Create(t *testing.T) { models.Image{ Title: title, Rating: &rating, + Date: &date, + URL: url, Organized: true, OCounter: ocounter, StudioID: &studioIDs[studioIdxWithImage], @@ -209,6 +215,8 @@ func Test_imageQueryBuilder_Update(t *testing.T) { var ( title = "title" rating = 60 + url = "url" + date = models.NewDate("2003-02-01") ocounter = 5 createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) @@ -225,6 +233,8 @@ func Test_imageQueryBuilder_Update(t *testing.T) { ID: imageIDs[imageIdxWithGallery], Title: title, Rating: &rating, + URL: url, + Date: &date, Organized: true, OCounter: ocounter, StudioID: &studioIDs[studioIdxWithImage], @@ -372,6 +382,8 @@ func clearImagePartial() models.ImagePartial { return models.ImagePartial{ Title: models.OptionalString{Set: true, Null: true}, Rating: models.OptionalInt{Set: true, Null: true}, + URL: models.OptionalString{Set: true, Null: true}, + Date: models.OptionalDate{Set: true, Null: true}, StudioID: models.OptionalInt{Set: true, Null: true}, GalleryIDs: &models.UpdateIDs{Mode: models.RelationshipUpdateModeSet}, TagIDs: &models.UpdateIDs{Mode: models.RelationshipUpdateModeSet}, @@ -383,6 +395,8 @@ func Test_imageQueryBuilder_UpdatePartial(t *testing.T) { var ( title = "title" rating = 60 + url = "url" + date = models.NewDate("2003-02-01") ocounter = 5 createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC) @@ -401,6 +415,8 @@ func Test_imageQueryBuilder_UpdatePartial(t *testing.T) { models.ImagePartial{ Title: models.NewOptionalString(title), Rating: models.NewOptionalInt(rating), + URL: models.NewOptionalString(url), + Date: models.NewOptionalDate(date), Organized: models.NewOptionalBool(true), OCounter: models.NewOptionalInt(ocounter), StudioID: models.NewOptionalInt(studioIDs[studioIdxWithImage]), @@ -423,6 +439,8 @@ func Test_imageQueryBuilder_UpdatePartial(t *testing.T) { ID: imageIDs[imageIdx1WithGallery], Title: title, Rating: &rating, + URL: url, + Date: &date, Organized: true, OCounter: ocounter, StudioID: &studioIDs[studioIdxWithImage], @@ -943,7 +961,8 @@ func Test_imageQueryBuilder_Destroy(t *testing.T) { } func makeImageWithID(index int) *models.Image { - ret := makeImage(index) + const fromDB = true + ret := makeImage(index, true) ret.ID = imageIDs[index] ret.Files = models.NewRelatedImageFiles([]*file.ImageFile{makeImageFile(index)}) @@ -2560,6 +2579,13 @@ func TestImageQuerySorting(t *testing.T) { -1, -1, }, + { + "date", + "date", + models.SortDirectionEnumDesc, + imageIdxWithTwoGalleries, + imageIdxWithGrandChildStudio, + }, } qb := db.Image diff --git a/pkg/sqlite/migrations/43_image_date_url.up.sql b/pkg/sqlite/migrations/43_image_date_url.up.sql new file mode 100644 index 000000000..b66591acb --- /dev/null +++ b/pkg/sqlite/migrations/43_image_date_url.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE `images` ADD COLUMN `url` varchar(255); +ALTER TABLE `images` ADD COLUMN `date` date; \ No newline at end of file diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index f086b5eb6..72ed733c5 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -885,9 +885,9 @@ func getObjectDate(index int) models.SQLiteDate { } } -func getObjectDateObject(index int) *models.Date { +func getObjectDateObject(index int, fromDB bool) *models.Date { d := getObjectDate(index) - if !d.Valid { + if !d.Valid || (fromDB && (d.String == "" || d.String == "0001-01-01")) { return nil } @@ -998,7 +998,7 @@ func makeScene(i int) *models.Scene { URL: getSceneEmptyString(i, urlField), Rating: getIntPtr(rating), OCounter: getOCounter(i), - Date: getObjectDateObject(i), + Date: getObjectDateObject(i, false), StudioID: studioID, GalleryIDs: models.NewRelatedIDs(gids), PerformerIDs: models.NewRelatedIDs(pids), @@ -1063,7 +1063,7 @@ func makeImageFile(i int) *file.ImageFile { } } -func makeImage(i int) *models.Image { +func makeImage(i int, fromDB bool) *models.Image { title := getImageStringValue(i, titleField) var studioID *int if _, ok := imageStudios[i]; ok { @@ -1078,6 +1078,8 @@ func makeImage(i int) *models.Image { return &models.Image{ Title: title, Rating: getIntPtr(getRating(i)), + Date: getObjectDateObject(i, fromDB), + URL: getImageStringValue(i, urlField), OCounter: getOCounter(i), StudioID: studioID, GalleryIDs: models.NewRelatedIDs(gids), @@ -1101,7 +1103,7 @@ func createImages(ctx context.Context, n int) error { } imageFileIDs = append(imageFileIDs, f.ID) - image := makeImage(i) + image := makeImage(i, false) err := qb.Create(ctx, &models.ImageCreateInput{ Image: image, @@ -1162,7 +1164,7 @@ func makeGallery(i int, includeScenes bool) *models.Gallery { Title: getGalleryStringValue(i, titleField), URL: getGalleryNullStringValue(i, urlField).String, Rating: getIntPtr(getRating(i)), - Date: getObjectDateObject(i), + Date: getObjectDateObject(i, false), StudioID: studioID, PerformerIDs: models.NewRelatedIDs(pids), TagIDs: models.NewRelatedIDs(tids), diff --git a/ui/v2.5/src/components/Images/ImageDetails/ImageDetailPanel.tsx b/ui/v2.5/src/components/Images/ImageDetails/ImageDetailPanel.tsx index 7094e778c..b1c961103 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/ImageDetailPanel.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/ImageDetailPanel.tsx @@ -6,9 +6,8 @@ import { TagLink, TruncatedText } from "src/components/Shared"; import { PerformerCard } from "src/components/Performers/PerformerCard"; import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; import { sortPerformers } from "src/core/performers"; -import { FormattedMessage, useIntl } from "react-intl"; +import { FormattedDate, FormattedMessage, useIntl } from "react-intl"; import { objectTitle } from "src/core/files"; - interface IImageDetailProps { image: GQL.ImageDataFragment; } @@ -91,6 +90,15 @@ export const ImageDetailPanel: React.FC = (props) => { + {props.image.date ? ( +
+ +
+ ) : undefined} {props.image.rating100 ? (
:{" "} @@ -99,6 +107,7 @@ export const ImageDetailPanel: React.FC = (props) => { ) : ( "" )} + {renderGalleries()} {file?.width && file?.height ? (
diff --git a/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx b/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx index b16da38b5..77b3dc02d 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/ImageEditPanel.tsx @@ -10,6 +10,7 @@ import { TagSelect, StudioSelect, LoadingIndicator, + URLField, } from "src/components/Shared"; import { useToast } from "src/hooks"; import { FormUtils } from "src/utils"; @@ -39,6 +40,8 @@ export const ImageEditPanel: React.FC = ({ const schema = yup.object({ title: yup.string().optional().nullable(), rating100: yup.number().optional().nullable(), + url: yup.string().optional().nullable(), + date: yup.string().optional().nullable(), studio_id: yup.string().optional().nullable(), performer_ids: yup.array(yup.string().required()).optional().nullable(), tag_ids: yup.array(yup.string().required()).optional().nullable(), @@ -47,6 +50,8 @@ export const ImageEditPanel: React.FC = ({ const initialValues = { title: image.title ?? "", rating100: image.rating100 ?? null, + url: image?.url ?? "", + date: image?.date ?? "", studio_id: image.studio?.id, performer_ids: (image.performers ?? []).map((p) => p.id), tag_ids: (image.tags ?? []).map((t) => t.id), @@ -189,6 +194,28 @@ export const ImageEditPanel: React.FC = ({
{renderTextField("title", intl.formatMessage({ id: "title" }))} + + + + + + + + {}} + urlScrapable={() => { + return false; + }} + isInvalid={!!formik.getFieldMeta("url").error} + /> + + + {renderTextField( + "date", + intl.formatMessage({ id: "date" }), + "YYYY-MM-DD" + )} {FormUtils.renderLabel({ title: intl.formatMessage({ id: "rating" }), diff --git a/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx b/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx index 9d0991775..85f0cf58b 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/ImageFileInfoPanel.tsx @@ -118,7 +118,24 @@ export const ImageFileInfoPanel: React.FC = ( } if (props.image.files.length === 1) { - return ; + return ( + <> + + + {props.image.url ? ( +
+ +
+ ) : ( + "" + )} + + ); } async function onSetPrimaryFile(fileID: string) { diff --git a/ui/v2.5/src/docs/en/Changelog/v0190.md b/ui/v2.5/src/docs/en/Changelog/v0190.md index 2adbf142d..ffde96fb9 100644 --- a/ui/v2.5/src/docs/en/Changelog/v0190.md +++ b/ui/v2.5/src/docs/en/Changelog/v0190.md @@ -2,6 +2,7 @@ * Performer autotagging does not currently match on performer aliases. This will be addressed when finer control over the matching is implemented. ### ✨ New Features +* Added URL and Date fields to Images. ([#3015](https://github.com/stashapp/stash/pull/3015)) * Added support for plugins to add injected CSS and Javascript to the UI. ([#3195](https://github.com/stashapp/stash/pull/3195)) * Added disambiguation field to Performers, to differentiate between performers with the same name. ([#3113](https://github.com/stashapp/stash/pull/3113)) diff --git a/ui/v2.5/src/models/list-filter/images.ts b/ui/v2.5/src/models/list-filter/images.ts index 72c9e30d7..7e0e0fce3 100644 --- a/ui/v2.5/src/models/list-filter/images.ts +++ b/ui/v2.5/src/models/list-filter/images.ts @@ -4,6 +4,7 @@ import { createStringCriterionOption, NullNumberCriterionOption, createMandatoryTimestampCriterionOption, + createDateCriterionOption, } from "./criteria/criterion"; import { PerformerFavoriteCriterionOption } from "./criteria/favorite"; import { ImageIsMissingCriterionOption } from "./criteria/is-missing"; @@ -24,6 +25,7 @@ const sortByOptions = [ "o_counter", "filesize", "file_count", + "date", ...MediaSortByOptions, ].map(ListFilterOptions.createSortBy); @@ -44,6 +46,8 @@ const criterionOptions = [ createMandatoryNumberCriterionOption("performer_count"), PerformerFavoriteCriterionOption, StudiosCriterionOption, + createStringCriterionOption("url"), + createDateCriterionOption("date"), createMandatoryNumberCriterionOption("file_count"), createMandatoryTimestampCriterionOption("created_at"), createMandatoryTimestampCriterionOption("updated_at"), diff --git a/ui/v2.5/src/utils/text.ts b/ui/v2.5/src/utils/text.ts index 6e67276e3..df2fbf58a 100644 --- a/ui/v2.5/src/utils/text.ts +++ b/ui/v2.5/src/utils/text.ts @@ -284,6 +284,26 @@ const sanitiseURL = (url?: string, siteURL?: URL) => { return `https://${url}`; }; +const domainFromURL = (urlString?: string, url?: URL) => { + if (url) { + return url.hostname; + } else if (urlString) { + var urlDomain = ""; + try { + var sanitizedUrl = sanitiseURL(urlString); + if (sanitizedUrl) { + urlString = sanitizedUrl; + } + urlDomain = new URL(urlString).hostname; + } catch { + urlDomain = urlString; // We cant determine the hostname so we return the base string + } + return urlDomain; + } else { + return ""; + } +}; + const formatDate = (intl: IntlShape, date?: string, utc = true) => { if (!date) { return ""; @@ -339,6 +359,7 @@ const TextUtils = { bitRate, resolution, sanitiseURL, + domainFromURL, twitterURL, instagramURL, formatDate,