mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
1979be1f85
commit
8634def6a8
@ -5392,6 +5392,9 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Stats.LoadingTitle" = "Preparing stats";
|
||||
"Stats.LoadingText" = "Please wait a few moments while\nwe generate your stats";
|
||||
|
||||
"Stats.ZoomOut" = "Zoom Out";
|
||||
"Stats.Total" = "Total";
|
||||
|
||||
"InstantPage.Views_0" = "%@ views";
|
||||
"InstantPage.Views_1" = "%@ view";
|
||||
"InstantPage.Views_2" = "%@ views";
|
||||
@ -5471,3 +5474,10 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"MuteFor.Forever" = "Mute Forever";
|
||||
|
||||
"Conversation.Dice" = "Send a 🎲 emoji to any chat to get a random number from Telegram.";
|
||||
|
||||
"ForwardedDices_1" = "Forwarded dice";
|
||||
"ForwardedDices_2" = "2 forwarded dices";
|
||||
"ForwardedDices_3_10" = "%@ forwarded dices";
|
||||
"ForwardedDices_any" = "%@ forwarded dices";
|
||||
"ForwardedDices_many" = "%@ forwarded dices";
|
||||
"ForwardedDices_0" = "%@ forwarded dices";
|
||||
|
@ -182,7 +182,7 @@ public class BaseChartController: ChartThemeContainer {
|
||||
fatalError("Abstract")
|
||||
}
|
||||
|
||||
public func apply(theme: ChartTheme, animated: Bool) {
|
||||
public func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ class GeneralChartComponentController: ChartThemeContainer {
|
||||
var isZoomable = true
|
||||
|
||||
var theme: ChartTheme = ChartTheme.defaultDayTheme
|
||||
var strings: ChartStrings = ChartStrings.defaultStrings
|
||||
var totalHorizontalRange: ClosedRange<CGFloat> = BaseConstants.defaultRange
|
||||
var totalVerticalRange: ClosedRange<CGFloat> = BaseConstants.defaultRange
|
||||
var initialHorizontalRange: ClosedRange<CGFloat> = BaseConstants.defaultRange
|
||||
@ -234,8 +235,9 @@ class GeneralChartComponentController: ChartThemeContainer {
|
||||
var setDetailsViewModel: ((ChartDetailsViewModel, Bool, Bool) -> Void)?
|
||||
var chartRangePagingClosure: ((Bool, CGFloat) -> Void)? // isEnabled, PageSize
|
||||
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
@ -248,7 +248,7 @@ public class GeneralLinesChartController: BaseLinesChartController {
|
||||
}
|
||||
}
|
||||
|
||||
override public func apply(theme: ChartTheme, animated: Bool) {
|
||||
override public func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalScalesRenderer.axisXColor = theme.chartStrongLinesColor
|
||||
|
@ -310,7 +310,7 @@ public class TwoAxisLinesChartController: BaseLinesChartController {
|
||||
}
|
||||
}
|
||||
|
||||
public override func apply(theme: ChartTheme, animated: Bool) {
|
||||
public override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalLineRenderer.linesColor = theme.chartStrongLinesColor
|
||||
|
||||
|
@ -192,8 +192,8 @@ class PercentChartComponentController: GeneralChartComponentController {
|
||||
verticalLineRenderer.isEnabled = false
|
||||
}
|
||||
|
||||
override func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
|
@ -291,11 +291,11 @@ public class PercentPieChartController: BaseChartController {
|
||||
}
|
||||
}
|
||||
|
||||
public override func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
public override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
pieController.apply(theme: theme, animated: animated)
|
||||
percentController.apply(theme: theme, animated: animated)
|
||||
pieController.apply(theme: theme, strings: strings, animated: animated)
|
||||
percentController.apply(theme: theme, strings: strings, animated: animated)
|
||||
transitionRenderer.backgroundColor = theme.chartBackgroundColor
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ class BarsComponentController: GeneralChartComponentController {
|
||||
let horizontalScalesRenderer: HorizontalScalesRenderer
|
||||
let verticalScalesRenderer: VerticalScalesRenderer
|
||||
|
||||
let lineBulletsRenderer = LineBulletsRenderer()
|
||||
let verticalLineRenderer = VerticalLinesRenderer()
|
||||
|
||||
let previewBarsChartRenderer: BarChartRenderer
|
||||
private(set) var barsWidth: CGFloat = 1
|
||||
|
||||
@ -37,6 +40,8 @@ class BarsComponentController: GeneralChartComponentController {
|
||||
self.previewBarsChartRenderer = previewBarsChartRenderer
|
||||
self.step = step
|
||||
|
||||
self.lineBulletsRenderer.isEnabled = false
|
||||
|
||||
self.mainBarsRenderer.optimizationLevel = BaseConstants.barsChartOptimizationLevel
|
||||
self.previewBarsChartRenderer.optimizationLevel = BaseConstants.barsChartOptimizationLevel
|
||||
|
||||
@ -161,6 +166,7 @@ class BarsComponentController: GeneralChartComponentController {
|
||||
mainBarsRenderer.setup(verticalRange: verticalRange, animated: animated)
|
||||
horizontalScalesRenderer.setup(verticalRange: verticalRange, animated: animated)
|
||||
verticalScalesRenderer.setup(verticalRange: verticalRange, animated: animated)
|
||||
lineBulletsRenderer.setup(verticalRange: verticalRange, animated: animated)
|
||||
}
|
||||
|
||||
public override func updateChartsVisibility(visibility: [Bool], animated: Bool) {
|
||||
@ -188,7 +194,7 @@ class BarsComponentController: GeneralChartComponentController {
|
||||
}
|
||||
if !self.step {
|
||||
viewModel.totalValue = ChartDetailsViewModel.Value(prefix: nil,
|
||||
title: "Total",
|
||||
title: self.strings.total,
|
||||
value: BaseConstants.detailsNumberFormatter.string(from: totalSumm),
|
||||
color: .white,
|
||||
visible: visibleChartValues.count > 1)
|
||||
@ -217,8 +223,8 @@ class BarsComponentController: GeneralChartComponentController {
|
||||
|
||||
mainBarsRenderer.setSelectedIndex(nil, animated: animated)
|
||||
}
|
||||
override func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
@ -239,4 +245,36 @@ class BarsComponentController: GeneralChartComponentController {
|
||||
self.setChartTitleClosure?(stirng, animated)
|
||||
}
|
||||
}
|
||||
|
||||
override func chartInteractionDidBegin(point: CGPoint, manual: Bool = true) {
|
||||
if manual && !isChartInteracting && detailsVisible {
|
||||
self.hideDetailsView(animated: true)
|
||||
ignoreInteraction = true
|
||||
return
|
||||
}
|
||||
let chartFrame = self.chartFrame()
|
||||
guard chartFrame.width > 0 else { return }
|
||||
let horizontalRange = currentHorizontalMainChartRange
|
||||
let dateToFind = Date(timeIntervalSince1970: TimeInterval(horizontalRange.distance * point.x + horizontalRange.lowerBound))
|
||||
guard let (closestDate, minIndex) = findClosestDateTo(dateToFind: dateToFind) else { return }
|
||||
|
||||
let chartWasInteracting = isChartInteractionBegun
|
||||
lastChartInteractionPoint = point
|
||||
isChartInteractionBegun = true
|
||||
isChartInteracting = true
|
||||
|
||||
let chartValue: CGFloat = CGFloat(closestDate.timeIntervalSince1970)
|
||||
var chartValueUpdated = true
|
||||
if chartValue == currentChartValue {
|
||||
chartValueUpdated = false
|
||||
}
|
||||
currentChartValue = chartValue
|
||||
let detailsViewPosition = (chartValue - horizontalRange.lowerBound) / horizontalRange.distance * chartFrame.width + chartFrame.minX
|
||||
showDetailsView(at: chartValue, detailsViewPosition: detailsViewPosition, dataIndex: minIndex, date: closestDate, animated: chartWasInteracting, feedback: chartWasInteracting && chartValueUpdated)
|
||||
|
||||
super.chartInteractionDidBegin(point: point)
|
||||
|
||||
self.verticalLineRenderer.values = [chartValue]
|
||||
// self.verticalLineRenderer.offset = barOffset
|
||||
}
|
||||
}
|
||||
|
@ -243,11 +243,11 @@ public class DailyBarsChartController: BaseChartController {
|
||||
}
|
||||
}
|
||||
|
||||
override public func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
override public func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
linesController.apply(theme: theme, animated: animated)
|
||||
barsController.apply(theme: theme, animated: animated)
|
||||
linesController.apply(theme: theme, strings: strings, animated: animated)
|
||||
barsController.apply(theme: theme, strings: strings, animated: animated)
|
||||
}
|
||||
|
||||
public override var drawChartVisibity: Bool {
|
||||
|
@ -202,8 +202,8 @@ class LinesComponentController: GeneralChartComponentController {
|
||||
lineBulletsRenderer.isEnabled = false
|
||||
}
|
||||
|
||||
override func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
|
@ -249,10 +249,10 @@ public class StackedBarsChartController: BaseChartController {
|
||||
}
|
||||
}
|
||||
|
||||
public override func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
public override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
zoomedBarsController.apply(theme: theme, animated: animated)
|
||||
barsController.apply(theme: theme, animated: animated)
|
||||
zoomedBarsController.apply(theme: theme, strings: strings, animated: animated)
|
||||
barsController.apply(theme: theme, strings: strings, animated: animated)
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ public class StepBarsChartController: BaseChartController {
|
||||
zoomedBarsController.mainBarsRenderer,
|
||||
barsController.horizontalScalesRenderer,
|
||||
barsController.verticalScalesRenderer,
|
||||
barsController.lineBulletsRenderer,
|
||||
barsController.verticalLineRenderer
|
||||
// performanceRenderer
|
||||
]
|
||||
}
|
||||
@ -257,10 +259,10 @@ public class StepBarsChartController: BaseChartController {
|
||||
}
|
||||
}
|
||||
|
||||
public override func apply(theme: ChartTheme, animated: Bool) {
|
||||
super.apply(theme: theme, animated: animated)
|
||||
public override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
super.apply(theme: theme, strings: strings, animated: animated)
|
||||
|
||||
zoomedBarsController.apply(theme: theme, animated: animated)
|
||||
barsController.apply(theme: theme, animated: animated)
|
||||
zoomedBarsController.apply(theme: theme, strings: strings, animated: animated)
|
||||
barsController.apply(theme: theme, strings: strings, animated: animated)
|
||||
}
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ public class TwoAxisStepBarsChartController: BaseLinesChartController {
|
||||
}
|
||||
}
|
||||
|
||||
public override func apply(theme: ChartTheme, animated: Bool) {
|
||||
public override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
|
||||
verticalLineRenderer.linesColor = theme.chartStrongLinesColor
|
||||
|
||||
|
@ -17,7 +17,7 @@ class ChartDetailsRenderer: BaseChartRenderer, ChartThemeContainer {
|
||||
private lazy var colorAnimator = AnimationController<CGFloat>(current: 1, refreshClosure: refreshClosure)
|
||||
private var fromTheme: ChartTheme = ChartTheme.defaultDayTheme
|
||||
private var currentTheme: ChartTheme = ChartTheme.defaultDayTheme
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
fromTheme = currentTheme
|
||||
currentTheme = theme
|
||||
colorAnimator.set(current: 1)
|
||||
|
@ -24,7 +24,19 @@ typealias NSEdgeInsets = UIEdgeInsets
|
||||
#endif
|
||||
|
||||
public protocol ChartThemeContainer {
|
||||
func apply(theme: ChartTheme, animated: Bool)
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool)
|
||||
}
|
||||
|
||||
public class ChartStrings {
|
||||
public let zoomOut: String
|
||||
public let total: String
|
||||
|
||||
public init(zoomOut: String, total: String) {
|
||||
self.zoomOut = zoomOut
|
||||
self.total = total
|
||||
}
|
||||
|
||||
public static var defaultStrings = ChartStrings(zoomOut: "Zoom Out", total: "Total")
|
||||
}
|
||||
|
||||
public class ChartTheme {
|
||||
|
@ -211,7 +211,7 @@ class ChartDetailsView: UIControl {
|
||||
}
|
||||
|
||||
extension ChartDetailsView: ChartThemeContainer {
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
self.theme = theme
|
||||
self.titleLabel.setTextColor(theme.chartDetailsTextColor, animated: animated)
|
||||
if let viewModel = self.viewModel {
|
||||
|
@ -128,8 +128,8 @@ public final class ChartNode: ASDisplayNode {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func setupTheme(_ theme: ChartTheme) {
|
||||
self.chartView.apply(theme: theme, animated: false)
|
||||
public func setup(theme: ChartTheme, strings: ChartStrings) {
|
||||
self.chartView.apply(theme: theme, strings: strings, animated: false)
|
||||
}
|
||||
|
||||
public func setup(controller: BaseChartController) {
|
||||
|
@ -34,6 +34,7 @@ class ChartStackSection: UIView, ChartThemeContainer {
|
||||
|
||||
var controller: BaseChartController?
|
||||
var theme: ChartTheme?
|
||||
var strings: ChartStrings?
|
||||
|
||||
var displayRange: Bool = true
|
||||
|
||||
@ -90,8 +91,11 @@ class ChartStackSection: UIView, ChartThemeContainer {
|
||||
controller?.cancelChartInteraction()
|
||||
}
|
||||
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
|
||||
self.backButton.setTitle(strings.zoomOut, for: .normal)
|
||||
|
||||
UIView.perform(animated: animated && self.isVisibleInWindow) {
|
||||
self.sectionContainerView.backgroundColor = theme.chartBackgroundColor
|
||||
@ -105,16 +109,16 @@ class ChartStackSection: UIView, ChartThemeContainer {
|
||||
|
||||
if rangeView.isVisibleInWindow || chartView.isVisibleInWindow {
|
||||
chartView.loadDetailsViewIfNeeded()
|
||||
chartView.apply(theme: theme, animated: animated && chartView.isVisibleInWindow)
|
||||
controller?.apply(theme: theme, animated: animated)
|
||||
rangeView.apply(theme: theme, animated: animated && rangeView.isVisibleInWindow)
|
||||
chartView.apply(theme: theme, strings: strings, animated: animated && chartView.isVisibleInWindow)
|
||||
controller?.apply(theme: theme, strings: strings, animated: animated)
|
||||
rangeView.apply(theme: theme, strings: strings, animated: animated && rangeView.isVisibleInWindow)
|
||||
} else {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval.random(in: 0...0.1)) {
|
||||
self.chartView.loadDetailsViewIfNeeded()
|
||||
|
||||
self.controller?.apply(theme: theme, animated: false)
|
||||
self.chartView.apply(theme: theme, animated: false)
|
||||
self.rangeView.apply(theme: theme, animated: false)
|
||||
self.controller?.apply(theme: theme, strings: strings, animated: false)
|
||||
self.chartView.apply(theme: theme, strings: strings, animated: false)
|
||||
self.rangeView.apply(theme: theme, strings: strings, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,8 +178,8 @@ class ChartStackSection: UIView, ChartThemeContainer {
|
||||
self.controller = controller
|
||||
self.displayRange = displayRange
|
||||
|
||||
if let theme = self.theme {
|
||||
controller.apply(theme: theme, animated: false)
|
||||
if let theme = self.theme, let strings = self.strings {
|
||||
controller.apply(theme: theme, strings: strings, animated: false)
|
||||
}
|
||||
|
||||
self.chartView.renderers = controller.mainChartRenderers
|
||||
|
@ -165,7 +165,7 @@ class ChartView: UIControl {
|
||||
|
||||
|
||||
extension ChartView: ChartThemeContainer {
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
detailsView?.apply(theme: theme, animated: animated && (detailsView?.isVisibleInWindow ?? false))
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
detailsView?.apply(theme: theme, strings: strings, animated: animated && (detailsView?.isVisibleInWindow ?? false))
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ class ChartVisibilityView: UIView {
|
||||
}
|
||||
|
||||
extension ChartVisibilityView: ChartThemeContainer {
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
UIView.perform(animated: animated) {
|
||||
self.backgroundColor = theme.chartBackgroundColor
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ private extension RangeChartView {
|
||||
}
|
||||
|
||||
extension RangeChartView: ChartThemeContainer {
|
||||
func apply(theme: ChartTheme, animated: Bool) {
|
||||
func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
|
||||
let closure = {
|
||||
self.lowerBoundTintView.backgroundColor = theme.rangeViewTintColor
|
||||
self.upperBoundTintView.backgroundColor = theme.rangeViewTintColor
|
||||
|
@ -442,6 +442,7 @@ public func channelStatsController(context: AccountContext, peerId: PeerId, cach
|
||||
if let statsContext = statsContext, let stats = stats {
|
||||
if case .OnDemand = stats.interactionsGraph {
|
||||
statsContext.loadInteractionsGraph()
|
||||
statsContext.loadMuteGraph()
|
||||
statsContext.loadTopHoursGraph()
|
||||
statsContext.loadNewFollowersBySourceGraph()
|
||||
statsContext.loadViewsBySourceGraph()
|
||||
|
@ -260,7 +260,7 @@ class StatsGraphItemNode: ListViewItemNode {
|
||||
strongSelf.activityIndicator.type = .custom(item.presentationData.theme.list.itemSecondaryTextColor, 16.0, 2.0, false)
|
||||
|
||||
if let updatedTheme = updatedTheme {
|
||||
strongSelf.chartNode.setupTheme(ChartTheme(presentationTheme: updatedTheme))
|
||||
strongSelf.chartNode.setup(theme: ChartTheme(presentationTheme: updatedTheme), strings: ChartStrings(zoomOut: item.presentationData.strings.Stats_ZoomOut, total: item.presentationData.strings.Stats_Total))
|
||||
}
|
||||
|
||||
if let updatedGraph = updatedGraph {
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -635,11 +635,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
case let .result(_, items, _):
|
||||
var diceStickers: [StickerPackItem] = []
|
||||
for case let item as StickerPackItem in items {
|
||||
if let emoji = item.getStringRepresentationsOfIndexKeys().first {
|
||||
diceStickers.append(item)
|
||||
}
|
||||
diceStickers.append(item)
|
||||
}
|
||||
animatedEmojiStickers["🎲".strippedEmoji] = diceStickers
|
||||
animatedEmojiStickers["dice"] = diceStickers
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
if let _ = self.telegramDice {
|
||||
if let diceEmojis = item.associatedData.animatedEmojiStickers["🎲"] {
|
||||
if let diceEmojis = item.associatedData.animatedEmojiStickers["dice"] {
|
||||
let animationNode = ManagedDiceAnimationNode(context: item.context, emojis: diceEmojis.map { $0.file })
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ func textStringForForwardedMessage(_ message: Message, strings: PresentationStri
|
||||
return ("", true)
|
||||
case _ as TelegramMediaPoll:
|
||||
return (strings.ForwardedPolls(1), true)
|
||||
case _ as TelegramMediaDice:
|
||||
return (strings.ForwardedDices(1), true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user