featureparity 0.0.23 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3fd282368d3fb2417adaf990518352726bc46fe816d70e73ec04f57e2a5ea067
4
- data.tar.gz: 7edf6bda3e44e4b8cce99efbe59d1adff0cbfe9ab2b8c24613a4b0eaf456cb7f
3
+ metadata.gz: 4e68b70dce16d28e928ead4413623002ccda97f4508f62bbe6d980c8bb29a21c
4
+ data.tar.gz: e630a9bd6f49db70c343f6374cd3c049a9c9fb4b786251dd313b76a177e0a1f9
5
5
  SHA512:
6
- metadata.gz: 83b7560fa29083b77581005c176b3c01f9a2d60201616cbb97dc8b60ede719f8029b4a02b7d5012310a3a57ef6cd642bd21af32344c5e42c8c300ecb76617356
7
- data.tar.gz: 1d2b3bf3e6500b1bed84696588225ad718f3aaa9016ffbeffc410226c631c8e55fb48cdafa30446f41814ac2869eb166b98e10fbb21d00b7fb8c2a4d73731802
6
+ metadata.gz: 55bde2507b4cfccbe49b8d799d0b3e7efba22c0298450e823a3ac86299ec4d392b0e51f13c3c643a2abc1d60f1d68d091074420c0928fbbcf9da6947fea296d1
7
+ data.tar.gz: c5d9754313a59721bfc78f5d4e65d4924ef1f5120659037cace19e6ee816a96dea0e467f47ad285cf03003ba37ed836501754c5592f9b22385f5a52315be5cae
data/lib/fp/client.rb CHANGED
@@ -108,16 +108,17 @@ module Fp
108
108
  end
109
109
 
110
110
  # POST /projects/:id/showroom/media/presign
111
- def presign_showroom_media(project_id, filename:, content_type:, size_bytes:, surface_id: nil)
111
+ def presign_showroom_media(project_id, filename:, content_type:, size_bytes:, surface_id: nil, purpose: nil)
112
112
  body = { filename: filename, content_type: content_type, size_bytes: size_bytes }
113
113
  body[:surface_id] = surface_id if surface_id
114
+ body[:purpose] = purpose if purpose
114
115
  post("/api/projects/#{project_id}/showroom/media/presign", body)
115
116
  end
116
117
 
117
118
  # POST /projects/:id/showroom/media
118
119
  # Confirm an upload and create the media record.
119
120
  def create_showroom_media(project_id, media_id:, s3_key:, filename:, content_type:, size_bytes:,
120
- surface_id: nil, caption: nil, position: nil)
121
+ surface_id: nil, caption: nil, position: nil, purpose: nil)
121
122
  body = {
122
123
  media_id: media_id,
123
124
  s3_key: s3_key,
@@ -128,6 +129,7 @@ module Fp
128
129
  body[:surface_id] = surface_id if surface_id
129
130
  body[:caption] = caption if caption
130
131
  body[:position] = position if position
132
+ body[:purpose] = purpose if purpose
131
133
  post("/api/projects/#{project_id}/showroom/media", body)
132
134
  end
133
135
 
@@ -23,6 +23,7 @@ module Fp
23
23
  help_usage 'fp showroom --project <slug>',
24
24
  'fp showroom media --project <slug>',
25
25
  'fp showroom media add <file> --project <slug> [--surface <key>] [--caption "..."] [--position N]',
26
+ 'fp showroom media add <image> --project <slug> --surface <key> --icon',
26
27
  'fp showroom media edit <id> --project <slug> [--caption "..."] [--position N]',
27
28
  'fp showroom media remove <id> --project <slug> [--yes]'
28
29
  help_flag :project, 'Project slug (falls back to .featureparity.yml)'
@@ -33,6 +34,7 @@ module Fp
33
34
  help_example 'fp showroom --project stowzilla'
34
35
  help_example 'fp showroom media add e2e/checkout.webm --project stowzilla --surface web --caption "Checking out"'
35
36
  help_example 'fp showroom media add overview.png --project stowzilla --position 1'
37
+ help_example 'fp showroom media add app-icon.png --project stowzilla --surface ios --icon'
36
38
  help_example 'fp showroom media edit <id> --project stowzilla --caption ""'
37
39
  help_example 'fp showroom media remove <id> --project stowzilla --yes'
38
40
  help_note "Prints the welcome note, the gallery, and each surface's card, then the surfaces that are NOT shown and why.\n" \
@@ -7,6 +7,7 @@ module Fp
7
7
  module Commands
8
8
  # fp showroom media [--project <slug>] - list the showroom's media
9
9
  # fp showroom media add <file> [--surface <key>] [--caption] [--position N]
10
+ # fp showroom media add <image> --surface <key> --icon - set the surface's icon
10
11
  # fp showroom media edit <id> [--caption "..."] [--position N]
11
12
  # fp showroom media remove <id> [--yes]
12
13
  #
@@ -19,13 +20,19 @@ module Fp
19
20
  # Uploads reuse attach's flow (presign, PUT the bytes, confirm) and its visual file
20
21
  # types — images and videos only. SVG can carry script and PDFs don't render inline,
21
22
  # so both are refused before anything is sent.
23
+ #
24
+ # `--icon` (Fizzy #1499) makes the image the surface's own icon in place of the
25
+ # built-in platform logo: one per surface, a new one replaces the old, and removing
26
+ # it brings the built-in logo back.
22
27
  class ShowroomMedia < Base
23
28
  CONTENT_TYPES = Attach::CONTENT_TYPES.slice(*Attach::VISUAL_EXTENSIONS).freeze
24
29
  MAX_FILE_SIZE = Attach::MAX_FILE_SIZE
25
30
  MAX_CAPTION_LENGTH = 500
31
+ ICON_EXTENSIONS = %w[png jpg jpeg gif webp].freeze
32
+ MAX_ICON_SIZE = 5 * 1024 * 1024
26
33
 
27
34
  LIST_FLAGS = { project: :string }.freeze
28
- ADD_FLAGS = { project: :string, surface: :string, caption: :string, position: :string }.freeze
35
+ ADD_FLAGS = { project: :string, surface: :string, caption: :string, position: :string, icon: :boolean }.freeze
29
36
  EDIT_FLAGS = { project: :string, caption: :string, position: :string }.freeze
30
37
  REMOVE_FLAGS = { project: :string, yes: :boolean }.freeze
31
38
 
@@ -83,6 +90,7 @@ module Fp
83
90
 
84
91
  # Everything that can be checked locally is checked before a byte is sent.
85
92
  file = prepare_file!(path)
93
+ check_icon!(file, opts) if opts[:icon]
86
94
  caption = flag_text(opts[:caption]).strip if opts.key?(:caption)
87
95
  caption = nil if caption&.empty?
88
96
  check_caption!(caption) if caption
@@ -95,11 +103,12 @@ module Fp
95
103
  require_project!(opts)
96
104
  project_id = resolve_project_id(opts[:project])
97
105
  surface = opts[:surface] ? find_surface!(project_id, opts[:surface]) : nil
106
+ purpose = opts[:icon] ? 'icon' : nil
98
107
 
99
108
  presign = fetch!(client.presign_showroom_media(
100
109
  project_id,
101
110
  filename: file[:filename], content_type: file[:content_type],
102
- size_bytes: file[:size], surface_id: surface&.dig('id')
111
+ size_bytes: file[:size], surface_id: surface&.dig('id'), purpose: purpose
103
112
  ), prefix: 'Failed to get upload URL')['presign'] || {}
104
113
 
105
114
  uploaded = client.upload_to_presigned_url(presign['upload_url'], file[:path], file[:content_type])
@@ -109,13 +118,19 @@ module Fp
109
118
  project_id,
110
119
  media_id: presign['media_id'], s3_key: presign['s3_key'],
111
120
  filename: file[:filename], content_type: file[:content_type], size_bytes: file[:size],
112
- surface_id: surface&.dig('id'), caption: caption, position: position
121
+ surface_id: surface&.dig('id'), caption: caption, position: position, purpose: purpose
113
122
  ), prefix: 'Failed to confirm upload')['media'] || {}
