Fix camera layout: proper aspect ratio, centering, and visible ring light
- Camera preview now uses 4:3 aspect ratio (matches photo capture) - Camera is centered both horizontally and vertically - Ring light background properly visible around camera - Controls positioned correctly with safe area handling - Grid overlay now clips to camera bounds - Improved control bar layout with proper spacing
This commit is contained in:
parent
7bada93504
commit
22586d6693
@ -14,31 +14,80 @@ struct RingLightCameraScreen: MCameraScreen {
|
|||||||
let isPremiumUnlocked: Bool
|
let isPremiumUnlocked: Bool
|
||||||
let onSettingsTapped: () -> Void
|
let onSettingsTapped: () -> Void
|
||||||
|
|
||||||
// MARK: - Capture Button Inner Padding
|
// MARK: - Layout Constants
|
||||||
|
|
||||||
|
/// Camera aspect ratio (4:3 for photos)
|
||||||
|
private let cameraAspectRatio: CGFloat = 4.0 / 3.0
|
||||||
|
|
||||||
|
/// Capture button inner padding
|
||||||
private let captureButtonInnerPadding: CGFloat = 8
|
private let captureButtonInnerPadding: CGFloat = 8
|
||||||
|
|
||||||
|
/// Control bar height for layout calculations
|
||||||
|
private let controlBarHeight: CGFloat = 100
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
GeometryReader { geometry in
|
||||||
// Ring light background - fills entire screen
|
let safeArea = geometry.safeAreaInsets
|
||||||
settings.lightColor
|
let availableWidth = geometry.size.width
|
||||||
.ignoresSafeArea()
|
let availableHeight = geometry.size.height
|
||||||
|
|
||||||
// Camera preview with ring padding (from MijickCamera)
|
// Calculate camera size to fit within available space with ring padding
|
||||||
createCameraOutputView()
|
let cameraSize = calculateCameraSize(
|
||||||
.clipShape(RoundedRectangle(cornerRadius: Design.CornerRadius.large))
|
availableWidth: availableWidth - (effectiveRingSize * 2),
|
||||||
.padding(effectiveRingSize)
|
availableHeight: availableHeight - (effectiveRingSize * 2) - controlBarHeight - safeArea.top
|
||||||
|
)
|
||||||
|
|
||||||
// Grid overlay if enabled
|
ZStack {
|
||||||
GridOverlay(isVisible: settings.isGridVisible)
|
// Ring light background - fills entire screen
|
||||||
.padding(effectiveRingSize)
|
settings.lightColor
|
||||||
.allowsHitTesting(false)
|
.ignoresSafeArea()
|
||||||
|
|
||||||
// Controls overlay
|
// Main content
|
||||||
VStack {
|
VStack(spacing: 0) {
|
||||||
topControlBar
|
// Top control bar
|
||||||
Spacer()
|
topControlBar
|
||||||
bottomControlBar
|
.padding(.top, safeArea.top + Design.Spacing.small)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// Camera preview - centered with fixed aspect ratio
|
||||||
|
createCameraOutputView()
|
||||||
|
.frame(width: cameraSize.width, height: cameraSize.height)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: Design.CornerRadius.large))
|
||||||
|
.overlay {
|
||||||
|
// Grid overlay on top of camera
|
||||||
|
if settings.isGridVisible {
|
||||||
|
GridOverlay(isVisible: true)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: Design.CornerRadius.large))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// Bottom control bar
|
||||||
|
bottomControlBar
|
||||||
|
.padding(.bottom, safeArea.bottom + Design.Spacing.medium)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Camera Size Calculation
|
||||||
|
|
||||||
|
/// Calculates camera size maintaining aspect ratio within available space
|
||||||
|
private func calculateCameraSize(availableWidth: CGFloat, availableHeight: CGFloat) -> CGSize {
|
||||||
|
let targetWidth = availableWidth
|
||||||
|
let targetHeight = targetWidth * cameraAspectRatio
|
||||||
|
|
||||||
|
if targetHeight <= availableHeight {
|
||||||
|
// Width-constrained
|
||||||
|
return CGSize(width: targetWidth, height: targetHeight)
|
||||||
|
} else {
|
||||||
|
// Height-constrained
|
||||||
|
let height = availableHeight
|
||||||
|
let width = height / cameraAspectRatio
|
||||||
|
return CGSize(width: width, height: height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +100,8 @@ struct RingLightCameraScreen: MCameraScreen {
|
|||||||
private var maxAllowedRingSize: CGFloat {
|
private var maxAllowedRingSize: CGFloat {
|
||||||
let screenSize = UIScreen.main.bounds.size
|
let screenSize = UIScreen.main.bounds.size
|
||||||
let smallerDimension = min(screenSize.width, screenSize.height)
|
let smallerDimension = min(screenSize.width, screenSize.height)
|
||||||
// Allow ring to take up to 40% of the smaller dimension
|
// Allow ring to take up to 30% of the smaller dimension
|
||||||
return smallerDimension * 0.4
|
return smallerDimension * 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Top Control Bar
|
// MARK: - Top Control Bar
|
||||||
@ -60,66 +109,79 @@ struct RingLightCameraScreen: MCameraScreen {
|
|||||||
private var topControlBar: some View {
|
private var topControlBar: some View {
|
||||||
HStack {
|
HStack {
|
||||||
// Grid toggle
|
// Grid toggle
|
||||||
Button {
|
controlButton(
|
||||||
|
icon: settings.isGridVisible ? "square.grid.3x3.fill" : "square.grid.3x3",
|
||||||
|
accessibilityLabel: "Grid",
|
||||||
|
accessibilityValue: settings.isGridVisible ? "On" : "Off",
|
||||||
|
accessibilityHint: "Toggles the rule of thirds grid overlay"
|
||||||
|
) {
|
||||||
settings.isGridVisible.toggle()
|
settings.isGridVisible.toggle()
|
||||||
} label: {
|
|
||||||
Image(systemName: settings.isGridVisible ? "square.grid.3x3.fill" : "square.grid.3x3")
|
|
||||||
.font(.body)
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.padding(Design.Spacing.small)
|
|
||||||
.background(.ultraThinMaterial, in: Circle())
|
|
||||||
}
|
}
|
||||||
.accessibilityLabel("Grid")
|
|
||||||
.accessibilityValue(settings.isGridVisible ? "On" : "Off")
|
|
||||||
.accessibilityHint("Toggles the rule of thirds grid overlay")
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
Button {
|
controlButton(
|
||||||
|
icon: "gearshape.fill",
|
||||||
|
accessibilityLabel: "Settings",
|
||||||
|
accessibilityHint: "Opens settings"
|
||||||
|
) {
|
||||||
onSettingsTapped()
|
onSettingsTapped()
|
||||||
} label: {
|
|
||||||
Image(systemName: "gearshape.fill")
|
|
||||||
.font(.body)
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.padding(Design.Spacing.small)
|
|
||||||
.background(.ultraThinMaterial, in: Circle())
|
|
||||||
}
|
}
|
||||||
.accessibilityLabel("Settings")
|
|
||||||
.accessibilityHint("Opens settings")
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.large)
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
.padding(.top, Design.Spacing.small)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Bottom Control Bar
|
// MARK: - Bottom Control Bar
|
||||||
|
|
||||||
private var bottomControlBar: some View {
|
private var bottomControlBar: some View {
|
||||||
HStack(spacing: Design.Spacing.xLarge) {
|
HStack {
|
||||||
// Camera flip
|
// Camera flip - left side
|
||||||
Button {
|
controlButton(
|
||||||
|
icon: "arrow.triangle.2.circlepath.camera.fill",
|
||||||
|
size: .title2,
|
||||||
|
accessibilityLabel: "Switch Camera",
|
||||||
|
accessibilityHint: "Switches between front and back camera"
|
||||||
|
) {
|
||||||
Task {
|
Task {
|
||||||
try? await setCameraPosition(cameraPosition == .front ? .back : .front)
|
try? await setCameraPosition(cameraPosition == .front ? .back : .front)
|
||||||
}
|
}
|
||||||
} label: {
|
|
||||||
Image(systemName: "arrow.triangle.2.circlepath.camera.fill")
|
|
||||||
.font(.title2)
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.padding(Design.Spacing.medium)
|
|
||||||
.background(.ultraThinMaterial, in: Circle())
|
|
||||||
}
|
}
|
||||||
.accessibilityLabel("Switch Camera")
|
|
||||||
.accessibilityHint("Switches between front and back camera")
|
|
||||||
|
|
||||||
// Capture button
|
Spacer()
|
||||||
|
|
||||||
|
// Capture button - center
|
||||||
captureButton
|
captureButton
|
||||||
|
|
||||||
// Placeholder for symmetry
|
Spacer()
|
||||||
|
|
||||||
|
// Placeholder for symmetry - right side
|
||||||
Color.clear
|
Color.clear
|
||||||
.frame(width: 44, height: 44)
|
.frame(width: 44, height: 44)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.xLarge)
|
.padding(.horizontal, Design.Spacing.xLarge)
|
||||||
.padding(.bottom, Design.Spacing.large)
|
}
|
||||||
|
|
||||||
|
// MARK: - Control Button Helper
|
||||||
|
|
||||||
|
private func controlButton(
|
||||||
|
icon: String,
|
||||||
|
size: Font = .body,
|
||||||
|
accessibilityLabel: String,
|
||||||
|
accessibilityValue: String? = nil,
|
||||||
|
accessibilityHint: String? = nil,
|
||||||
|
action: @escaping () -> Void
|
||||||
|
) -> some View {
|
||||||
|
Button(action: action) {
|
||||||
|
Image(systemName: icon)
|
||||||
|
.font(size)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.padding(Design.Spacing.small)
|
||||||
|
.background(.ultraThinMaterial, in: Circle())
|
||||||
|
}
|
||||||
|
.accessibilityLabel(accessibilityLabel)
|
||||||
|
.accessibilityValue(accessibilityValue ?? "")
|
||||||
|
.accessibilityHint(accessibilityHint ?? "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Capture Button
|
// MARK: - Capture Button
|
||||||
@ -129,14 +191,17 @@ struct RingLightCameraScreen: MCameraScreen {
|
|||||||
captureOutput()
|
captureOutput()
|
||||||
} label: {
|
} label: {
|
||||||
ZStack {
|
ZStack {
|
||||||
|
// Outer white ring
|
||||||
Circle()
|
Circle()
|
||||||
.fill(.white)
|
.fill(.white)
|
||||||
.frame(width: Design.Capture.buttonSize, height: Design.Capture.buttonSize)
|
.frame(width: Design.Capture.buttonSize, height: Design.Capture.buttonSize)
|
||||||
|
|
||||||
|
// Colored border matching ring light
|
||||||
Circle()
|
Circle()
|
||||||
.strokeBorder(settings.lightColor, lineWidth: Design.LineWidth.thick)
|
.strokeBorder(settings.lightColor, lineWidth: Design.LineWidth.thick)
|
||||||
.frame(width: Design.Capture.buttonSize, height: Design.Capture.buttonSize)
|
.frame(width: Design.Capture.buttonSize, height: Design.Capture.buttonSize)
|
||||||
|
|
||||||
|
// Inner white circle
|
||||||
Circle()
|
Circle()
|
||||||
.fill(.white)
|
.fill(.white)
|
||||||
.frame(
|
.frame(
|
||||||
|
|||||||
@ -5,10 +5,6 @@
|
|||||||
"comment" : "The value of the ring size slider, displayed in parentheses.",
|
"comment" : "The value of the ring size slider, displayed in parentheses.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"%lld%%" : {
|
|
||||||
"comment" : "A text label displaying the current brightness percentage.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"%lldpt" : {
|
"%lldpt" : {
|
||||||
"comment" : "A label displaying the current ring size, formatted as a number followed by the unit \"pt\".",
|
"comment" : "A label displaying the current ring size, formatted as a number followed by the unit \"pt\".",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -65,8 +61,8 @@
|
|||||||
"comment" : "The text for a button that dismisses the current view.",
|
"comment" : "The text for a button that dismisses the current view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Captured boomerang" : {
|
"Capture" : {
|
||||||
"comment" : "A label describing a captured boomerang.",
|
"comment" : "A button that, when tapped, takes a photo.<turn_end>```",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Captured photo" : {
|
"Captured photo" : {
|
||||||
@ -109,10 +105,6 @@
|
|||||||
"comment" : "The text for a button that dismisses a view. In this case, it dismisses the settings view.",
|
"comment" : "The text for a button that dismisses a view. In this case, it dismisses the settings view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Edit" : {
|
|
||||||
"comment" : "Label for the button that allows the user to edit their captured photo or video.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"Front Flash" : {
|
"Front Flash" : {
|
||||||
"comment" : "Title of a toggle in the Settings view that controls whether the front flash is enabled.",
|
"comment" : "Title of a toggle in the Settings view that controls whether the front flash is enabled.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -121,6 +113,10 @@
|
|||||||
"comment" : "The title of the \"Go Pro\" button in the Pro paywall.",
|
"comment" : "The title of the \"Go Pro\" button in the Pro paywall.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"Grid" : {
|
||||||
|
"comment" : "A button that toggles the visibility of a grid overlay in the camera view.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Grid Overlay" : {
|
"Grid Overlay" : {
|
||||||
"comment" : "Text displayed in a settings toggle for showing a grid overlay to help compose your shot.",
|
"comment" : "Text displayed in a settings toggle for showing a grid overlay to help compose your shot.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -152,20 +148,24 @@
|
|||||||
"comment" : "The accessibility value for the grid toggle when it is off.",
|
"comment" : "The accessibility value for the grid toggle when it is off.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"On" : {
|
||||||
|
"comment" : "A label that describes a setting as \"On\".",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Open Source Licenses" : {
|
"Open Source Licenses" : {
|
||||||
"comment" : "A heading displayed above a list of open source licenses used in the app.",
|
"comment" : "A heading displayed above a list of open source licenses used in the app.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"Opens settings" : {
|
||||||
|
"comment" : "A hint describing the action of tapping the settings button.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Opens upgrade options" : {
|
"Opens upgrade options" : {
|
||||||
"comment" : "An accessibility hint for the \"Upgrade to Pro\" button that indicates it opens upgrade options.",
|
"comment" : "An accessibility hint for the \"Upgrade to Pro\" button that indicates it opens upgrade options.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Photo" : {
|
"Photo" : {
|
||||||
|
|
||||||
},
|
|
||||||
"Photo captured" : {
|
|
||||||
"comment" : "Voiceover announcement when a photo is captured.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
},
|
||||||
"Premium color" : {
|
"Premium color" : {
|
||||||
"comment" : "An accessibility hint for a premium color option in the color preset button.",
|
"comment" : "An accessibility hint for a premium color option in the color preset button.",
|
||||||
@ -191,10 +191,6 @@
|
|||||||
"comment" : "Title for a button that allows the user to retake a captured photo or video.",
|
"comment" : "Title for a button that allows the user to retake a captured photo or video.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Ring Glow" : {
|
|
||||||
"comment" : "Title of a slider that controls the intensity of the ring glow effect in the captured media.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"Ring size" : {
|
"Ring size" : {
|
||||||
"comment" : "An accessibility label for the ring size slider in the settings view.",
|
"comment" : "An accessibility label for the ring size slider in the settings view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -203,6 +199,10 @@
|
|||||||
"comment" : "The label for the ring size slider in the settings view.",
|
"comment" : "The label for the ring size slider in the settings view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"Save" : {
|
||||||
|
"comment" : "Title for a button that saves the currently captured photo or video to the user's photo library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Saved to Photos" : {
|
"Saved to Photos" : {
|
||||||
"comment" : "Text shown as a toast message when a photo is successfully saved to Photos.",
|
"comment" : "Text shown as a toast message when a photo is successfully saved to Photos.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -242,9 +242,6 @@
|
|||||||
"Skin Smoothing" : {
|
"Skin Smoothing" : {
|
||||||
"comment" : "A toggle that enables or disables real-time skin smoothing.",
|
"comment" : "A toggle that enables or disables real-time skin smoothing.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
|
||||||
"Smoothing" : {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"Soft Pink" : {
|
"Soft Pink" : {
|
||||||
"comment" : "Name of a ring light color preset.",
|
"comment" : "Name of a ring light color preset.",
|
||||||
@ -262,6 +259,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Switch Camera" : {
|
||||||
|
"comment" : "A button that switches between the front and back camera.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Switches between front and back camera" : {
|
||||||
|
"comment" : "A hint that describes the functionality of the \"Switch Camera\" button in the camera screen.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Sync Now" : {
|
"Sync Now" : {
|
||||||
"comment" : "A button label that triggers a sync action.",
|
"comment" : "A button label that triggers a sync action.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -280,11 +285,19 @@
|
|||||||
},
|
},
|
||||||
"Syncing..." : {
|
"Syncing..." : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Takes a photo" : {
|
||||||
|
"comment" : "An accessibility hint for the capture button.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Third-party libraries used in this app" : {
|
"Third-party libraries used in this app" : {
|
||||||
"comment" : "A description of the third-party libraries used in this app.",
|
"comment" : "A description of the third-party libraries used in this app.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"Toggles the rule of thirds grid overlay" : {
|
||||||
|
"comment" : "An accessibility hint for the grid toggle button in the top control bar of the ring light camera screen.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"True Mirror" : {
|
"True Mirror" : {
|
||||||
"comment" : "Title of a toggle in the settings view that allows the user to flip the camera preview.",
|
"comment" : "Title of a toggle in the settings view that allows the user to flip the camera preview.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -293,10 +306,6 @@
|
|||||||
"comment" : "Description of a benefit that comes with the Pro subscription, specifically related to the boomerang tool.",
|
"comment" : "Description of a benefit that comes with the Pro subscription, specifically related to the boomerang tool.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Unlock filters, AI remove, and more with Pro" : {
|
|
||||||
"comment" : "A teaser text that appears below the capture edit view, promoting a premium feature.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"Unlock premium colors, video, and more" : {
|
"Unlock premium colors, video, and more" : {
|
||||||
"comment" : "A description of the benefits of upgrading to the Pro version of the app.",
|
"comment" : "A description of the benefits of upgrading to the Pro version of the app.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -313,10 +322,6 @@
|
|||||||
"comment" : "Display name for the \"Video\" capture mode.",
|
"comment" : "Display name for the \"Video\" capture mode.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Video saved" : {
|
|
||||||
"comment" : "Voiceover text announced when a video is successfully saved to the user's Photos library.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"View on GitHub" : {
|
"View on GitHub" : {
|
||||||
"comment" : "A button label that says \"View on GitHub\".",
|
"comment" : "A button label that says \"View on GitHub\".",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user