Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
captain
8 лет назад

[Шаг 4. Рефакторинг. Просмотр постов] Клиент GOLOS для Android/iOS на Delphi.

Сегодня будет немного лирики. Оказалось, что малыш дома, это фактор, который здорово тормозит работу, особенно ту, которая на общественных началах. Она в списке приоритетов уходит далеко на второй план. Этим, отчасти, и вызвана некоторая пауза между нашими уроками.

И второй момент, как оказалось golos здорово заточен под Web, а вот Delphi не очень. Если говорить проще, то просто тормозит она безбожно в некоторых моментах. Всякие веб-решения гораздо легче и производительнее. Так я пока что работаю над фоновой загрузкой картинок к постам, и это не войдет в наш сегодняшний урок. Ну и многие другие ограничения пришлось преодолеть. Как любят говорить математики "отсюда легко видеть, что...". Так вот в итоге все легко, но иногда к этому решению приходится идти окольными путями.

Ну а теперь и про рефакторинг. То что мы набросали сначала, позволило нам запустить приложение, но имело отвратительную структуру и не поддавалось дальнейшему расширению. Поэтому мы с вами проведем некоторую реорганизацию кода, которая будет заключаться вот в чем:

  • У нас появятся процедуры GetNewRecords и т.п., которые будут отвечать за выбор нужных постов из БД
  • У нас появится процедура AddItems, которая очистит ленту и добавит в нее новые посты, полученные предыдущими процедурами
  • У нас будет процедура LoadComment, которая загрузит выбранный пост по щелчку на нем в ленте постов в ListView
  • Процедура GetRecordImage работает с метаданными поста и позволяет получить ссылку на его картинку
  • Мы переработали вывод записей в ленту

А теперь подробнее. Мы добавили новую закладку в наш TabControl и разметили на ней компонент AniIndicator, он показывает крутящееся нечто, в момент когда программа занята. Мы используем его сдедующим образом. Открываем этот "экран", запускаем затратную по времени операцию. В операции не забываем обрабатывать поток сообщений при помощи Application.ProcessMessages. После выполнения операции возвращаемся к основному экрану.
Кроме того, загрузка начального списка записей вынесена из FormCreate в FormActivate, как раз для того, чтобы после запуска у нас не получилось так что программа долго тупит и ничего не показывает.

Добавили закладку в наш TabControl и разметили на ней компонент WebBrowser. Туда будем грузить сам пост и комментарии к нему.

Обработчик выпадающего списка, где мы выбираем какую ленту постов грузить, теперь выглядит вот так:

procedure THeaderFooterwithNavigation.ThemeBoxChange(Sender: TObject);
begin
  try
    TabControl1.ActiveTab := xAnimation;
    ListView.Items.Clear;
    case ThemeBox.Selected.Index of
      0: GetNewRecordsAction.Execute;
      1: GetPopularRecordsAction.Execute;
    end;
  finally
    TabControl1.ActiveTab := TabItem1;
  end;
end;

по мере добавления новых режимов просто будем добавлять туда новые кейсы.

Вот так теперь выглядит выбор новых постов, обратите внимание, как изменился запрос к БД.

procedure THeaderFooterwithNavigation.GetNewRecords;
begin
  if UniConnection.Connected then
  begin
    UniQuery.SQL.Clear;
    UniQuery.SQL.Add('SELECT top(50) author, title, parent_permlink, permlink,  MIN(tx_id) AS tx_id, '+
        '(select top(1) id from Comments S where S.author = T.author and S.root_title = T.title order by id desc) as comment_id ' +
        'FROM TxComments T  '+
        ' where title <> '''' and parent_permlink <> ''bm-open'' and body not LIKE ''@@%'' '+
        ' GROUP BY author, title, parent_permlink, permlink  '+
        ' order by tx_id desc');
    AddItems;
  end;
end;

Весь набор полученных данных сохраняется в компоненте запроса UniQuery, мы оттуда их достаем и выводим в ленту:

procedure THeaderFooterwithNavigation.AddItems;
var
  Item: TListViewItem;
  I: integer;
begin
  ListView.Items.Clear;
  UniQuery.Open;
  if UniQuery.RecordCount > 0 then
  begin
    UniQuery.First;
    for I := 0 to UniQuery.RecordCount - 1 do
    begin
      if Pos('bm-', UniQuery.FieldByName('author').AsString) > 0 then
      begin
        UniQuery.Next;
        continue;
      end;

      Item := ListView.Items.Add;
      Item.ImageIndex := 0;
      Item.Text := UniQuery.FieldByName('title').AsString;
      Item.Detail := sLineBreak + 'Создано '+ '@' + UniQuery.FieldByName('author').AsString +
      sLineBreak + 'в ' + DeTransliterate(UniQuery.FieldByName('parent_permlink').AsString);
      Item.Data['author'] := UniQuery.FieldByName('author').AsString;
      Item.Data['permlink'] := UniQuery.FieldByName('permlink').AsString;
      Item.Data['parent_permlink'] := UniQuery.FieldByName('parent_permlink').AsString;
      Item.Data['title'] := UniQuery.FieldByName('title').AsString;
      Item.Data['tx_id'] := UniQuery.FieldByName('tx_id').AsString;
      Item.Data['json_metadata'] := GetFieldByTx(UniQuery.FieldByName('tx_id').AsString, 'json_metadata');
      Application.ProcessMessages;
      UniQuery.Next;
    end;
  end;
end;

Кроме того, мы эти же данные дублируем в объекте Data каждого элемента, чтобы впоследствии к ним обратиться. Например, вот так, при клике на элемент ListView, мы получаем данные о tx_id - ключе записи:

procedure THeaderFooterwithNavigation.ListViewItemClick(const Sender: TObject;
  const AItem: TListViewItem);
begin
  TabControl1.ActiveTab := xAnimation;
  LoadComment(AItem.Data['tx_id'].AsString, AItem.Index);
end;

и передаем их в процедуру LoadComment, которая уже и выводит сам пост в WebBrowser:

procedure THeaderFooterwithNavigation.LoadComment(TxID: string; ItemId: integer);
var
  html, body, title: string;
begin
  if UniConnection.Connected then
    begin
      UniQuery.SQL.Clear;
      UniQuery.SQL.Add('select body from TxComments where tx_id = ' + TxID);
      UniQuery.Open;
      UniQuery.First;
      title := ListView.Items[ItemId].Data['title'].AsString;
      body := UniQuery.FieldByName('body').AsString;
      body := StringReplace(body, '< img ', '< img width="'+IntToStr(width - 40)+'" ', [rfReplaceAll, rfIgnoreCase]);
      html := '<script src="https://cdn.rawgit.com/showdownjs/showdown/1.6.4/dist/showdown.min.js"></script>';
      html := html + '<h1>'+ title + '</h1>';
      html := html + '';
      html := html + sLineBreak + body;
      Web.LoadFromStrings(html, '');
      TabControl1.ActiveTab := ViewDetails;
      UniQuery.Close;
    end;
end;

Мы здесь немного преобразуем часть элементов, так мы переписываем тэги img так, чтобы ширина картинки была чуть меньше ширины формы и не "рвала" страницу. Но при отображении остается еще куча проблем, например отображение Markdown, нотак что это уже в следующем уроке.

Что имеем на текущий момент - мы можем загружать ленту постов и при клике на пост подгружать его для просмотра. Уже неплохо.

Ну и пару картинок:

Предыдущие шаги тут

1
164.113 GOLOS
На Golos с January 2017
Комментарии (4)
Сортировать по:
Сначала старые