114
123
 
115
124
  where = surface ? "surface '#{surface['key']}'" : 'the project gallery'
116
125
  output.success({ media: item, surface: surface&.dig('key'), project: opts[:project] }) do
117
- puts "Added #{file[:filename]} to #{where} in project '#{opts[:project]}'."
118
- print_item_detail(item)
126
+ if purpose
127
+ puts "Set #{file[:filename]} as the icon for #{where} in project '#{opts[:project]}'."
128
+ puts "Remove it to go back to the built-in logo: fp showroom media remove #{item['id']} " \
129
+ "--project #{opts[:project]}"
130
+ else
131
+ puts "Added #{file[:filename]} to #{where} in project '#{opts[:project]}'."
132
+ print_item_detail(item)
133
+ end
119
134
  end
120
135
  end
121
136
 
@@ -239,6 +254,22 @@ module Fp
239
254
  { path: path, filename: filename, content_type: content_type, size: size }
240
255
  end
241
256
 
257
+ # An icon is a still picture on one surface, with nothing to caption or order.
258
+ def check_icon!(file, opts)
259
+ problem =
260
+ if flag_text(opts[:surface]).strip.empty? then '--icon needs --surface <key>: an icon belongs to a surface.'
261
+ elsif opts.key?(:caption) || opts.key?(:position) then '--icon takes no --caption or --position.'
262
+ elsif !ICON_EXTENSIONS.include?(File.extname(file[:filename]).delete('.').downcase)
263
+ 'An icon must be an image (png, jpg, jpeg, gif, webp).'
264
+ elsif file[:size] > MAX_ICON_SIZE
265
+ "Icon too large: #{format_size(file[:size])} (max #{format_size(MAX_ICON_SIZE)})"
266
+ end
267
+ return unless problem
268
+
269
+ output.error(problem)
270
+ exit 1
271
+ end
272
+
242
273
  def check_caption!(caption)
243
274
  return if caption.length <= MAX_CAPTION_LENGTH
244
275
 
@@ -305,6 +336,7 @@ module Fp
305
336
 
306
337
  def print_item_line(item)
307
338
  position = item['position'].to_i.positive? ? "##{item['position'].to_i}" : '-'
339
+ position = 'icon' if item['purpose'] == 'icon'
308
340
  caption = item['caption'].to_s.strip
309
341
  line = " #{item['id']} #{position.ljust(3)} #{item['media_kind'].to_s.ljust(5)} #{item['filename']}"
310
342
  line += " \"#{caption}\"" unless caption.empty?
data/lib/fp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fp
4
- VERSION = '0.0.23'
4
+ VERSION = '0.0.24'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: featureparity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.23
4
+ version: 0.0.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla